public/main/gradebook/lib/user_data_generator.class.php
<?php
/* For licensing terms, see /license.txt */
/**
* Class UserDataGenerator
* Class to select, sort and transform object data into array data,
* used for a student's general view.
*
* @author Bert Steppé
*/
class UserDataGenerator
{
// Sorting types constants
const UDG_SORT_TYPE = 1;
const UDG_SORT_NAME = 2;
const UDG_SORT_COURSE = 4;
const UDG_SORT_CATEGORY = 8;
const UDG_SORT_AVERAGE = 16;
const UDG_SORT_SCORE = 32;
const UDG_SORT_MASK = 64;
const UDG_SORT_ASC = 128;
const UDG_SORT_DESC = 256;
private $items;
private $userid;
private $coursecodecache;
private $categorycache;
private $scorecache;
private $avgcache;
/**
* UserDataGenerator constructor.
*
* @param int $userid
* @param array $evals
* @param array $links
*/
public function __construct($userid, $evals = [], $links = [])
{
$this->userid = $userid;
$result = [];
/** @var Evaluation $eval */
foreach ($evals as $eval) {
$toadd = true;
$courseId = $eval->getCourseId();
if (!empty($courseId)) {
$result = Result::load(null, $userid, $eval->get_id());
if (0 == count($result)) {
$toadd = false;
}
}
if ($toadd) {
$evals_filtered_copy = $evals;
}
}
if (0 == count($result)) {
$evals_filtered = $evals;
} else {
$evals_filtered = $evals_filtered_copy;
}
$this->items = array_merge($evals_filtered, $links);
$this->coursecodecache = [];
$this->categorycache = [];
$this->scorecache = null;
$this->avgcache = null;
}
/**
* Get total number of items (rows).
*/
public function get_total_items_count()
{
return count($this->items);
}
/**
* Get actual array data.
*
* @return array 2-dimensional array - each array contains the elements:
* 0: eval/link object
* 1: item name
* 2: course name
* 3: category name
* 4: average score
* 5: student's score
* 6: student's score as custom display (only if custom scoring enabled)
*/
public function get_data(
$sorting = 0,
$start = 0,
$count = null,
$ignore_score_color = false
) {
// do some checks on count, redefine if invalid value
if (!isset($count)) {
$count = count($this->items) - $start;
}
if ($count < 0) {
$count = 0;
}
$allitems = $this->items;
// sort users array
if ($sorting & self::UDG_SORT_TYPE) {
usort($allitems, ['UserDataGenerator', 'sort_by_type']);
} elseif ($sorting & self::UDG_SORT_NAME) {
usort($allitems, ['UserDataGenerator', 'sort_by_name']);
} elseif ($sorting & self::UDG_SORT_COURSE) {
usort($allitems, ['UserDataGenerator', 'sort_by_course']);
} elseif ($sorting & self::UDG_SORT_CATEGORY) {
usort($allitems, ['UserDataGenerator', 'sort_by_category']);
} elseif ($sorting & self::UDG_SORT_AVERAGE) {
// if user sorts on average scores, first calculate them and cache them
foreach ($allitems as $item) {
$this->avgcache[$item->get_item_type().$item->get_id()] = $item->calc_score();
}
usort($allitems, ['UserDataGenerator', 'sort_by_average']);
} elseif ($sorting & self::UDG_SORT_SCORE) {
// if user sorts on student's scores, first calculate them and cache them
foreach ($allitems as $item) {
$this->scorecache[$item->get_item_type().$item->get_id()] = $item->calc_score($this->userid);
}
usort($allitems, ['UserDataGenerator', 'sort_by_score']);
} elseif ($sorting & self::UDG_SORT_MASK) {
// if user sorts on student's masks, first calculate scores and cache them
foreach ($allitems as $item) {
$this->scorecache[$item->get_item_type().$item->get_id()] = $item->calc_score($this->userid);
}
usort($allitems, ['UserDataGenerator', 'sort_by_mask']);
}
if ($sorting & self::UDG_SORT_DESC) {
$allitems = array_reverse($allitems);
}
// select the items we have to display
$visibleitems = array_slice($allitems, $start, $count);
// fill score cache if not done yet
if (!isset($this->scorecache)) {
foreach ($visibleitems as $item) {
$this->scorecache[$item->get_item_type().$item->get_id()] = $item->calc_score($this->userid);
}
}
// generate the data to display
$scoredisplay = ScoreDisplay::instance();
$data = [];
$model = ExerciseLib::getCourseScoreModel();
foreach ($visibleitems as $item) {
$row = [];
$row[] = $item;
$row[] = $item->get_name();
$row[] = $this->build_course_name($item);
$row[] = $this->build_category_name($item);
if (!empty($model)) {
if (isset($this->avgcache)) {
$avgscore = $this->avgcache[$item->get_item_type().$item->get_id()];
} else {
$avgscore = $item->calc_score();
}
$row[] = ExerciseLib::show_score($avgscore[0], $avgscore[1]);
$score = $this->scorecache[$item->get_item_type().$item->get_id()];
$displayScore = ExerciseLib::show_score($score[0], $score[1]);
$row[] = $displayScore;
if ($scoredisplay->is_custom()) {
$row[] = $displayScore;
}
} else {
$row[] = $this->build_average_column($item, $ignore_score_color);
$row[] = $this->build_result_column($item, $ignore_score_color);
if ($scoredisplay->is_custom()) {
$row[] = $this->build_mask_column($item, $ignore_score_color);
}
}
$data[] = $row;
}
return $data;
}
/**
* @param $item1
* @param $item2
*
* @return int
*/
public function sort_by_type($item1, $item2)
{
if ($item1->get_item_type() == $item2->get_item_type()) {
return $this->sort_by_name($item1, $item2);
} else {
return $item1->get_item_type() < $item2->get_item_type() ? -1 : 1;
}
}
/**
* @param $item1
* @param $item2
*
* @return int
*/
public function sort_by_course($item1, $item2)
{
$name1 = api_strtolower(
$this->get_course_name_from_code_cached($item1->get_course_code())
);
$name2 = api_strtolower(
$this->get_course_name_from_code_cached($item2->get_course_code())
);
return api_strnatcmp($name1, $name2);
}
/**
* @param $item1
* @param $item2
*
* @return int
*/
public function sort_by_category($item1, $item2)
{
$cat1 = $this->get_category_cached($item1->get_category_id());
$cat2 = $this->get_category_cached($item2->get_category_id());
$name1 = api_strtolower($this->get_category_name_to_display($cat1));
$name2 = api_strtolower($this->get_category_name_to_display($cat2));
return api_strnatcmp($name1, $name2);
}
/**
* @param $item1
* @param $item2
*
* @return int
*/
public function sort_by_name($item1, $item2)
{
return api_strnatcmp($item1->get_name(), $item2->get_name());
}
/**
* @param $item1
* @param $item2
*
* @return int
*/
public function sort_by_average($item1, $item2)
{
$score1 = $this->avgcache[$item1->get_item_type().$item1->get_id()];
$score2 = $this->avgcache[$item2->get_item_type().$item2->get_id()];
return $this->compare_scores($score1, $score2);
}
/**
* @param $item1
* @param $item2
*
* @return int
*/
public function sort_by_score($item1, $item2)
{
$score1 = $this->scorecache[$item1->get_item_type().$item1->get_id()];
$score2 = $this->scorecache[$item2->get_item_type().$item2->get_id()];
return $this->compare_scores($score1, $score2);
}
/**
* @param $item1
* @param $item2
*
* @return int
*/
public function sort_by_mask($item1, $item2)
{
$score1 = $this->scorecache[$item1->get_item_type().$item1->get_id()];
$score2 = $this->scorecache[$item2->get_item_type().$item2->get_id()];
return ScoreDisplay::compare_scores_by_custom_display($score1, $score2);
}
/**
* @param $score1
* @param $score2
*
* @return int
*/
public function compare_scores($score1, $score2)
{
if (!isset($score1)) {
return isset($score2) ? 1 : 0;
} elseif (!isset($score2)) {
return -1;
} elseif (($score1[0] / $score1[1]) == ($score2[0] / $score2[1])) {
return 0;
} else {
return ($score1[0] / $score1[1]) < ($score2[0] / $score2[1]) ? -1 : 1;
}
}
/**
* @param $item
*
* @return string
*/
private function build_course_name($item)
{
return $this->get_course_name_from_code_cached($item->get_course_code());
}
/**
* @param $item
*
* @return string
*/
private function build_category_name($item)
{
$cat = $this->get_category_cached($item->get_category_id());
return $this->get_category_name_to_display($cat);
}
/**
* @param $item
* @param $ignore_score_color
*
* @return string
*/
private function build_average_column($item, $ignore_score_color)
{
if (isset($this->avgcache)) {
$avgscore = $this->avgcache[$item->get_item_type().$item->get_id()];
} else {
$avgscore = $item->calc_score('', 'average');
}
$scoredisplay = ScoreDisplay::instance();
$displaytype = SCORE_AVERAGE;
return $scoredisplay->display_score($avgscore, $displaytype);
}
/**
* @param $item
* @param $ignore_score_color
*
* @return string
*/
private function build_result_column($item, $ignore_score_color)
{
$studscore = $this->scorecache[$item->get_item_type().$item->get_id()];
$scoredisplay = ScoreDisplay::instance();
$displaytype = SCORE_DIV_PERCENT;
if ($ignore_score_color) {
$displaytype |= SCORE_IGNORE_SPLIT;
}
return $scoredisplay->display_score(
$studscore,
$displaytype,
SCORE_ONLY_DEFAULT
);
}
/**
* @param $item
* @param $ignore_score_color
*
* @return string
*/
private function build_mask_column($item, $ignore_score_color)
{
$studscore = $this->scorecache[$item->get_item_type().$item->get_id()];
$scoredisplay = ScoreDisplay::instance();
$displaytype = SCORE_DIV_PERCENT;
if ($ignore_score_color) {
$displaytype |= SCORE_IGNORE_SPLIT;
}
return $scoredisplay->display_score(
$studscore,
$displaytype,
SCORE_ONLY_CUSTOM
);
}
/**
* @param string $courseCode
*
* @return string
*/
private function get_course_name_from_code_cached(string $courseCode): string
{
if (isset($this->coursecodecache) &&
isset($this->coursecodecache[$courseCode])
) {
return $this->coursecodecache[$courseCode];
} else {
$name = CourseManager::getCourseNameFromCode($courseCode);
$this->coursecodecache[$courseCode] = $name;
return $name;
}
}
/**
* @param int $category_id
*/
private function get_category_cached(int $category_id)
{
if (isset($this->categorycache) &&
isset($this->categorycache[$category_id])
) {
return $this->categorycache[$category_id];
} else {
$cat = Category::load($category_id);
if (isset($cat)) {
$this->categorycache[$category_id] = $cat[0];
return $cat[0];
} else {
return null;
}
}
}
/**
* @param $cat
*
* @return string
*/
private function get_category_name_to_display($cat)
{
if (isset($cat)) {
if ('0' == $cat->get_parent_id() || null == $cat->get_parent_id()) {
return '';
} else {
return $cat->get_name();
}
}
return '';
}
}