Add new exercise report, showing sessions and exercise categories

See BT#13187

- Add course id when creating new objects Answer/Question/Exercise
- Format code
pull/2487/head
Julio 7 years ago
parent 84dee178b6
commit eb7d4027e6
  1. 31
      main/exercise/TestCategory.php
  2. 2
      main/exercise/exercise.class.php
  3. 2
      main/exercise/question.class.php
  4. 52
      main/inc/ajax/model.ajax.php
  5. 149
      main/inc/lib/exercise.lib.php
  6. 33
      main/inc/lib/myspace.lib.php
  7. 195
      main/mySpace/exercise_category_report.php

@ -316,13 +316,14 @@ class TestCategory
* return : array of category id (integer)
* hubert.borderiou 07-04-2011
* @param int $exerciseId
* @param int $courseId
*
* @return array
*/
public static function getListOfCategoriesIDForTest($exerciseId)
public static function getListOfCategoriesIDForTest($exerciseId, $courseId = 0)
{
// parcourir les questions d'un test, recup les categories uniques dans un tableau
$exercise = new Exercise();
$exercise = new Exercise($courseId);
$exercise->read($exerciseId, false);
$categoriesInExercise = $exercise->getQuestionWithCategories();
// the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ???
@ -337,14 +338,14 @@ class TestCategory
}
/**
* @param Exercise $exercise_obj
* @param Exercise $exercise
* @return array
*/
public static function getListOfCategoriesIDForTestObject(Exercise $exercise_obj)
public static function getListOfCategoriesIDForTestObject(Exercise $exercise)
{
// parcourir les questions d'un test, recup les categories uniques dans un tableau
$categories_in_exercise = array();
$question_list = $exercise_obj->getQuestionOrderedListByName();
$question_list = $exercise->getQuestionOrderedListByName();
// the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ???
foreach ($question_list as $questionInfo) {
@ -397,13 +398,13 @@ class TestCategory
}
/**
* @param Exercise $exercise_obj
* @param Exercise $exercise
* @return array
*/
public static function getListOfCategoriesForTest(Exercise $exercise_obj)
public static function getListOfCategoriesForTest(Exercise $exercise)
{
$result = array();
$categories = self::getListOfCategoriesIDForTestObject($exercise_obj);
$categories = self::getListOfCategoriesIDForTestObject($exercise);
foreach ($categories as $cat_id) {
$cat = new TestCategory();
$cat = (array) $cat->getCategory($cat_id);
@ -1011,19 +1012,19 @@ class TestCategory
/**
* Returns the category form.
* @param Exercise $exercise_obj
* @param Exercise $exercise
* @return string
*/
public function returnCategoryForm(Exercise $exercise_obj)
public function returnCategoryForm(Exercise $exercise)
{
$categories = $this->getListOfCategoriesForTest($exercise_obj);
$saved_categories = $exercise_obj->get_categories_in_exercise();
$categories = $this->getListOfCategoriesForTest($exercise);
$saved_categories = $exercise->get_categories_in_exercise();
$return = null;
if (!empty($categories)) {
$nbQuestionsTotal = $exercise_obj->getNumberQuestionExerciseCategory();
$exercise_obj->setCategoriesGrouping(true);
$real_question_count = count($exercise_obj->getQuestionList());
$nbQuestionsTotal = $exercise->getNumberQuestionExerciseCategory();
$exercise->setCategoriesGrouping(true);
$real_question_count = count($exercise->getQuestionList());
$warning = null;
if ($nbQuestionsTotal != $real_question_count) {

@ -3475,7 +3475,7 @@ class Exercise
}
// Construction of the Answer object
$objAnswerTmp = new Answer($questionId);
$objAnswerTmp = new Answer($questionId, $course_id);
$nbrAnswers = $objAnswerTmp->selectNbrAnswers();
if ($debug) {

@ -159,7 +159,7 @@ abstract class Question
$objQuestion->extra = $object->extra;
$objQuestion->course = $course_info;
$objQuestion->feedback = isset($object->feedback) ? $object->feedback : '';
$objQuestion->category = TestCategory::getCategoryForQuestion($id);
$objQuestion->category = TestCategory::getCategoryForQuestion($id, $course_id);
$tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
$sql = "SELECT DISTINCT q.exercice_id

@ -33,6 +33,7 @@ if (!in_array(
$action,
array(
'get_exercise_results',
'get_exercise_results_report',
'get_work_student_list_overview',
'get_hotpotatoes_exercise_results',
'get_work_teacher',
@ -520,6 +521,19 @@ switch ($action) {
$whereCondition
);
break;
case 'get_exercise_results_report':
$exerciseId = $_REQUEST['exercise_id'];
$courseId = $_REQUEST['course_id'];
$startDate = Database::escape_string($_REQUEST['start_date']);
$courseInfo = api_get_course_info_by_id($courseId);
$whereCondition .= " AND exe_date > '$startDate' ";
$count = ExerciseLib::get_count_exam_results(
$exerciseId,
$whereCondition,
$courseInfo['code'],
true
);
break;
case 'get_hotpotatoes_exercise_results':
$hotpot_path = $_REQUEST['path'];
$count = ExerciseLib::get_count_exam_hotpotatoes_results($hotpot_path);
@ -1147,6 +1161,43 @@ switch ($action) {
$whereCondition
);
break;
case 'get_exercise_results_report':
api_protect_admin_script();
// Used inside ExerciseLib::get_exam_results_data()
$documentPath = api_get_path(SYS_COURSE_PATH).$courseInfo['path']."/document";
$columns = array(
'firstname',
'lastname',
'username',
'session',
'start_date',
'exe_date',
'score'
);
$categoryList = TestCategory::getListOfCategoriesIDForTest($exerciseId, $courseId);
if (!empty($categoryList)) {
foreach ($categoryList as $categoryInfo) {
$columns[] = 'category_'.$categoryInfo['id'];
}
}
$columns[] = 'actions';
$result = ExerciseLib::get_exam_results_data(
$start,
$limit,
$sidx,
$sord,
$exerciseId,
$whereCondition,
false,
$courseInfo['code'],
true,
true
);
break;
case 'get_hotpotatoes_exercise_results':
$course = api_get_course_info();
$documentPath = api_get_path(SYS_COURSE_PATH).$course['path']."/document";
@ -1966,6 +2017,7 @@ $allowed_actions = array(
'get_session_progress',
'get_exercise_progress',
'get_exercise_results',
'get_exercise_results_report',
'get_work_student_list_overview',
'get_hotpotatoes_exercise_results',
'get_work_teacher',

@ -1508,9 +1508,12 @@ HOTSPOT;
* Gets count of exam results
* @param int $exerciseId
* @param array $conditions
* @param string $courseCode
* @param bool $showSession
*
* @return array
*/
public static function get_count_exam_results($exerciseId, $conditions)
public static function get_count_exam_results($exerciseId, $conditions, $courseCode = '', $showSession = false)
{
$count = self::get_exam_results_data(
null,
@ -1519,8 +1522,11 @@ HOTSPOT;
null,
$exerciseId,
$conditions,
true
true,
$courseCode,
$showSession
);
return $count;
}
@ -1680,7 +1686,9 @@ HOTSPOT;
$exercise_id,
$extra_where_conditions = null,
$get_count = false,
$courseCode = null
$courseCode = null,
$showSessionField = false,
$showExerciseCategories = false
) {
//@todo replace all this globals
global $documentPath, $filter;
@ -1688,9 +1696,7 @@ HOTSPOT;
$courseCode = empty($courseCode) ? api_get_course_id() : $courseCode;
$courseInfo = api_get_course_info($courseCode);
$course_id = $courseInfo['real_id'];
$sessionId = api_get_session_id();
$is_allowedToEdit = api_is_allowed_to_edit(null, true) || api_is_allowed_to_edit(true) || api_is_drh() || api_is_student_boss();
$TBL_USER = Database::get_main_table(TABLE_MAIN_USER);
$TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
$TBL_GROUP_REL_USER = Database::get_course_table(TABLE_GROUP_USER);
@ -1698,10 +1704,14 @@ HOTSPOT;
$TBL_TRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$TBL_TRACK_HOTPOTATOES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
$TBL_TRACK_ATTEMPT_RECORDING = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
$session_id_and = ' AND te.session_id = '.$sessionId.' ';
$sessionId = api_get_session_id();
$session_id_and = '';
$sessionCondition = '';
if (!$showSessionField) {
$session_id_and = " AND te.session_id = '.$sessionId ";
$sessionCondition = " AND ttte.session_id = $sessionId";
}
$exercise_id = intval($exercise_id);
$exercise_where = '';
if (!empty($exercise_id)) {
$exercise_where .= ' AND te.exe_exo_id = '.$exercise_id.' ';
@ -1717,12 +1727,13 @@ HOTSPOT;
$sql_inner_join_tbl_track_exercices = "
(
SELECT DISTINCT ttte.*, if(tr.exe_id,1, 0) as revised
FROM $TBL_TRACK_EXERCICES ttte LEFT JOIN $TBL_TRACK_ATTEMPT_RECORDING tr
FROM $TBL_TRACK_EXERCICES ttte
LEFT JOIN $TBL_TRACK_ATTEMPT_RECORDING tr
ON (ttte.exe_id = tr.exe_id)
WHERE
c_id = $course_id AND
exe_exo_id = $exercise_id AND
ttte.session_id = ".$sessionId."
exe_exo_id = $exercise_id
$sessionCondition
)";
if ($is_allowedToEdit) {
@ -1841,6 +1852,7 @@ HOTSPOT;
te.exe_weighting,
te.exe_date,
te.exe_id,
te.session_id,
email as exemail,
te.start_date,
ce.expired_time,
@ -1947,7 +1959,6 @@ HOTSPOT;
$lp_list_obj = new LearnpathList(api_get_user_id());
$lp_list = $lp_list_obj->get_flat_list();
$oldIds = array_column($lp_list, 'lp_old_id', 'iid');
if (is_array($results)) {
@ -2108,12 +2119,12 @@ HOTSPOT;
break;
case 2: //finished but not marked as such
$actions .= '<a href="exercise_report.php?'
. api_get_cidreq()
. '&exerciseId='
. $exercise_id
. '&a=close&id='
. $id
. '">'.
.api_get_cidreq()
.'&exerciseId='
.$exercise_id
.'&a=close&id='
.$id
.'">'.
Display:: return_icon(
'lock.png',
get_lang('MarkAttemptAsClosed'),
@ -2127,13 +2138,12 @@ HOTSPOT;
);
break;
case 3: //still ongoing
$actions .= "".
Display:: return_icon(
'clock.png',
get_lang('AttemptStillOngoingPleaseWait'),
array(),
ICON_SIZE_SMALL
);
$actions .= Display:: return_icon(
'clock.png',
get_lang('AttemptStillOngoingPleaseWait'),
array(),
ICON_SIZE_SMALL
);
$actions .= '';
$revisedLabel = Display::label(
get_lang('Ongoing'),
@ -2158,9 +2168,8 @@ HOTSPOT;
false
);
$actions .= '<a href="http://www.whatsmyip.org/ip-geo-location/?ip='.$ip.'" target="_blank">'
. Display::return_icon('info.png', $ip)
.'</a>';
.Display::return_icon('info.png', $ip)
.'</a>';
$recalculateUrl = api_get_path(WEB_CODE_PATH).'exercise/recalculate.php?'.
api_get_cidreq().'&'.
@ -2180,7 +2189,8 @@ HOTSPOT;
]
);
$delete_link = '<a href="exercise_report.php?'.api_get_cidreq().'&filter_by_user='.intval($_GET['filter_by_user']).'&filter='.$filter.'&exerciseId='.$exercise_id.'&delete=delete&did='.$id.'"
$filterByUser = isset($_GET['filter_by_user']) ? (int) $_GET['filter_by_user'] : 0;
$delete_link = '<a href="exercise_report.php?'.api_get_cidreq().'&filter_by_user='.$filterByUser.'&filter='.$filter.'&exerciseId='.$exercise_id.'&delete=delete&did='.$id.'"
onclick="javascript:if(!confirm(\'' . sprintf(
get_lang('DeleteAttempt'),
$results[$i]['username'],
@ -2201,7 +2211,6 @@ HOTSPOT;
}
$actions .= $delete_link;
}
} else {
$attempt_url = api_get_path(WEB_CODE_PATH).'exercise/result.php?'.api_get_cidreq().'&id='.$results[$i]['exe_id'].'&id_session='.$sessionId;
$attempt_link = Display::url(
@ -2215,10 +2224,84 @@ HOTSPOT;
$actions .= $attempt_link;
}
$actions .= '</div>';
$results[$i]['id'] = $results[$i]['exe_id'];
$exeId = $results[$i]['exe_id'];
$results[$i]['id'] = $exeId;
if ($is_allowedToEdit) {
$sessionName = '';
if (!empty($results[$i]['session_id'])) {
$sessionName = api_get_session_name($results[$i]['session_id']);
}
$objExercise = new Exercise($course_id);
if ($showExerciseCategories) {
$question_list = array();
// Getting attempt info
$exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
if (!empty($exercise_stat_info['data_tracking'])) {
$question_list = explode(',', $exercise_stat_info['data_tracking']);
if (!empty($question_list)) {
foreach ($question_list as $questionId) {
$objQuestionTmp = Question::read($questionId, $course_id);
// We're inside *one* question. Go through each possible answer for this question
$result = $objExercise->manage_answer(
$exeId,
$questionId,
null,
'exercise_result',
false,
false,
true,
false,
$objExercise->selectPropagateNeg(),
null,
true
);
$my_total_score = $result['score'];
$my_total_weight = $result['weight'];
// Category report
$category_was_added_for_this_test = false;
if (isset($objQuestionTmp->category) && !empty($objQuestionTmp->category)) {
if (!isset($category_list[$objQuestionTmp->category]['score'])) {
$category_list[$objQuestionTmp->category]['score'] = 0;
}
if (!isset($category_list[$objQuestionTmp->category]['total'])) {
$category_list[$objQuestionTmp->category]['total'] = 0;
}
$category_list[$objQuestionTmp->category]['score'] += $my_total_score;
$category_list[$objQuestionTmp->category]['total'] += $my_total_weight;
$category_was_added_for_this_test = true;
}
if (isset($objQuestionTmp->category_list) && !empty($objQuestionTmp->category_list)) {
foreach ($objQuestionTmp->category_list as $category_id) {
$category_list[$category_id]['score'] += $my_total_score;
$category_list[$category_id]['total'] += $my_total_weight;
$category_was_added_for_this_test = true;
}
}
// No category for this question!
if ($category_was_added_for_this_test == false) {
if (!isset($category_list['none']['score'])) {
$category_list['none']['score'] = 0;
}
if (!isset($category_list['none']['total'])) {
$category_list['none']['total'] = 0;
}
$category_list['none']['score'] += $my_total_score;
$category_list['none']['total'] += $my_total_weight;
}
}
}
}
}
foreach ($category_list as $categoryId => $result) {
$results[$i]['category_'.$categoryId] = self::show_score($result['score'], $result['total']);
}
$results[$i]['session'] = $sessionName;
$results[$i]['status'] = $revisedLabel;
$results[$i]['score'] = $score;
$results[$i]['lp'] = $lp_name;
@ -4116,7 +4199,7 @@ EOT;
if ($show_results) {
$question_content .= '</div>';
}
if(!$show_only_score){
if (!$show_only_score) {
$exercise_content .= Display::div(Display::panel($question_content),array('class' => 'question-panel'));
}
} // end foreach() block that loops over all questions

@ -18,37 +18,40 @@ class MySpace
*/
public static function getAdminActions()
{
$actions = array(
//array('url' => api_get_path(WEB_CODE_PATH).'mySpace/index.php', 'content' => get_lang('Home')),
array(
$actions = [
[
'url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=coaches',
'content' => get_lang('DisplayCoaches'),
),
array(
],
[
'url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=user',
'content' => get_lang('DisplayUserOverview'),
),
array(
],
[
'url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=session',
'content' => get_lang('DisplaySessionOverview'),
),
array(
],
[
'url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=course',
'content' => get_lang('DisplayCourseOverview'),
),
array(
],
[
'url' => api_get_path(WEB_CODE_PATH).'tracking/question_course_report.php?view=admin',
'content' => get_lang('LPQuestionListResults'),
),
array(
],
[
'url' => api_get_path(WEB_CODE_PATH).'tracking/course_session_report.php?view=admin',
'content' => get_lang('LPExerciseResultsBySession'),
),
],
[
'url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=accessoverview',
'content' => get_lang('DisplayAccessOverview').' ('.get_lang('Beta').')',
],
);
[
'url' => api_get_path(WEB_CODE_PATH).'mySpace/exercise_category_report.php',
'content' => get_lang('ExerciseCategoryAllSessionsReport'),
],
];
return Display::actions($actions, null);
}

@ -0,0 +1,195 @@
<?php
/* For licensing terms, see /license.txt */
$cidReset = true;
require_once __DIR__.'/../inc/global.inc.php';
api_protect_admin_script();
$exportCSV = isset($_GET['export']) && $_GET['export'] === 'csv' ? true : false;
// the section (for the tabs)
$this_section = SECTION_TRACKING;
$csv_content = array();
$nameTools = get_lang('MySpace');
$is_platform_admin = api_is_platform_admin();
$is_drh = api_is_drh();
$is_session_admin = api_is_session_admin();
if ($exportCSV) {
if ($display == 'user') {
MySpace::export_tracking_user_overview();
exit;
} elseif ($display == 'session') {
MySpace::export_tracking_session_overview();
exit;
} elseif ($display == 'course') {
MySpace::export_tracking_course_overview();
exit;
}
}
$currentUrl = api_get_self();
$courseId = isset($_GET['course_id']) ? (int) $_GET['course_id'] : 0;
$defaults = [];
$defaults['start_date'] = isset($_GET['start_date']) ? Security::remove_XSS($_GET['start_date']) : '';
$defaults['course_id'] = $courseId;
$htmlHeadXtra[] = api_get_jqgrid_js();
$htmlHeadXtra[] = '<script>
$(document).ready( function() {
$("#exercise_course_id").on("change", function(e) {
var data = $(this).select2(\'data\');
var option = data[0];
//Then I take the values like if I work with an array
var value = option.id;
var selectedDate = $("#start_date").datepicker({ dateFormat: \'dd,MM,yyyy\' }).val();
window.location.replace("'.$currentUrl.'?start_date="+selectedDate+"&course_id="+value);
});
});
</script>';
Display::display_header($nameTools);
$form = new FormValidator('exercise', 'get');
$form->addDatePicker('start_date', get_lang('StartDate'));
if (empty($courseId)) {
$form->addSelectAjax(
'course_id',
get_lang('Course'),
null,
[
'url' => api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=search_course'
]
);
} else {
$courseInfo = api_get_course_info_by_id($courseId);
$form->addHidden('course_id', $courseId);
$form->addLabel(get_lang('Course'), $courseInfo['name']);
$exerciseList = ExerciseLib::get_all_exercises_for_course_id(
$courseInfo,
0,
$courseId,
true
);
if (!empty($exerciseList)) {
$options = [];
foreach ($exerciseList as $exercise) {
$options[$exercise['id']] = $exercise['title'];
}
$form->addSelect('exercise_id', get_lang('Exercises'), $options);
} else {
$form->addLabel(get_lang('Exercises'), Display::return_message(get_lang('NoExercises')));
}
}
$form->setDefaults($defaults);
$form->addButtonSearch(get_lang('Search'));
$form->display();
if ($form->validate()) {
$values = $form->getSubmitValues();
$exerciseId = $values['exercise_id'];
$startDate = Security::remove_XSS($values['start_date']);
$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_exercise_results_report&exercise_id='.$exerciseId.'&course_id='.$courseId.'&start_date='.$startDate;
$categoryList = TestCategory::getListOfCategoriesIDForTest($exerciseId, $courseId);
$columns = array(
get_lang('FirstName'),
get_lang('LastName'),
get_lang('LoginName'),
get_lang('Session'),
get_lang('StartDate'),
get_lang('EndDate'),
get_lang('Score')
);
if (!empty($categoryList)) {
foreach ($categoryList as $categoryInfo) {
$columns[] = $categoryInfo['title'];
}
}
$columns[] = get_lang('Actions');
$columnModel = array(
array('name' => 'firstname', 'index' => 'firstname', 'width' => '50', 'align' => 'left', 'search' => 'true'),
array('name' => 'lastname', 'index' => 'lastname', 'width' => '50', 'align' => 'left', 'formatter' => 'action_formatter', 'search' => 'true'),
array('name' => 'login', 'index' => 'username', 'width' => '40', 'align' => 'left', 'search' => 'true', 'hidden' => 'true'),
array('name' => 'session', 'index' => 'session', 'width' => '40', 'align' => 'left', 'search' => 'false'),
array('name' => 'start_date', 'index' => 'start_date', 'width' => '60', 'align' => 'left', 'search' => 'true'),
array('name' => 'exe_date', 'index' => 'exe_date', 'width' => '60', 'align' => 'left', 'search' => 'true'),
array('name' => 'score', 'index' => 'exe_result', 'width' => '50', 'align' => 'center', 'search' => 'true')
);
if (!empty($categoryList)) {
foreach ($categoryList as $categoryInfo) {
$columnModel[] = array('name' => 'category_'.$categoryInfo['id'], 'index' => 'exe_result', 'width' => '50', 'align' => 'center', 'search' => 'true');
}
}
$columnModel[] = array(
'name' => 'actions',
'index' => 'actions',
'width' => '60',
'align' => 'left',
'search' => 'false',
'sortable' => 'false',
'hidden' => 'true'
);
$extra_params['autowidth'] = 'true';
//height auto
$extra_params['height'] = 'auto';
$actionLinks = '
// add username as title in lastname filed - ref 4226
function action_formatter(cellvalue, options, rowObject) {
// rowObject is firstname,lastname,login,... get the third word
var loginx = "'.api_htmlentities(sprintf(get_lang("LoginX"), ":::"), ENT_QUOTES).'";
var tabLoginx = loginx.split(/:::/);
// tabLoginx[0] is before and tabLoginx[1] is after :::
// may be empty string but is defined
return "<span title=\""+tabLoginx[0]+rowObject[2]+tabLoginx[1]+"\">"+cellvalue+"</span>";
}';
$tableId = 'results';
?>
<script>
$(function() {
<?php
echo Display::grid_js(
'results',
$url,
$columns,
$columnModel,
$extra_params,
array(),
$actionLinks,
true
);
?>
});
</script>
<?php
echo '<script>$(function() {
jQuery("#'.$tableId.'").jqGrid("navGrid","#'.$tableId.'_pager",{view:false, edit:false, add:false, del:false, search:false, excel:true});
jQuery("#'.$tableId.'").jqGrid("navButtonAdd","#'.$tableId.'_pager",{
caption:"",
title:"' . get_lang('ExportExcel').'",
onClickButton : function () {
jQuery("#'.$tableId.'").jqGrid("excelExport",{"url":"'.$url.'&export_format=xls"});
}
});
});</script>';
echo Display::grid_html('results');
}
Display::display_footer();
Loading…
Cancel
Save