Feedback , 1=>DirectFeedback, 2=>NoFeedback
define('EXERCISE_FEEDBACK_TYPE_END',0);
define('EXERCISE_FEEDBACK_TYPE_DIRECT',1);
define('EXERCISE_FEEDBACK_TYPE_EXAM',2);
$debug = 1; //All exercise scripts should depend in this debug variable
require_once dirname(__FILE__).'/../inc/lib/exercise_show_functions.lib.php';
if(!class_exists('Exercise')):
class Exercise {
public $id;
public $exercise;
public $description;
public $sound;
public $type; //ALL_ON_ONE_PAGE or ONE_PER_PAGE
public $random;
public $random_answers;
public $active;
public $timeLimit;
public $attempts;
public $feedbacktype;
public $end_time;
public $start_time;
public $questionList; // array with the list of this exercise's questions
public $results_disabled;
public $expired_time;
public $course;
public $propagate_neg;
/**
* Constructor of the class
*
* @author - Olivier Brouckaert
*/
function Exercise($course_id = null) {
$this->id = 0;
$this->exercise = '';
$this->description = '';
$this->sound = '';
$this->type = ALL_ON_ONE_PAGE;
$this->random = 0;
$this->random_answers = 0;
$this->active = 1;
$this->questionList = array();
$this->timeLimit = 0;
$this->end_time = '0000-00-00 00:00:00';
$this->start_time = '0000-00-00 00:00:00';
$this->results_disabled = 1;
$this->expired_time = '0000-00-00 00:00:00';
$this->propagate_neg = 0;
if (!empty($course_id)) {
$this->course_id = intval($course_id);
$course_info = api_get_course_info_by_id($this->course_id);
} else {
$course_info = api_get_course_info();
}
$this->course = $course_info;
}
/**
* reads exercise informations from the data base
*
* @author - Olivier Brouckaert
* @param - integer $id - exercise ID
* @return - boolean - true if exercise exists, otherwise false
*/
function read($id) {
$TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION,$this->course['db_name']);
$TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST,$this->course['db_name']);
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION,$this->course['db_name']);
#$TBL_REPONSES = Database::get_course_table(TABLE_QUIZ_ANSWER);
$sql="SELECT * FROM $TBL_EXERCICES WHERE id='".Database::escape_string($id)."'";
$result=Database::query($sql);
// if the exercise has been found
if ($object=Database::fetch_object($result)) {
$this->id = $id;
$this->exercise = $object->title;
$this->description = $object->description;
$this->sound = $object->sound;
$this->type = $object->type;
$this->random = $object->random;
$this->random_answers = $object->random_answers;
$this->active = $object->active;
$this->results_disabled = $object->results_disabled;
$this->attempts = $object->max_attempt;
$this->feedbacktype = $object->feedback_type;
$this->propagate_neg = $object->propagate_neg;
if ($object->end_time != '0000-00-00 00:00:00') {
$this->end_time = api_get_local_time($object->end_time);
}
if ($object->start_time != '0000-00-00 00:00:00') {
$this->start_time = api_get_local_time($object->start_time);
}
$this->expired_time = $object->expired_time; //control time
$sql="SELECT question_id, question_order FROM $TBL_EXERCICE_QUESTION, $TBL_QUESTIONS WHERE question_id=id AND exercice_id='".Database::escape_string($id)."' ORDER BY question_order";
$result=Database::query($sql);
// fills the array with the question ID for this exercise
// the key of the array is the question position
while ($new_object = Database::fetch_object($result)) {
$this->questionList[$new_object->question_order]= $new_object->question_id;
}
//overload questions list with recorded questions list
//load questions only for exercises of type 'one question per page'
//this is needed only is there is no questions
//
// @todo not sure were in the code this is used somebody mess with the exercise tool
global $_configuration, $questionList;
if ($this->type == ONE_PER_PAGE && $_configuration['live_exercise_tracking'] && $_SERVER['REQUEST_METHOD'] != 'POST' && defined('QUESTION_LIST_ALREADY_LOGGED')) {
//if(empty($_SESSION['questionList']))
$this->questionList = $questionList;
}
return true;
}
// exercise not found
return false;
}
/**
* returns the exercise ID
*
* @author - Olivier Brouckaert
* @return - integer - exercise ID
*/
function selectId() {
return $this->id;
}
/**
* returns the exercise title
*
* @author - Olivier Brouckaert
* @return - string - exercise title
*/
function selectTitle() {
return $this->exercise;
}
/**
* returns the number of attempts setted
*
* @return - numeric - exercise attempts
*/
function selectAttempts() {
return $this->attempts;
}
/** returns the number of FeedbackType *
* 0=>Feedback , 1=>DirectFeedback, 2=>NoFeedback
* @return - numeric - exercise attempts
*/
function selectFeedbackType() {
return $this->feedbacktype;
}
/**
* returns the time limit
*/
function selectTimeLimit() {
return $this->timeLimit;
}
/**
* returns the exercise description
*
* @author - Olivier Brouckaert
* @return - string - exercise description
*/
function selectDescription() {
return $this->description;
}
/**
* returns the exercise sound file
*
* @author - Olivier Brouckaert
* @return - string - exercise description
*/
function selectSound() {
return $this->sound;
}
/**
* returns the exercise type
*
* @author - Olivier Brouckaert
* @return - integer - exercise type
*/
function selectType() {
return $this->type;
}
/**
* tells if questions are selected randomly, and if so returns the draws
*
* @author - Carlos Vargas
* @return - integer - results disabled exercise
*/
function selectResultsDisabled() {
return $this->results_disabled;
}
/**
* tells if questions are selected randomly, and if so returns the draws
*
* @author - Olivier Brouckaert
* @return - integer - 0 if not random, otherwise the draws
*/
function isRandom() {
if($this->random > 0){
return true;
} else {
return false;
}
}
/**
* returns random answers status.
*
* @author - Juan Carlos Ra�a
*/
function selectRandomAnswers() {
$this->random_answers;
return $this->random_answers;
}
/**
* Same as isRandom() but has a name applied to values different than 0 or 1
*/
function getShuffle() {
return $this->random;
}
/**
* returns the exercise status (1 = enabled ; 0 = disabled)
*
* @author - Olivier Brouckaert
* @return - boolean - true if enabled, otherwise false
*/
function selectStatus() {
return $this->active;
}
/**
* returns the array with the question ID list
*
* @author - Olivier Brouckaert
* @return - array - question ID list
*/
function selectQuestionList() {
return $this->questionList;
}
/**
* returns the number of questions in this exercise
*
* @author - Olivier Brouckaert
* @return - integer - number of questions
*/
function selectNbrQuestions() {
return sizeof($this->questionList);
}
function selectPropagateNeg() {
return $this->propagate_neg;
}
/**
* Selects questions randomly in the question list
*
* @author - Olivier Brouckaert
* @return - array - if the exercise is not set to take questions randomly, returns the question list
* without randomizing, otherwise, returns the list with questions selected randomly
*/
function selectRandomList() {
$nbQuestions = $this->selectNbrQuestions();
$temp_list = $this->questionList;
//Not a random exercise, or if there are not at least 2 questions
if($this->random == 0 || $nbQuestions < 2) {
return $this->questionList;
}
if ($nbQuestions != 0) {
shuffle($temp_list);
$my_random_list = array_combine(range(1,$nbQuestions),$temp_list);
$my_question_list = array();
$i = 0;
foreach ($my_random_list as $item) {
if ($i < $this->random) {
$my_question_list[$i] = $item;
} else {
break;
}
$i++;
}
return $my_question_list;
}
}
/**
* returns 'true' if the question ID is in the question list
*
* @author - Olivier Brouckaert
* @param - integer $questionId - question ID
* @return - boolean - true if in the list, otherwise false
*/
function isInList($questionId) {
if (is_array($this->questionList))
return in_array($questionId,$this->questionList);
else
return false;
}
/**
* changes the exercise title
*
* @author - Olivier Brouckaert
* @param - string $title - exercise title
*/
function updateTitle($title) {
$this->exercise=$title;
}
/**
* changes the exercise max attempts
*
* @param - numeric $attempts - exercise max attempts
*/
function updateAttempts($attempts) {
$this->attempts=$attempts;
}
/**
* changes the exercise feedback type
*
* @param - numeric $attempts - exercise max attempts
*/
function updateFeedbackType($feedback_type) {
$this->feedbacktype=$feedback_type;
}
/**
* changes the exercise description
*
* @author - Olivier Brouckaert
* @param - string $description - exercise description
*/
function updateDescription($description) {
$this->description=$description;
}
/**
* changes the exercise expired_time
*
* @author - Isaac flores
* @param - int The expired time of the quiz
*/
function updateExpiredTime($expired_time) {
$this->expired_time = $expired_time;
}
function updatePropagateNegative($value) {
$this->propagate_neg = $value;
}
/**
* changes the exercise sound file
*
* @author - Olivier Brouckaert
* @param - string $sound - exercise sound file
* @param - string $delete - ask to delete the file
*/
function updateSound($sound,$delete) {
global $audioPath, $documentPath;
$TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT, $this->course['db_name']);
$TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY,$this->course['db_name']);
if ($sound['size'] && (strstr($sound['type'],'audio') || strstr($sound['type'],'video'))) {
$this->sound=$sound['name'];
if (@move_uploaded_file($sound['tmp_name'],$audioPath.'/'.$this->sound)) {
$query="SELECT 1 FROM $TBL_DOCUMENT WHERE path='".str_replace($documentPath,'',$audioPath).'/'.$this->sound."'";
$result=Database::query($query);
if(!Database::num_rows($result)) {
/*$query="INSERT INTO $TBL_DOCUMENT(path,filetype) VALUES "
." ('".str_replace($documentPath,'',$audioPath).'/'.$this->sound."','file')";
Database::query($query);*/
$id = add_document($this->course,str_replace($documentPath,'',$audioPath).'/'.$this->sound,'file',$sound['size'],$sound['name']);
//$id = Database::insert_id();
//$time = time();
//$time = date("Y-m-d H:i:s", $time);
// insert into the item_property table, using default visibility of "visible"
/*$query = "INSERT INTO $TBL_ITEM_PROPERTY "
."(tool, ref, insert_user_id,to_group_id, insert_date, lastedit_date, lastedit_type) "
." VALUES "
."('".TOOL_DOCUMENT."', $id, $_user['user_id'], 0, '$time', '$time', 'DocumentAdded' )";
Database::query($query);*/
api_item_property_update($this->course, TOOL_DOCUMENT, $id, 'DocumentAdded',api_get_user_id());
item_property_update_on_folder($this->course,str_replace($documentPath,'',$audioPath),api_get_user_id());
}
}
} elseif($delete && is_file($audioPath.'/'.$this->sound)) {
$this->sound='';
}
}
/**
* changes the exercise type
*
* @author - Olivier Brouckaert
* @param - integer $type - exercise type
*/
function updateType($type) {
$this->type=$type;
}
/**
* sets to 0 if questions are not selected randomly
* if questions are selected randomly, sets the draws
*
* @author - Olivier Brouckaert
* @param - integer $random - 0 if not random, otherwise the draws
*/
function setRandom($random) {
$this->random=$random;
}
/**
* sets to 0 if answers are not selected randomly
* if answers are selected randomly
* @author - Juan Carlos Ra�a
* @param - integer $random_answers - random answers
*/
function updateRandomAnswers($random_answers) {
$this->$random_answers = $random_answers;
}
/**
* enables the exercise
*
* @author - Olivier Brouckaert
*/
function enable() {
$this->active=1;
}
/**
* disables the exercise
*
* @author - Olivier Brouckaert
*/
function disable() {
$this->active=0;
}
function disable_results() {
$this->results_disabled = true;
}
function enable_results()
{
$this->results_disabled = false;
}
function updateResultsDisabled($results_disabled) {
$this->results_disabled = intval($results_disabled);
}
/**
* updates the exercise in the data base
*
* @author - Olivier Brouckaert
*/
function save($type_e='') {
global $_course;
$TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
$TBL_QUIZ_QUESTION= Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
$id = $this->id;
$exercise = $this->exercise;
$description = $this->description;
$sound = $this->sound;
$type = $this->type;
$attempts = $this->attempts;
$feedbacktype = $this->feedbacktype;
$random = $this->random;
$random_answers = $this->random_answers;
$active = $this->active;
$propagate_neg = $this->propagate_neg;
$session_id = api_get_session_id();
//If direct we do not show results
if ($feedbacktype == EXERCISE_FEEDBACK_TYPE_DIRECT) {
//$results_disabled = 1;
} else {
$results_disabled = intval($this->results_disabled);
}
$expired_time = intval($this->expired_time);
$start_time = Database::escape_string(api_get_utc_datetime($this->start_time));
$end_time = Database::escape_string(api_get_utc_datetime($this->end_time));
// Exercise already exists
if($id) {
$sql="UPDATE $TBL_EXERCICES SET
title='".Database::escape_string($exercise)."',
description='".Database::escape_string($description)."'";
if ($type_e != 'simple') {
$sql .= ", sound='".Database::escape_string($sound)."',
type='".Database::escape_string($type)."',
random='".Database::escape_string($random)."',
random_answers='".Database::escape_string($random_answers)."',
active='".Database::escape_string($active)."',
feedback_type='".Database::escape_string($feedbacktype)."',
start_time='$start_time',end_time='$end_time',
max_attempt='".Database::escape_string($attempts)."',
expired_time='".Database::escape_string($expired_time)."',
propagate_neg='".Database::escape_string($propagate_neg)."',
results_disabled='".Database::escape_string($results_disabled)."'";
}
$sql .= " WHERE id='".Database::escape_string($id)."'";
Database::query($sql);
// update into the item_property table
api_item_property_update($_course, TOOL_QUIZ, $id,'QuizUpdated',api_get_user_id());
if (api_get_setting('search_enabled')=='true') {
$this -> search_engine_edit();
}
} else {// creates a new exercise
//add condition by anonymous user
/*if (!api_is_anonymous()) {
//is course manager
$cond1=Database::escape_string($exercise);
$cond2=Database::escape_string($description);
} else {
//is anonymous user
$cond1=Database::escape_string(Security::remove_XSS($exercise));
$cond2=Database::escape_string(Security::remove_XSS(api_html_entity_decode($description),COURSEMANAGERLOWSECURITY));
}*/
$sql="INSERT INTO $TBL_EXERCICES (start_time, end_time, title, description, sound, type, random, random_answers,active, results_disabled, max_attempt, feedback_type, expired_time, session_id)
VALUES(
'$start_time','$end_time',
'".Database::escape_string($exercise)."',
'".Database::escape_string($description)."',
'".Database::escape_string($sound)."',
'".Database::escape_string($type)."',
'".Database::escape_string($random)."',
'".Database::escape_string($random_answers)."',
'".Database::escape_string($active)."',
'".Database::escape_string($results_disabled)."',
'".Database::escape_string($attempts)."',
'".Database::escape_string($feedbacktype)."',
'".Database::escape_string($expired_time)."',
'".Database::escape_string($session_id)."'
)";
Database::query($sql);
$this->id=Database::insert_id();
// insert into the item_property table
api_item_property_update($_course, TOOL_QUIZ, $this->id,'QuizAdded',api_get_user_id());
if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian')) {
$this -> search_engine_save();
}
}
// updates the question position
$this->update_question_positions();
}
function update_question_positions() {
// updates the question position
$TBL_QUIZ_QUESTION= Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
foreach($this->questionList as $position=>$questionId) {
//$sql="UPDATE $TBL_QUESTIONS SET position='".Database::escape_string($position)."' WHERE id='".Database::escape_string($questionId)."'";
$sql="UPDATE $TBL_QUIZ_QUESTION SET question_order='".Database::escape_string($position)."' " .
"WHERE question_id='".Database::escape_string($questionId)."' and exercice_id=".Database::escape_string($this->id)."";
Database::query($sql);
}
}
/**
* moves a question up in the list
*
* @author - Olivier Brouckaert
* @author - Julio Montoya (rewrote the code)
* @param - integer $id - question ID to move up
*/
function moveUp($id) {
// there is a bug with some version of PHP with the key and prev functions
// the script commented was tested in dev.dokeos.com with no success
// Instead of using prev and next this was change with arrays.
/*
foreach($this->questionList as $position=>$questionId)
{
// if question ID found
if($questionId == $id)
{
// position of question in the array
echo $pos1=$position; //1
echo " ";
prev($this->questionList);
prev($this->questionList);
// position of previous question in the array
$pos2=key($this->questionList);
//if the cursor of the array hit the end
// then we must reset the array to get the previous key
if($pos2===null)
{
end($this->questionList);
prev($this->questionList);
$pos2=key($this->questionList);
}
// error, can't move question
if(!$pos2)
{
//echo 'cant move!';
$pos2=key($this->questionList);
reset($this->questionList);
}
$id2=$this->questionList[$pos2];
// exits foreach()
break;
}
$i++;
}
*/
$question_list =array();
foreach($this->questionList as $position=>$questionId)
{
$question_list[]= $questionId;
}
$len=count($question_list);
$orderlist=array_keys($this->questionList);
for($i=0;$i<$len;$i++)
{
$questionId = $question_list[$i];
if($questionId == $id)
{
// position of question in the array
$pos1=$orderlist[$i];
$pos2=$orderlist[$i-1];
if($pos2===null)
{
$pos2 = $orderlist[$len-1];
}
// error, can't move question
if(!$pos2)
{
$pos2=$orderlist[0];
$i=0;
}
break;
}
}
// permutes questions in the array
$temp=$this->questionList[$pos2];
$this->questionList[$pos2]=$this->questionList[$pos1];
$this->questionList[$pos1]=$temp;
}
/**
* moves a question down in the list
*
* @author - Olivier Brouckaert
* @param - integer $id - question ID to move down
*/
function moveDown($id) {
// there is a bug with some version of PHP with the key and prev functions
// the script commented was tested in dev.dokeos.com with no success
// Instead of using prev and next this was change with arrays.
/*
foreach($this->questionList as $position=>$questionId)
{
// if question ID found
if($questionId == $id)
{
// position of question in the array
$pos1=$position;
//next($this->questionList);
// position of next question in the array
$pos2=key($this->questionList);
// error, can't move question
if(!$pos2)
{
//echo 'cant move!';
return;
}
$id2=$this->questionList[$pos2];
// exits foreach()
break;
}
}
*/
$question_list =array();
foreach($this->questionList as $position=>$questionId) {
$question_list[]= $questionId;
}
$len=count($question_list);
$orderlist=array_keys($this->questionList);
for($i=0;$i<$len;$i++) {
$questionId = $question_list[$i];
if($questionId == $id) {
$pos1=$orderlist[$i+1];
$pos2 =$orderlist[$i];
if(!$pos2) {
//echo 'cant move!';
}
break;
}
}
// permutes questions in the array
$temp=$this->questionList[$pos2];
$this->questionList[$pos2]=$this->questionList[$pos1];
$this->questionList[$pos1]=$temp;
}
/**
* adds a question into the question list
*
* @author - Olivier Brouckaert
* @param - integer $questionId - question ID
* @return - boolean - true if the question has been added, otherwise false
*/
function addToList($questionId) {
// checks if the question ID is not in the list
if(!$this->isInList($questionId)) {
// selects the max position
if(!$this->selectNbrQuestions()) {
$pos=1;
} else {
if (is_array($this->questionList))
$pos=max(array_keys($this->questionList))+1;
}
$this->questionList[$pos]=$questionId;
return true;
}
return false;
}
/**
* removes a question from the question list
*
* @author - Olivier Brouckaert
* @param - integer $questionId - question ID
* @return - boolean - true if the question has been removed, otherwise false
*/
function removeFromList($questionId) {
// searches the position of the question ID in the list
$pos=array_search($questionId,$this->questionList);
// question not found
if($pos === false) {
return false;
} else {
// deletes the position from the array containing the wanted question ID
unset($this->questionList[$pos]);
return true;
}
}
/**
* deletes the exercise from the database
* Notice : leaves the question in the data base
*
* @author - Olivier Brouckaert
*/
function delete() {
global $_course;
$TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
$sql="UPDATE $TBL_EXERCICES SET active='-1' WHERE id='".Database::escape_string($this->id)."'";
Database::query($sql);
api_item_property_update($_course, TOOL_QUIZ, $this->id,'QuizDeleted',api_get_user_id());
if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian') ) {
$this -> search_engine_delete();
}
}
/**
* Creates the form to create / edit an exercise
* @param FormValidator $form the formvalidator instance (by reference)
*/
function createForm ($form, $type='full') {
global $id;
if(empty($type)){
$type='full';
}
// form title
if (!empty($_GET['exerciseId'])) {
$form_title = get_lang('ModifyExercise');
} else {
$form_title = get_lang('NewEx');
}
$form->addElement('header', '', $form_title);
// title
$form -> addElement('text', 'exerciseTitle', get_lang('ExerciseName'),'class="input_titles" id="exercise_title"');
//$form->applyFilter('exerciseTitle','html_filter');
$form -> addElement('html','
'; //margin top -10
return $html;
}
/**
* So the time control will work
*/
public function show_time_control_js($time_left) {
$time_left = intval($time_left);
return "";
}
/**
* Lp javascript for hotspots
*/
public function show_lp_javascript() {
return "
";
}
/**
* This function was originally found in the exercise_show.php
* @param int exe id
* @param int question id
* @param int the choice the user selected
* @param array the hotspot coordinates $hotspot[$question_id] = coordinates
* @param string function is called from 'exercise_show' or 'exercise_result'
* @param bool save results in the DB or just show the reponse
* @param bool gets information from DB or from the current selection
* @param bool show results or not
* @todo reduce parameters of this function
* @return string html code
*/
function manage_answer($exeId, $questionId, $choice, $from = 'exercise_show', $exerciseResultCoordinates = array(), $saved_results = true, $from_database = false, $show_result = true, $propagate_neg = 0, $hotspot_delineation_result = array()) {
global $_configuration, $feedback_type, $debug;
require_once api_get_path(LIBRARY_PATH).'geometry.lib.php';
if ($debug) error_log('manage_answer called exe_id '.$exeId);
if ($debug) error_log('manage_answer $from '.$from);
if ($debug) error_log('manage_answer $saved_results '.$saved_results);
if ($debug) error_log('manage_answer $from_database '.$from_database);
if ($debug) error_log('manage_answer $show_result '.$show_result);
if ($debug) error_log('manage_answer $propagate_neg '.$propagate_neg);
if ($debug) error_log('manage_answer $$hotspot_delineation_result '.print_r($hotspot_delineation_result, 1));
$extra_data = array();
$html = '';
$questionId = intval($questionId);
$exeId = intval($exeId);
$TBL_TRACK_ATTEMPT = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
$table_ans = Database::get_course_table(TABLE_QUIZ_ANSWER);
// Creates a temporary Question object
$objQuestionTmp = Question :: read($questionId);
$questionName = $objQuestionTmp->selectTitle();
$questionDescription = $objQuestionTmp->selectDescription();
$questionWeighting = $objQuestionTmp->selectWeighting();
$answerType = $objQuestionTmp->selectType();
$quesId = $objQuestionTmp->selectId();
$extra = $objQuestionTmp->extra;
$next = 1; //not for now
//Extra information of the question
if (!empty($extra)){
$extra = explode(':', $extra);
$true_score = $extra[0];
$false_score = $extra[1];
$doubt_score = $extra[2];
}
$totalWeighting = 0;
$totalScore = 0;
// Destruction of the Question object
unset ($objQuestionTmp);
// Construction of the Answer object
$objAnswerTmp = new Answer($questionId);
$nbrAnswers = $objAnswerTmp->selectNbrAnswers();
if ($debug) error_log('Count of answers :'.$nbrAnswers);
if ($debug) error_log('$answerType : '.$answerType);
$questionScore = 0;
if ($answerType == FREE_ANSWER) {
$nbrAnswers = 1;
}
$user_answer = '';
// Get answer list for matching
$sql_answer = 'SELECT id, answer FROM '.$table_ans.' WHERE question_id="'.$questionId.'" ';
$res_answer = Database::query($sql_answer);
$answer_matching =array();
while ($real_answer = Database::fetch_array($res_answer)) {
$answer_matching[$real_answer['id']]= $real_answer['answer'];
}
$real_answers = array();
$quiz_question_options = Question::readQuestionOption($questionId);
for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
$answer = $objAnswerTmp->selectAnswer($answerId);
$answerComment = $objAnswerTmp->selectComment($answerId);
$answerCorrect = $objAnswerTmp->isCorrect($answerId);
$answerWeighting = $objAnswerTmp->selectWeighting($answerId);
$numAnswer = $objAnswerTmp->selectAutoId($answerId);
//delineation
$delineation_cord = $objAnswerTmp->selectHotspotCoordinates(1);
$answer_delineation_destination=$objAnswerTmp->selectDestination(1);
switch ($answerType) {
// for unique answer
case UNIQUE_ANSWER :
case UNIQUE_ANSWER_NO_OPTION :
if ($from_database) {
$queryans = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." where exe_id = '".$exeId."' and question_id= '".$questionId."'";
$resultans = Database::query($queryans);
$choice = Database::result($resultans,0,"answer");
$numAnswer=$objAnswerTmp->selectAutoId($answerId);
$studentChoice=($choice == $numAnswer)?1:0;
if ($studentChoice) {
$questionScore+=$answerWeighting;
$totalScore+=$answerWeighting;
}
} else {
$studentChoice=($choice == $numAnswer)?1:0;
if ($studentChoice) {
$questionScore+=$answerWeighting;
$totalScore+=$answerWeighting;
}
}
break;
// for multiple answers
case MULTIPLE_ANSWER_TRUE_FALSE :
if ($from_database) {
$choice=array();
$queryans = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." where exe_id = '".$exeId."' and question_id= '".$questionId."'";
$resultans = Database::query($queryans);
while ($row = Database::fetch_array($resultans)) {
$ind = $row['answer'];
$result = explode(':',$ind);
$my_answer_id = $result[0];
$option = $result[1];
$choice[$my_answer_id] = $option;
}
$numAnswer=$objAnswerTmp->selectAutoId($answerId);
$studentChoice =$choice[$numAnswer];
} else {
$studentChoice =$choice[$numAnswer];
}
if ($studentChoice == $answerCorrect ) {
$questionScore +=$true_score;
} else {
if ($quiz_question_options[$studentChoice]['name'] != 'DoubtScore') {
$questionScore += $false_score;
} else {
$questionScore += $doubt_score;
}
}
$totalScore +=$true_score;
break;
case MULTIPLE_ANSWER :
if ($from_database) {
$choice=array();
$queryans = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." where exe_id = '".$exeId."' and question_id= '".$questionId."'";
$resultans = Database::query($queryans);
while ($row = Database::fetch_array($resultans)) {
$ind = $row['answer'];
$choice[$ind] = 1;
}
$numAnswer=$objAnswerTmp->selectAutoId($answerId);
$studentChoice=$choice[$numAnswer];
if ($studentChoice) {
$questionScore +=$answerWeighting;
$totalScore +=$answerWeighting;
}
} else {
$studentChoice=$choice[$numAnswer];
if ($studentChoice) {
$questionScore +=$answerWeighting;
$totalScore +=$answerWeighting;
}
}
break;
case MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE:
if ($from_database) {
$queryans = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." where exe_id = '".$exeId."' and question_id= '".$questionId."'";
$resultans = Database::query($queryans);
while ($row = Database::fetch_array($resultans)) {
$ind = $row['answer'];
$result = explode(':',$ind);
$my_answer_id = $result[0];
$option = $result[1];
$choice[$my_answer_id] = $option;
}
$numAnswer=$objAnswerTmp->selectAutoId($answerId);
$studentChoice=$choice[$numAnswer];
if ($answerCorrect == 1) {
if ($studentChoice == 1) { //true value see MultipleAnswerCombinationTrueFalse class
$real_answers[$answerId] = true;
} elseif ($studentChoice == 2) { //false value
$real_answers[$answerId] = false;
} else {
$real_answers[$answerId] = false;
}
} else {
if ($studentChoice == 1) { //true value
$real_answers[$answerId] = false;
} elseif ($studentChoice == 2) { //false value see MultipleAnswerCombinationTrueFalse class
$real_answers[$answerId] = true;
} else {
$real_answers[$answerId] = true;
}
}
} else {
$studentChoice=$choice[$numAnswer];
if ($answerCorrect == 1) {
if ($studentChoice == 1) { //true value see MultipleAnswerCombinationTrueFalse class
$real_answers[$answerId] = true;
} elseif ($studentChoice == 2) { //false value
$real_answers[$answerId] = false;
} else {
$real_answers[$answerId] = false;
}
} else {
if ($studentChoice == 1) { //true value
$real_answers[$answerId] = false;
} elseif ($studentChoice == 2) { //false value see MultipleAnswerCombinationTrueFalse class
$real_answers[$answerId] = true;
} else {
$real_answers[$answerId] = true;
}
}
$final_answer = true;
foreach($real_answers as $my_answer) {
if (!$my_answer) {
$final_answer = false;
}
}
}
break;
case MULTIPLE_ANSWER_COMBINATION:
if ($from_database) {
$queryans = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." where exe_id = '".$exeId."' and question_id= '".$questionId."'";
$resultans = Database::query($queryans);
while ($row = Database::fetch_array($resultans)) {
$ind = $row['answer'];
$choice[$ind] = 1;
}
$numAnswer=$objAnswerTmp->selectAutoId($answerId);
$studentChoice=$choice[$numAnswer];
if ($answerCorrect == 1) {
if ($studentChoice) {
$real_answers[$answerId] = true;
} else {
$real_answers[$answerId] = false;
}
} else {
if ($studentChoice) {
$real_answers[$answerId] = false;
} else {
$real_answers[$answerId] = true;
}
}
} else {
$studentChoice=$choice[$numAnswer];
if ($answerCorrect == 1) {
if ($studentChoice) {
$real_answers[$answerId] = true;
} else {
$real_answers[$answerId] = false;
}
} else {
if ($studentChoice) {
$real_answers[$answerId] = false;
} else {
$real_answers[$answerId] = true;
}
}
$final_answer = true;
foreach($real_answers as $my_answer) {
if (!$my_answer) {
$final_answer = false;
}
}
}
break;
// for fill in the blanks
case FILL_IN_BLANKS :
// the question is encoded like this
// [A] B [C] D [E] F::10,10,10@1
// number 1 before the "@" means that is a switchable fill in blank question
// [A] B [C] D [E] F::10,10,10@ or [A] B [C] D [E] F::10,10,10
// means that is a normal fill blank question
// first we explode the "::"
$pre_array = explode('::', $answer);
// is switchable fill blank or not
$last = count($pre_array) - 1;
$is_set_switchable = explode('@', $pre_array[$last]);
$switchable_answer_set = false;
if (isset ($is_set_switchable[1]) && $is_set_switchable[1] == 1) {
$switchable_answer_set = true;
}
$answer = '';
for ($k = 0; $k < $last; $k++) {
$answer .= $pre_array[$k];
}
// splits weightings that are joined with a comma
$answerWeighting = explode(',', $is_set_switchable[0]);
// we save the answer because it will be modified
//$temp = $answer;
$temp = text_filter($answer);
$answer = '';
$j = 0;
//initialise answer tags
$user_tags = array ();
$correct_tags = array ();
$real_text = array ();
// the loop will stop at the end of the text
while (1) {
// quits the loop if there are no more blanks (detect '[')
if (($pos = api_strpos($temp, '[')) === false) {
// adds the end of the text
$answer = $temp;
/* // Deprecated code
// TeX parsing - replacement of texcode tags
$texstring = api_parse_tex($texstring);
$answer = str_replace("{texcode}", $texstring, $answer);
*/
$real_text[] = $answer;
break; //no more "blanks", quit the loop
}
// adds the piece of text that is before the blank
//and ends with '[' into a general storage array
$real_text[] = api_substr($temp, 0, $pos +1);
$answer .= api_substr($temp, 0, $pos +1);
//take the string remaining (after the last "[" we found)
$temp = api_substr($temp, $pos +1);
// quit the loop if there are no more blanks, and update $pos to the position of next ']'
if (($pos = api_strpos($temp, ']')) === false) {
// adds the end of the text
$answer .= $temp;
break;
}
if ($from_database) {
$queryfill = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." WHERE exe_id = '".Database::escape_string($exeId)."' AND question_id= '".Database::escape_string($questionId)."'";
$resfill = Database::query($queryfill);
$str = Database::result($resfill,0,'answer');
preg_match_all('#\[([^[]*)\]#', $str, $arr);
$str = str_replace('\r\n', '', $str);
$choice = $arr[1];
$tmp=strrpos($choice[$j],' / ');
$choice[$j]=api_substr($choice[$j],0,$tmp);
$choice[$j]=trim($choice[$j]);
//Needed to let characters ' and " to work as part of an answer
$choice[$j] = stripslashes($choice[$j]);
} else {
$choice[$j] = trim($choice[$j]);
}
$user_tags[] = api_strtolower($choice[$j]);
//put the contents of the [] answer tag into correct_tags[]
$correct_tags[] = api_strtolower(api_substr($temp, 0, $pos));
$j++;
$temp = api_substr($temp, $pos +1);
//$answer .= ']';
}
$answer = '';
$real_correct_tags = $correct_tags;
$chosen_list = array ();
for ($i = 0; $i < count($real_correct_tags); $i++) {
if ($i == 0) {
$answer .= $real_text[0];
}
if (!$switchable_answer_set) {
//needed to parse ' and " characters
$user_tags[$i] = stripslashes($user_tags[$i]);
if ($correct_tags[$i] == $user_tags[$i]) {
// gives the related weighting to the student
$questionScore += $answerWeighting[$i];
// increments total score
$totalScore += $answerWeighting[$i];
// adds the word in green at the end of the string
$answer .= $correct_tags[$i];
}
// else if the word entered by the student IS NOT the same as the one defined by the professor
elseif (!empty ($user_tags[$i])) {
// adds the word in red at the end of the string, and strikes it
$answer .= '' . $user_tags[$i] . '';
} else {
// adds a tabulation if no word has been typed by the student
$answer .= ' ';
}
} else {
// switchable fill in the blanks
if (in_array($user_tags[$i], $correct_tags)) {
$chosen_list[] = $user_tags[$i];
$correct_tags = array_diff($correct_tags, $chosen_list);
// gives the related weighting to the student
$questionScore += $answerWeighting[$i];
// increments total score
$totalScore += $answerWeighting[$i];
// adds the word in green at the end of the string
$answer .= $user_tags[$i];
}
elseif (!empty ($user_tags[$i])) {
// else if the word entered by the student IS NOT the same as the one defined by the professor
// adds the word in red at the end of the string, and strikes it
$answer .= '' . $user_tags[$i] . '';
} else {
// adds a tabulation if no word has been typed by the student
$answer .= ' ';
}
}
// adds the correct word, followed by ] to close the blank
$answer .= ' / ' . $real_correct_tags[$i] . ']';
if (isset ($real_text[$i +1])) {
$answer .= $real_text[$i +1];
}
}
break;
// for free answer
case FREE_ANSWER :
if ($from_database) {
$query = "SELECT answer, marks FROM ".$TBL_TRACK_ATTEMPT." WHERE exe_id = '".$exeId."' AND question_id= '".$questionId."'";
$resq = Database::query($query);
$choice = Database::result($resq,0,'answer');
$choice = str_replace('\r\n', '', $choice);
$choice = stripslashes($choice);
$questionScore = Database::result($resq,0,"marks");
if ($questionScore==-1) {
$totalScore+=0;
} else {
$totalScore+=$questionScore;
}
$arrques[] = $questionName;
$arrans[] = $choice;
} else {
$studentChoice = $choice;
if ($studentChoice) {
//Fixing negative puntation see #2193
$questionScore = 0;
$totalScore += 0;
}
}
break;
// for matching
case MATCHING :
if ($from_database) {
$sql_answer = 'SELECT id, answer FROM '.$table_ans.' WHERE question_id="'.$questionId.'" AND correct=0';
$res_answer = Database::query($sql_answer);
// getting the real answer
$real_list =array();
while ($real_answer = Database::fetch_array($res_answer)) {
$real_list[$real_answer['id']]= $real_answer['answer'];
}
$sql_select_answer = 'SELECT id, answer, correct, id_auto FROM '.$table_ans.'
WHERE question_id="'.$questionId.'" AND correct <> 0 ORDER BY id_auto';
$res_answers = Database::query($sql_select_answer);
$questionScore = 0;
while ($a_answers = Database::fetch_array($res_answers)) {
$i_answer_id = $a_answers['id']; //3
$s_answer_label = $a_answers['answer']; // your daddy - your mother
$i_answer_correct_answer = $a_answers['correct']; //1 - 2
$i_answer_id_auto = $a_answers['id_auto']; // 3 - 4
$sql_user_answer = "SELECT answer FROM $TBL_TRACK_ATTEMPT
WHERE exe_id = '$exeId' AND question_id = '$questionId' AND position='$i_answer_id_auto'";
$res_user_answer = Database::query($sql_user_answer);
if (Database::num_rows($res_user_answer)>0 ) {
$s_user_answer = Database::result($res_user_answer,0,0); // rich - good looking
} else {
$s_user_answer = 0;
}
$i_answerWeighting=$objAnswerTmp->selectWeighting($i_answer_id);
$user_answer = '';
if (!empty($s_user_answer)) {
if ($s_user_answer == $i_answer_correct_answer) {
$questionScore += $i_answerWeighting;
$totalScore += $i_answerWeighting;
$user_answer = ''.$real_list[$i_answer_correct_answer].'';
} else {
$user_answer = ''.$real_list[$s_user_answer].'';
}
}
if ($show_result) {
echo '
';
}
}
break(2); //break the switch and the "for" condition
} else {
$numAnswer=$objAnswerTmp->selectAutoId($answerId);
if ($answerCorrect) {
if ($answerCorrect == $choice[$numAnswer]) {
$questionScore += $answerWeighting;
$totalScore += $answerWeighting;
$user_answer = ''.$answer_matching[$choice[$numAnswer]].'';
} else {
$user_answer = ''.$answer_matching[$choice[$numAnswer]].'';
}
$matching[$numAnswer] = $choice[$numAnswer];
}
break;
}
// for hotspot with no order
case HOT_SPOT :
if ($from_database) {
if ($show_result) {
$TBL_TRACK_HOTSPOT = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
$query = "SELECT hotspot_correct FROM ".$TBL_TRACK_HOTSPOT." where hotspot_exe_id = '".$exeId."' and hotspot_question_id= '".$questionId."' AND hotspot_answer_id='".Database::escape_string($answerId)."'";
$resq=Database::query($query);
$studentChoice = Database::result($resq,0,"hotspot_correct");
}
} else {
$studentChoice = $choice[$answerId];
if ($studentChoice) {
$questionScore += $answerWeighting;
$totalScore += $answerWeighting;
}
}
break;
// @todo never added to chamilo
//for hotspot with fixed order
case HOT_SPOT_ORDER :
$studentChoice = $choice['order'][$answerId];
if ($studentChoice == $answerId) {
$questionScore += $answerWeighting;
$totalScore += $answerWeighting;
$studentChoice = true;
} else {
$studentChoice = false;
}
break;
// for hotspot with delineation
case HOT_SPOT_DELINEATION :
if ($from_database) {
// getting the user answer
$TBL_TRACK_HOTSPOT = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
$query = "SELECT hotspot_correct, hotspot_coordinate from ".$TBL_TRACK_HOTSPOT." where hotspot_exe_id = '".$exeId."' and hotspot_question_id= '".$questionId."' AND hotspot_answer_id='1'"; //by default we take 1 because it's a delineation
$resq = Database::query($query);
$row = Database::fetch_array($resq,'ASSOC');
$choice = $row['hotspot_correct'];
$user_answer = $row['hotspot_coordinate'];
// THIS is very important otherwise the poly_compile will throw an error!!
// round-up the coordinates
$coords = explode('/',$user_answer);
$user_array = '';
foreach ($coords as $coord) {
list($x,$y) = explode(';',$coord);
$user_array .= round($x).';'.round($y).'/';
}
$user_array = substr($user_array,0,-1);
} else {
if ($studentChoice) {
$newquestionList[]=$questionid;
}
if ($answerId===1) {
$studentChoice =$choice[$answerId];
$questionScore +=$answerWeighting;
if ($hotspot_delineation_result[1]==1) {
$totalScore +=$answerWeighting; //adding the total
}
//$totalScore +=$answerWeighting; we do not if the hotspot is right or not
}
}
$_SESSION['hotspot_coord'][1] = $delineation_cord;
$_SESSION['hotspot_dest'][1] = $answer_delineation_destination;
break;
} // end switch Answertype
global $origin;
if ($show_result) {
if ($from == 'exercise_result') {
if ($debug) error_log('Showing questions $from '.$from);
//display answers (if not matching type, or if the answer is correct)
if ($answerType != MATCHING || $answerCorrect) {
if (in_array($answerType, array(UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION, MULTIPLE_ANSWER, MULTIPLE_ANSWER_COMBINATION))) {
if ($origin!='learnpath') {
ExerciseShowFunctions::display_unique_or_multiple_answer($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,0,0,0);
}
} elseif($answerType == MULTIPLE_ANSWER_TRUE_FALSE) {
if ($origin!='learnpath') {
ExerciseShowFunctions::display_multiple_answer_true_false($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,0,$questionId,0);
}
} elseif($answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE ) {
if ($origin!='learnpath') {
ExerciseShowFunctions::display_multiple_answer_combination_true_false($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,0,0,0);
}
} elseif($answerType == FILL_IN_BLANKS) {
if ($origin!='learnpath') {
ExerciseShowFunctions::display_fill_in_blanks_answer($answer,0,0);
}
} elseif($answerType == FREE_ANSWER) {
// to store the details of open questions in an array to be used in mail
$arrques[] = $questionName;
$arrans[] = $choice;
if($origin != 'learnpath') {
ExerciseShowFunctions::display_free_answer($choice,0,0);
}
} elseif($answerType == HOT_SPOT) {
if ($origin != 'learnpath') {
ExerciseShowFunctions::display_hotspot_answer($answerId, $answer, $studentChoice, $answerComment);
}
} elseif($answerType == HOT_SPOT_ORDER) {
if ($origin != 'learnpath') {
ExerciseShowFunctions::display_hotspot_order_answer($answerId, $answer, $studentChoice, $answerComment);
}
} elseif ($answerType == HOT_SPOT_DELINEATION) {
$user_answer = $_SESSION['exerciseResultCoordinates'][$questionId];
//$_SESSION['exerciseResultCoordinates']=str_replace('/','|',$user_answer);
//if (!$comes_from_popup) {
//round-up the coordinates
$coords = explode('/',$user_answer);
$user_array = '';
foreach ($coords as $coord) {
list($x,$y) = explode(';',$coord);
$user_array .= round($x).';'.round($y).'/';
}
$user_array = substr($user_array,0,-1);
if ($next) {
//$tbl_track_e_hotspot = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
// Save into db
/* $sql = "INSERT INTO $tbl_track_e_hotspot (hotspot_user_id, hotspot_course_code, hotspot_exe_id, hotspot_question_id, hotspot_answer_id, hotspot_correct, hotspot_coordinate )
VALUES ('".Database::escape_string($_user['user_id'])."', '".Database::escape_string($_course['id'])."', '".Database::escape_string($exeId)."', '".Database::escape_string($questionId)."', '".Database::escape_string($answerId)."', '".Database::escape_string($studentChoice)."', '".Database::escape_string($user_array)."')";
$result = api_sql_query($sql,__FILE__,__LINE__);*/
$user_answer = $user_array;
// we compare only the delineation not the other points
$answer_question = $_SESSION['hotspot_coord'][1];
$answerDestination = $_SESSION['hotspot_dest'][1];
//calculating the area
$poly_user = convert_coordinates($user_answer,'/');
$poly_answer = convert_coordinates($answer_question,'|');
$max_coord = poly_get_max($poly_user,$poly_answer);
$poly_user_compiled = poly_compile($poly_user,$max_coord);
$poly_answer_compiled = poly_compile($poly_answer,$max_coord);
$poly_results = poly_result($poly_answer_compiled,$poly_user_compiled,$max_coord);
$overlap = $poly_results['both'];
$poly_answer_area = $poly_results['s1'];
$poly_user_area = $poly_results['s2'];
$missing = $poly_results['s1Only'];
$excess = $poly_results['s2Only'];
//$overlap = round(polygons_overlap($poly_answer,$poly_user)); //this is an area in pixels
if ($dbg_local>0) { error_log(__LINE__.' - Polygons results are '.print_r($poly_results,1),0);}
if ($overlap < 1) {
//shortcut to avoid complicated calculations
$final_overlap = 0;
$final_missing = 100;
$final_excess = 100;
} else {
// the final overlap is the percentage of the initial polygon that is overlapped by the user's polygon
$final_overlap = round(((float)$overlap / (float)$poly_answer_area)*100);
if ($dbg_local>1) { error_log(__LINE__.' - Final overlap is '.$final_overlap,0);}
// the final missing area is the percentage of the initial polygon that is not overlapped by the user's polygon
$final_missing = 100 - $final_overlap;
if ($dbg_local>1) { error_log(__LINE__.' - Final missing is '.$final_missing,0);}
// the final excess area is the percentage of the initial polygon's size that is covered by the user's polygon outside of the initial polygon
$final_excess = round((((float)$poly_user_area-(float)$overlap)/(float)$poly_answer_area)*100);
if ($dbg_local>1) { error_log(__LINE__.' - Final excess is '.$final_excess,0);}
}
//checking the destination parameters parsing the "@@"
$destination_items= explode('@@', $answerDestination);
$threadhold_total = $destination_items[0];
$threadhold_items=explode(';',$threadhold_total);
$threadhold1 = $threadhold_items[0]; // overlap
$threadhold2 = $threadhold_items[1]; // excess
$threadhold3 = $threadhold_items[2]; //missing
// if is delineation
if ($answerId===1) {
//setting colors
if ($final_overlap>=$threadhold1) {
$overlap_color=true; //echo 'a';
}
//echo $excess.'-'.$threadhold2;
if ($final_excess<=$threadhold2) {
$excess_color=true; //echo 'b';
}
//echo '--------'.$missing.'-'.$threadhold3;
if ($final_missing<=$threadhold3) {
$missing_color=true; //echo 'c';
}
// if pass
if ($final_overlap>=$threadhold1 && $final_missing<=$threadhold3 && $final_excess<=$threadhold2) {
$next=1; //go to the oars
$result_comment=get_lang('Acceptable');
$final_answer = 1; // do not update with update_exercise_attempt
} else {
$next=0;
$result_comment=get_lang('Unacceptable');
$comment=$answerDestination=$objAnswerTmp->selectComment(1);
$answerDestination=$objAnswerTmp->selectDestination(1);
//checking the destination parameters parsing the "@@"
$destination_items= explode('@@', $answerDestination);
}
} elseif($answerId>1) {
if ($objAnswerTmp->selectHotspotType($answerId) == 'noerror') {
if ($dbg_local>0) { error_log(__LINE__.' - answerId is of type noerror',0);}
//type no error shouldn't be treated
$next = 1;
continue;
}
if ($dbg_local>0) { error_log(__LINE__.' - answerId is >1 so we\'re probably in OAR',0);}
//check the intersection between the oar and the user
//echo 'user'; print_r($x_user_list); print_r($y_user_list);
//echo 'official';print_r($x_list);print_r($y_list);
//$result = get_intersection_data($x_list,$y_list,$x_user_list,$y_user_list);
$inter= $result['success'];
//$delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId);
$delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId);
$poly_answer = convert_coordinates($delineation_cord,'|');
$max_coord = poly_get_max($poly_user,$poly_answer);
$poly_answer_compiled = poly_compile($poly_answer,$max_coord);
$overlap = poly_touch($poly_user_compiled, $poly_answer_compiled,$max_coord);
if ($overlap == false) {
//all good, no overlap
$next = 1;
continue;
} else {
if ($dbg_local>0) { error_log(__LINE__.' - Overlap is '.$overlap.': OAR hit',0);}
$organs_at_risk_hit++;
//show the feedback
$next=0;
$comment=$answerDestination=$objAnswerTmp->selectComment($answerId);
$answerDestination=$objAnswerTmp->selectDestination($answerId);
$destination_items= explode('@@', $answerDestination);
/*
$try_hotspot=$destination_items[1];
$lp_hotspot=$destination_items[2];
$select_question_hotspot=$destination_items[3];
$url_hotspot=$destination_items[4];*/
}
}
} else { // the first delineation feedback
if ($dbg_local>0) { error_log(__LINE__.' first',0);}
}
} elseif($answerType==MATCHING) {
if ($origin != 'learnpath') {
echo '
';
break;
case HOT_SPOT:
ExerciseShowFunctions::display_hotspot_answer($answerId, $answer, $studentChoice, $answerComment);
break;
case HOT_SPOT_DELINEATION:
$user_answer = $user_array;
if ($next) {
//$tbl_track_e_hotspot = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
// Save into db
/* $sql = "INSERT INTO $tbl_track_e_hotspot (hotspot_user_id, hotspot_course_code, hotspot_exe_id, hotspot_question_id, hotspot_answer_id, hotspot_correct, hotspot_coordinate )
VALUES ('".Database::escape_string($_user['user_id'])."', '".Database::escape_string($_course['id'])."', '".Database::escape_string($exeId)."', '".Database::escape_string($questionId)."', '".Database::escape_string($answerId)."', '".Database::escape_string($studentChoice)."', '".Database::escape_string($user_array)."')";
$result = api_sql_query($sql,__FILE__,__LINE__);*/
$user_answer = $user_array;
// we compare only the delineation not the other points
$answer_question = $_SESSION['hotspot_coord'][1];
$answerDestination = $_SESSION['hotspot_dest'][1];
//calculating the area
$poly_user = convert_coordinates($user_answer,'/');
$poly_answer = convert_coordinates($answer_question,'|');
$max_coord = poly_get_max($poly_user,$poly_answer);
$poly_user_compiled = poly_compile($poly_user,$max_coord);
$poly_answer_compiled = poly_compile($poly_answer,$max_coord);
$poly_results = poly_result($poly_answer_compiled,$poly_user_compiled,$max_coord);
$overlap = $poly_results['both'];
$poly_answer_area = $poly_results['s1'];
$poly_user_area = $poly_results['s2'];
$missing = $poly_results['s1Only'];
$excess = $poly_results['s2Only'];
//$overlap = round(polygons_overlap($poly_answer,$poly_user)); //this is an area in pixels
if ($dbg_local>0) { error_log(__LINE__.' - Polygons results are '.print_r($poly_results,1),0);}
if ($overlap < 1) {
//shortcut to avoid complicated calculations
$final_overlap = 0;
$final_missing = 100;
$final_excess = 100;
} else {
// the final overlap is the percentage of the initial polygon that is overlapped by the user's polygon
$final_overlap = round(((float)$overlap / (float)$poly_answer_area)*100);
if ($dbg_local>1) { error_log(__LINE__.' - Final overlap is '.$final_overlap,0);}
// the final missing area is the percentage of the initial polygon that is not overlapped by the user's polygon
$final_missing = 100 - $final_overlap;
if ($dbg_local>1) { error_log(__LINE__.' - Final missing is '.$final_missing,0);}
// the final excess area is the percentage of the initial polygon's size that is covered by the user's polygon outside of the initial polygon
$final_excess = round((((float)$poly_user_area-(float)$overlap)/(float)$poly_answer_area)*100);
if ($dbg_local>1) { error_log(__LINE__.' - Final excess is '.$final_excess,0);}
}
//checking the destination parameters parsing the "@@"
$destination_items= explode('@@', $answerDestination);
$threadhold_total = $destination_items[0];
$threadhold_items=explode(';',$threadhold_total);
$threadhold1 = $threadhold_items[0]; // overlap
$threadhold2 = $threadhold_items[1]; // excess
$threadhold3 = $threadhold_items[2]; //missing
// if is delineation
if ($answerId===1) {
//setting colors
if ($final_overlap>=$threadhold1) {
$overlap_color=true; //echo 'a';
}
//echo $excess.'-'.$threadhold2;
if ($final_excess<=$threadhold2) {
$excess_color=true; //echo 'b';
}
//echo '--------'.$missing.'-'.$threadhold3;
if ($final_missing<=$threadhold3) {
$missing_color=true; //echo 'c';
}
// if pass
if ($final_overlap>=$threadhold1 && $final_missing<=$threadhold3 && $final_excess<=$threadhold2) {
$next=1; //go to the oars
$result_comment=get_lang('Acceptable');
$final_answer = 1; // do not update with update_exercise_attempt
} else {
$next=0;
$result_comment=get_lang('Unacceptable');
$comment=$answerDestination=$objAnswerTmp->selectComment(1);
$answerDestination=$objAnswerTmp->selectDestination(1);
//checking the destination parameters parsing the "@@"
$destination_items= explode('@@', $answerDestination);
}
} elseif($answerId>1) {
if ($objAnswerTmp->selectHotspotType($answerId) == 'noerror') {
if ($dbg_local>0) { error_log(__LINE__.' - answerId is of type noerror',0);}
//type no error shouldn't be treated
$next = 1;
continue;
}
if ($dbg_local>0) { error_log(__LINE__.' - answerId is >1 so we\'re probably in OAR',0);}
//check the intersection between the oar and the user
//echo 'user'; print_r($x_user_list); print_r($y_user_list);
//echo 'official';print_r($x_list);print_r($y_list);
//$result = get_intersection_data($x_list,$y_list,$x_user_list,$y_user_list);
$inter= $result['success'];
//$delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId);
$delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId);
$poly_answer = convert_coordinates($delineation_cord,'|');
$max_coord = poly_get_max($poly_user,$poly_answer);
$poly_answer_compiled = poly_compile($poly_answer,$max_coord);
$overlap = poly_touch($poly_user_compiled, $poly_answer_compiled,$max_coord);
if ($overlap == false) {
//all good, no overlap
$next = 1;
continue;
} else {
if ($dbg_local>0) { error_log(__LINE__.' - Overlap is '.$overlap.': OAR hit',0);}
$organs_at_risk_hit++;
//show the feedback
$next=0;
$comment=$answerDestination=$objAnswerTmp->selectComment($answerId);
$answerDestination=$objAnswerTmp->selectDestination($answerId);
$destination_items= explode('@@', $answerDestination);
/*
$try_hotspot=$destination_items[1];
$lp_hotspot=$destination_items[2];
$select_question_hotspot=$destination_items[3];
$url_hotspot=$destination_items[4];*/
}
}
} else { // the first delineation feedback
if ($dbg_local>0) { error_log(__LINE__.' first',0);}
}
break;
case HOT_SPOT_ORDER:
ExerciseShowFunctions::display_hotspot_order_answer($answerId, $answer, $studentChoice, $answerComment);
break;
case MATCHING:
if ($origin != 'learnpath') {
echo '
';
}
break;
}
}
}
} // end for that loops over all answers of the current question
// destruction of Answer
if (!$saved_results && $answerType == HOT_SPOT) {
$queryfree = "SELECT marks FROM ".$TBL_TRACK_ATTEMPT." where exe_id = '".Database::escape_string($exeId)."' and question_id= '".Database::escape_string($questionId)."'";
$resfree = Database::query($queryfree);
$questionScore = Database::result($resfree,0,"marks");
}
$final_answer = true;
foreach($real_answers as $my_answer) {
if (!$my_answer) {
$final_answer = false;
}
}
//we add the total score after dealing with the answers
if ($answerType == MULTIPLE_ANSWER_COMBINATION || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE ) {
if ($final_answer) {
//getting only the first score where we save the weight of all the question
$answerWeighting = $objAnswerTmp->selectWeighting(1);
$questionScore += $answerWeighting;
$totalScore += $answerWeighting;
}
}
$extra_data = array('final_overlap' => $final_overlap, 'final_missing'=>$final_missing, 'final_excess'=> $final_excess,
'overlap_color' => $overlap_color, 'missing_color'=>$missing_color, 'excess_color'=> $excess_color,
'threadhold1' => $threadhold1, 'threadhold2'=>$threadhold2, 'threadhold3'=> $threadhold3,
);
if ($from == 'exercise_result') {
// if answer is hotspot. To the difference of exercise_show.php, we use the results from the session (from_db=0)
// TODO Change this, because it is wrong to show the user some results that haven't been stored in the database yet
if ($answerType == HOT_SPOT || $answerType == HOT_SPOT_ORDER || $answerType == HOT_SPOT_DELINEATION ) {
if ($debug) error_log('$from AND this is a hotspot kind of question ');
$my_exe_id = 0;
$from_database = 0;
if ($answerType == HOT_SPOT_DELINEATION) {
if (0) {
if ($overlap_color) {
$overlap_color='green';
} else {
$overlap_color='red';
}
if ($missing_color) {
$missing_color='green';
} else {
$missing_color='red';
}
if ($excess_color) {
$excess_color='green';
} else {
$excess_color='red';
}
if (!is_numeric($final_overlap)) {
$final_overlap = 0;
}
if (!is_numeric($final_missing)) {
$final_missing = 0;
}
if (!is_numeric($final_excess)) {
$final_excess = 0;
}
if ($final_overlap>100) {
$final_overlap = 100;
}
$table_resume='