[svn r13988] Major improvements in the handling of scores and prerequisites in the learning path tool. Implies database field values update, meaning you need to create a new learning path if you want to try it out in 1.8.5 SVN (without executing the upgrade script). Should close FS#2112.

skala
Yannick Warnier 18 years ago
parent 2cd3fee96e
commit d01e1175eb
  1. 15
      main/exercice/exercise_result.php
  2. 28
      main/exercice/savescores.php
  3. 9
      main/exercice/showinframes.php
  4. 27
      main/install/update-db-1.8.4-1.8.5.inc.php
  5. 57
      main/newscorm/learnpath.class.php
  6. 68
      main/newscorm/learnpathItem.class.php
  7. 9
      main/newscorm/lp_comm.server.php
  8. 14
      main/newscorm/scorm_api.php

@ -25,7 +25,7 @@
* @package dokeos.exercise
* @author Olivier Brouckaert, main author
* @author Roan Embrechts, some refactoring
* @version $Id: exercise_result.php 13845 2007-11-29 05:03:37Z yannoo $
* @version $Id: exercise_result.php 13988 2007-12-14 05:05:51Z yannoo $
*
* @todo split more code up in functions, move functions to library?
*/
@ -853,17 +853,8 @@ if ($origin != 'learnpath')
//we are not in learnpath tool
Display::display_footer();
}else{
$user_id = (!empty($_SESSION['_user']['id'])?$_SESSION['_user']['id']:0);
$lp_item = Database::get_course_table('lp_item');
$lp_item_view = Database::get_course_table('lp_item_view');
$sql2 = "UPDATE $lp_item_view SET score = '$totalScore'
WHERE lp_item_id = '$learnpath_item_id'
AND lp_view_id = '".$_SESSION['scorm_view_id']."'
ORDER BY view_count DESC LIMIT 1";
api_sql_query($sql2,__FILE__,__LINE__);
$_SESSION['oLP']->save_last();
echo '<script language="javascript" type="text/javascript">window.parent.API.void_save_asset('.$totalScore.');</script>';
echo '</body></html>';
}
$send_email = api_get_course_setting('email_alert_manager_on_new_quiz');
$csspath = "http://portal.dokeos.com/demo/main/css/default.css";

@ -22,11 +22,22 @@
* Saving the scores.
* @package dokeos.exercise
* @author
* @version $Id: savescores.php 12852 2007-08-02 04:54:12Z yannoo $
* @version $Id: savescores.php 13988 2007-12-14 05:05:51Z yannoo $
*/
// name of the language file that needs to be included
$language_file = 'learnpath';
if($_GET['origin']=='learnpath')
{
require_once ('../newscorm/learnpath.class.php');
require_once ('../newscorm/learnpathItem.class.php');
require_once ('../newscorm/scorm.class.php');
require_once ('../newscorm/scormItem.class.php');
require_once ('../newscorm/aicc.class.php');
require_once ('../newscorm/aiccItem.class.php');
}
include ('../inc/global.inc.php');
$this_section=SECTION_COURSES;
@ -47,6 +58,7 @@ $_cid = api_get_course_id();
$test = mysql_real_escape_string($_REQUEST['test']);
$score = mysql_real_escape_string($_REQUEST['score']);
$origin = $_REQUEST['origin'];
$jscript2run = '';
/**
* Save the score for a HP quiz. Can be used by the learnpath tool as well
@ -90,12 +102,8 @@ function save_scores($file, $score)
{
//if we are in a learning path, save the score in the corresponding
//table to get tracking in there as well
$user_id = api_get_user_id();
$lp_item_view = Database::get_course_table('lp_item_view');
$sql2 = "UPDATE $lp_item_view SET score = '$score'" .
" WHERE lp_view_id = '".$_SESSION['scorm_view_id']."'" .
" AND lp_item_id = '".$_SESSION['scorm_item_id']."'";
$res2 = api_sql_query($sql2,__FILE__,__LINE__);
global $jscript2run;
$jscript2run .= '<script language="javascript" type="text/javascript">window.parent.API.void_save_asset('.$score.');</script>';
}
}
@ -108,9 +116,8 @@ if ($origin != 'learnpath')
{
// $url = "Hpdownload.php?doc_url=".$test."&cid=".$cid; // back to the test
$url = "exercice.php"; // back to exercices
echo "<SCRIPT LANGUAGE='JavaScript' type='text/javascript'>";
echo "window.open('$url', '_top', '')"; // back to exercices
echo "</SCRIPT> ";
$jscript2run .= '<script language="javascript" type="text/javascript">'."window.open('$url', '_top', '')".'</script>';
echo $jscript2run;
}
else
{
@ -118,6 +125,7 @@ else
<html>
<head>
<link rel='stylesheet' type='text/css' href='../css/<?php echo api_get_setting('stylesheets');?>/scorm.css' />
<?php echo $jscript2run; ?>
</head>
<body>
<br />

@ -22,7 +22,7 @@
* Code library for HotPotatoes integration.
* @package dokeos.exercise
* @author Istvan Mandak
* @version $Id: showinframes.php 12831 2007-08-01 17:22:34Z yannoo $
* @version $Id: showinframes.php 13988 2007-12-14 05:05:51Z yannoo $
*/
/*
@ -138,9 +138,4 @@ if ($origin!='learnpath') {
}
?>
</html>
<?php
//echo $full_file_path;
//}
//echo $uid;
?>
</html>

@ -305,6 +305,33 @@ if (defined('DOKEOS_INSTALL') || defined('DOKEOS_COURSE_UPDATE'))
}
}
}
$mytable = $row_course['db_name'].".lp_item";
if($singleDbForm)
{
$mytable = "$prefix{$row_course['db_name']}_lp_item";
}
$mysql = "SELECT * FROM $mytable WHERE min_score != 0 AND prerequisite != ''";
$myres = mysql_query($query);
if($myres!==false && mysql_num_rows($myres)>0)
{
while($myrow = mysql_fetch_array($myres))
{
if(is_numeric($myrow['prerequisite']))
{
$mysql2 = "UPDATE $mytable SET mastery_score = '".$myrow['min_score']."' WHERE id = '".$myrow['prerequisite']."'";
$myres2 = mysql_query($mysql2);
//echo $mysql2."<br />";
if($myres2 !== false)
{
$mysql3 = "UPDATE $mytable SET min_score = 0 WHERE id = '".$myrow['id']."'";
$myres3 = mysql_query($mysql3);
//echo $mysql3."<br />";
}
}
}
}
$sql = "UPDATE $mydb.lp_item SET ";
}
}
}

@ -1077,11 +1077,10 @@ class learnpath {
*/
function edit_item_prereq($id, $prerequisite_id, $min_score = 0, $max_score = 100)
function edit_item_prereq($id, $prerequisite_id, $mastery_score = 0, $max_score = 100)
{
if($this->debug>0){error_log('New LP - In learnpath::edit_item_prereq()',0);}
if($this->debug>0){error_log('New LP - In learnpath::edit_item_prereq('.$id.','.$prerequisite_id.','.$mastery_score.','.$max_score.')',0);}
if(empty($id) or ($id != strval(intval($id))) or empty($prerequisite_id)){ return false; }
@ -1089,28 +1088,32 @@ class learnpath {
$tbl_lp_item = Database::get_course_table('lp_item');
if(!is_numeric($min_score) || $min_score < 0 || $min_score > 100)
$min_score = 0;
if(!is_numeric($mastery_score) || $mastery_score < 0)
$mastery_score = 0;
if(!is_numeric($max_score) || $max_score < 0 || $max_score > 100)
if(!is_numeric($max_score) || $max_score < 0)
$max_score = 100;
if($min_score > $max_score)
$max_score = $min_score;
if($mastery_score > $max_score)
$max_score = $mastery_score;
if(!is_numeric($prerequisite_id))
$prerequisite_id = 'NULL';
$sql_upd = "
UPDATE " . $tbl_lp_item . "
SET
min_score = " . $min_score . ",
max_score = " . $max_score . ",
prerequisite = " . $prerequisite_id . "
WHERE id = " . $id;
SET prerequisite = ".$prerequisite_id." WHERE id = ".$id;
$res_upd = api_sql_query($sql_upd ,__FILE__, __LINE__);
if($prerequisite_id!='NULL' && $prerequisite_id!='')
{
$sql_upd = " UPDATE ".$tbl_lp_item." SET
mastery_score = " . $mastery_score .
//", max_score = " . $max_score . " " . //max score cannot be changed in the form anyway - see display_item_prerequisites_form()
" WHERE ref = '" . $prerequisite_id."'" ; //will this be enough to ensure unicity?
$res_upd = api_sql_query($sql_upd ,__FILE__, __LINE__);
}
//TODO update the item object (can be ignored for now because refreshed)
return true;
@ -3042,7 +3045,8 @@ class learnpath {
$prereq_string = str_replace(' ','',$prereq_string);
if($this->debug>0){error_log('Found prereq_string: '.$prereq_string,0);}
//now send to the parse_prereq() function that will check this component's prerequisites
$result = $this->items[$item]->parse_prereq($prereq_string,$this->items,$this->refs_list);
$result = $this->items[$item]->parse_prereq($prereq_string,$this->items,$this->refs_list,$this->get_user_id());
if($result === false)
{
$this->set_error_msg($this->items[$item]->prereq_alert);
@ -3453,12 +3457,13 @@ class learnpath {
is_object($this->items[$this->current]))
{
$type = $this->get_type();
$item_type = $this->items[$this->current]->get_type();
if(
($type == 2 && $this->items[$this->current]->get_type()!='sco')
($type == 2 && $item_type!='sco')
OR
($type == 3 && $this->items[$this->current]->get_type()!='au')
($type == 3 && $item_type!='au')
OR
($type==1)
($type == 1 && $item_type!=TOOL_QUIZ && $item_type!=TOOL_HOTPOTATOES)
)
{
$this->items[$this->current]->open($allow_new_attempt);
@ -3822,6 +3827,7 @@ class learnpath {
'next_item_id' => $array[$i]['next_item_id'],
'min_score' => $array[$i]['min_score'],
'max_score' => $array[$i]['max_score'],
'mastery_score' => $array[$i]['mastery_score'],
'display_order' => $array[$i]['display_order'],
'prerequisite' => $array[$i]['prerequisite'],
'depth' => $depth
@ -6904,8 +6910,8 @@ function display_thread_form($action = 'add', $id = 0, $extra_info = '')
$row = Database::fetch_array($result);
$preq_id = $row['prerequisite'];
$preq_min = $row['min_score'];
$preq_max = $row['max_score'];
//$preq_mastery = $row['mastery_score'];
//$preq_max = $row['max_score'];
$return = $this->display_manipulate($item_id, TOOL_DOCUMENT);
$return .= '<div style="margin:3px 10px;">';
@ -6938,14 +6944,21 @@ function display_thread_form($action = 'add', $id = 0, $extra_info = '')
'id' => $row['id'],
'item_type' => $row['item_type'],
'title' => $row['title'],
'ref' => $row['ref'],
'description' => $row['description'],
'parent_item_id' => $row['parent_item_id'],
'previous_item_id' => $row['previous_item_id'],
'next_item_id' => $row['next_item_id'],
'max_score' => $row['max_score'],
'min_score' => $row['min_score'],
'mastery_score' => $row['mastery_score'],
'next_item_id' => $row['next_item_id'],
'display_order' => $row['display_order']);
if($row['ref'] == $preq_id)
{
$preq_mastery = $row['mastery_score'];
$preq_max = $row['max_score'];
}
}
$this->tree_array($arrLP);
@ -6969,7 +6982,7 @@ function display_thread_form($action = 'add', $id = 0, $extra_info = '')
if($arrLP[$i]['item_type'] == TOOL_QUIZ)
{
$return .= '<td class="exercise">';
$return .= '<input maxlength="3" name="min_' . $arrLP[$i]['id'] . '" type="text" value="' . (($arrLP[$i]['id'] == $preq_id) ? $preq_min : 0) . '" />';
$return .= '<input maxlength="3" name="min_' . $arrLP[$i]['id'] . '" type="text" value="' . (($arrLP[$i]['id'] == $preq_id) ? $preq_mastery : 0) . '" />';
$return .= '</td>';
$return .= '<td class="exercise">';
$return .= '<input maxlength="3" name="max_' . $arrLP[$i]['id'] . '" type="text" value="' . $arrLP[$i]['max_score'] . '" disabled="true" />';
@ -6978,7 +6991,7 @@ function display_thread_form($action = 'add', $id = 0, $extra_info = '')
if($arrLP[$i]['item_type'] == TOOL_HOTPOTATOES)
{
$return .= '<td class="exercise">';
$return .= '<input maxlength="3" name="min_' . $arrLP[$i]['id'] . '" type="text" value="' . (($arrLP[$i]['id'] == $preq_id) ? $preq_min : 0) . '" />';
$return .= '<input maxlength="3" name="min_' . $arrLP[$i]['id'] . '" type="text" value="' . (($arrLP[$i]['id'] == $preq_id) ? $preq_mastery : 0) . '" />';
$return .= '</td>';
$return .= '<td class="exercise">';
$return .= '<input maxlength="3" name="max_' . $arrLP[$i]['id'] . '" type="text" value="' . $arrLP[$i]['max_score'] . '" disabled="true" />';

@ -167,8 +167,16 @@ class learnpathItem{
{
if($this->debug>0){error_log('New LP - In learnpathItem::close()',0);}
$this->current_stop_time = time();
if($this->get_type() != 'sco'){
$this->status = $this->possible_status[2];
$type = $this->get_type();
if($type != 'sco'){
if($type == TOOL_QUIZ or $type == TOOL_HOTPOTATOES)
{
$this->get_status(true,true);//update status (second option forces the update)
}
else
{
$this->status = $this->possible_status[2];
}
}
if($this->save_on_close)
{
@ -1114,11 +1122,12 @@ class learnpathItem{
/**
* Parses the prerequisites string with the AICC logic language
* @param string The prerequisites string as it figures in imsmanifest.xml
* @param object Learnpath object pointer. We need it to get a list of other resources the prerequisites might be pointing to.
* @param Array Array of items in the current learnpath object. Although we're in the learnpathItem object, it's necessary to have a list of all items to be able to check the current item's prerequisites
* @param Array List of references (the "ref" column in the lp_item table) that are strings used in the expression of prerequisites.
* @param integer The user ID. In some cases like Dokeos quizzes, it's necessary to have the user ID to query other tables (like the results of quizzes)
* @return boolean True if the list of prerequisites given is entirely satisfied, false otherwise
* @TODO //TODO if it's a Dokeos LP, use item ID instead of item REF for prereq!!!
*/
function parse_prereq($prereqs_string, $items, $refs_list){
function parse_prereq($prereqs_string, $items, $refs_list,$user_id){
if($this->debug>0){error_log('New LP - In learnpathItem::parse_prereq() for learnpath '.$this->lp_id.' with string '.$prereqs_string,0);}
//deal with &, |, ~, =, <>, {}, ,, X*, () in reverse order
$this->prereq_alert = '';
@ -1136,7 +1145,7 @@ class learnpathItem{
$res = preg_match_all('/(\(([^\(\)]*)\))/',$prereqs_string,$matches);
if($res){
foreach($matches[2] as $id=>$match){
$str_res = $this->parse_prereq($match,$items,$refs_list);
$str_res = $this->parse_prereq($match,$items,$refs_list,$user_id);
if($str_res){
$prereqs_string = str_replace($matches[1][$id],'_true_',$prereqs_string);
}else{
@ -1154,7 +1163,7 @@ class learnpathItem{
if(count($list)>1){
$andstatus = true;
foreach($list as $condition){
$andstatus = $andstatus && $this->parse_prereq($condition,$items,$refs_list);
$andstatus = $andstatus && $this->parse_prereq($condition,$items,$refs_list,$user_id);
if($andstatus==false){
if($this->debug>1){error_log('New LP - One condition in AND was false, short-circuit',0);}
break;
@ -1232,7 +1241,7 @@ class learnpathItem{
$list = array();
$myres = preg_match('/~([^(\d+\*)\{]*)/',$prereqs_string,$list);
if($myres){
$returnstatus = !$this->parse_prereq($list[1],$items,$refs_list);
$returnstatus = !$this->parse_prereq($list[1],$items,$refs_list,$user_id);
if(empty($this->prereq_alert) && !$returnstatus){
$this->prereq_alert = get_lang('_prereq_not_complete');
}
@ -1316,13 +1325,14 @@ class learnpathItem{
{
$sql = 'SELECT exe_result, exe_weighting
FROM '.Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES).'
WHERE exe_exo_id = '.$items[$refs_list[$prereqs_string]]->path.'
WHERE exe_exo_id = '.$items[$refs_list[$prereqs_string]]->path.'
AND exe_user_id = '.$user_id.'
ORDER BY exe_date DESC
LIMIT 0, 1';
$rs_quiz = api_sql_query($sql, __FILE__, __LINE__);
if($quiz = Database :: fetch_array($rs_quiz))
{
if($quiz['exe_result'] >= $this -> min_score)
if($quiz['exe_result'] >= $items[$refs_list[$prereqs_string]]->get_mastery_score())
{
$returnstatus = true;
}
@ -1368,7 +1378,7 @@ class learnpathItem{
$orstatus = false;
foreach($list as $condition){
if($this->debug>1){error_log('New LP - Found OR, adding it ('.$condition.')',0);}
$orstatus = $orstatus || $this->parse_prereq($condition,$items,$refs_list);
$orstatus = $orstatus || $this->parse_prereq($condition,$items,$refs_list,$user_id);
if($orstatus == true){
//shortcircuit OR
if($this->debug>1){error_log('New LP - One condition in OR was true, short-circuit',0);}
@ -1596,40 +1606,14 @@ class learnpathItem{
}
}
}else{ //if not SCO, such messages should not be expected
$type = strtolower($type);
$type = strtolower($this->type);
switch($type){
case 'asset':
$this->set_status($this->possible_status[2]);
break;
case 'hotpotatoes':
/*
//This section has been commented out because the score
// limits should be defined when including the test in
// the learning path
$tbl_hotpot = Database::get_course_table(QUIZ_TABLE);
$hotpot_sql = "SELECT * FROM $tbl_hotpot WHERE ...";
$hotpot_res = api_sql_query($hotpot_sql,__FILE__,__LINE__);
if(Database::num_rows($hotpot_res)>0){
$hotpot_row = Database::fetch_array($hotpot_res);
$this->min_score = $hotpot_row['min_score'];
$this->max_score = $hotpot_row['max_score'];
$this->set_score($hotpot_row['score']);
//TODO move hardcoded quota value somewhere as variable
if($hotpot_row['score']/$hotpot_row['max_score']>=0.70){
$this->set_status($this->possible_status[3]);
}else{
$this->set_status($this->possible_status[4]);
}
}else{
//if not found, set to incompleted as it probably means
//the user hasn't passed the test
$this->set_status($this->possible_status[2]);
}
*/
$this->set_status($this->possible_status[2]);
case TOOL_HOTPOTATOES:
break;
case TOOL_QUIZ:
$this->set_status($this->possible_status[2]);
break;
default:
//for now, everything that is not sco and not asset is set to
@ -1788,7 +1772,7 @@ class learnpathItem{
*/
function set_score($score)
{
if($this->debug>0){error_log('New LP - In learnpathItem::set_score()',0);}
if($this->debug>0){error_log('New LP - In learnpathItem::set_score('.$score.')',0);}
if(($score <= $this->max_score) && ($score >= $this->min_score))
{
$this->current_score = $score;
@ -1799,6 +1783,10 @@ class learnpathItem{
{
$this->set_status($this->possible_status[3]);
}
elseif($master != -1 && $this->current_score<$master)
{
$this->set_status($this->possible_status[4]);
}
return true;
}
return false;

@ -140,13 +140,18 @@ function save_item($lp_id,$user_id,$view_id,$item_id,$score=-1,$max=-1,$min=-1,$
$mylp->save_item($item_id,false);
}
$mystatus = $mylpi->get_status(false);
$mytotal = $mylp->get_total_items_count_without_chapters();
$mycomplete = $mylp->get_complete_items_count();
$myprogress_mode = $mylp->get_progress_bar_mode();
$myprogress_mode = ($myprogress_mode==''?'%':$myprogress_mode);
//$mylpi->write_to_db();
$_SESSION['lpobject'] = serialize($mylp);
$objResponse->addScript("update_toc('".$status."','".$item_id."');");
if($mylpi->get_type()!='sco')
{ //if this object's JS status has not been updated by the SCORM API, update now
$objResponse->addScript("lesson_status='".$mystatus."';");
}
$objResponse->addScript("update_toc('".$mystatus."','".$item_id."');");
$update_list = $mylp->get_update_queue();
foreach($update_list as $my_upd_id => $my_upd_status)
{
@ -157,7 +162,7 @@ function save_item($lp_id,$user_id,$view_id,$item_id,$score=-1,$max=-1,$min=-1,$
$objResponse->addScript("update_progress_bar('$mycomplete','$mytotal','$myprogress_mode');");
if($debug>0){
$objResponse->addScript("logit_lms('Saved data for item ".$item_id.", user ".$user_id." (status=".$status.")',2)");
$objResponse->addScript("logit_lms('Saved data for item ".$item_id.", user ".$user_id." (status=".$mystatus.")',2)");
if($debug>1){error_log('End of xajax_save_item()',0);}
}

@ -95,6 +95,7 @@ function APIobject() {
this.GetDiagnostic=LMSGetDiagnostic;
this.Terminate=Terminate; //only in Scorm 1.3
this.save_asset = dokeos_save_asset;
this.void_save_asset = dokeos_void_save_asset;
}
//it is not sure that the scos use the above declarations
@ -783,6 +784,19 @@ function dokeos_save_asset(){
xajax_save_objectives(lms_lp_id,lms_user_id,lms_view_id,lms_item_id,item_objectives);
}
}
/**
* Save a Dokeos learnpath item's time and mark as completed upon leaving it.
* Same function as dokeos_save_asset() but saves it with empty params
* to use values set from another side in the database. Only used by Dokeos quizzes.
* Also save the score locally because it hasn't been done through SetValue().
* Saving the status will be dealt with by the XAJAX function.
*/
function dokeos_void_save_asset(myscore)
{
logit_lms('dokeos_save_asset',2);
score = myscore;
xajax_save_item(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, myscore);
}
/**
* Logs information about SCORM messages into the log frame
* @param string Message to log

Loading…
Cancel
Save