Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

neue charts #235

Merged
merged 7 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The plugin can be used with all question types in Moodle that allow automatic sc
* Generalized Partial Credit Model

In addition, the plug-in enables the creation, administration and modification of different scales on which measurements are taken. For this purpose, the plug-in creates a new role "Test Administrator", which is authorized to perform these administrative tasks for defined course areas.

The plugin is a central part of the CATQuiz plugin family. It is recommended to install the following plugins as well:
* mod_adaptive - the activity from which a CAT test can be started
* mod_catquizfeedbackgrouping - a text block that gives detailed feedback on a CAT test and optionally assigns users to groups according to this feedback
Expand All @@ -39,6 +39,7 @@ The following parameters can be defined:
'personabilitychart',
'progressindividual',
'progresscomparison',
'abilityprofile',
];
'comparetotestaverage' => [
'comparisontext', // You performed better than ...
Expand Down
50 changes: 0 additions & 50 deletions classes/catcontext.php
Original file line number Diff line number Diff line change
Expand Up @@ -463,56 +463,6 @@ public function getname():string {
return $this->name;
}

/**
* Compare the context to contexts in db. Returns similar context if found, else false.
*
* @return stdClass|boolean
*/
public function compare_to_existing_contexts() {

global $DB;

$record = (object)[
'name' => $this->name,
'description' => $this->description,
'descriptionformat' => $this->descriptionformat,
'starttimestamp' => $this->starttimestamp,
'endtimestamp' => $this->endtimestamp,
'json' => $this->json,
'usermodified' => $this->usermodified,
'timecreated' => $this->timecreated,
'timemodified' => $this->timemodified,
'timecalculated' => $this->timecalculated,
];

// Only if the id is not empty, we add the id key.
if (!empty($this->id)) {
$record->id = $this->id;
}

$explodedname = explode("_", $this->name);

$dbrecords = $DB->get_records_sql(
"SELECT * FROM {local_catquiz_catcontext}"
);

// Check if $record->name contains
// If context was created less than 2 minutes ago, and it's for the same scale, we return it.
$time = intval(time());
foreach ($dbrecords as $dbrecord) {
// TODO change again to 120!
if ($time - intval($dbrecord->timecreated) < 1200
&& str_contains($dbrecord->name, $explodedname[1])
&& str_contains($dbrecord->name, $explodedname[2])
) {
$this->id = $dbrecord->id;
return $dbrecord;
}
}
// If no record was found, return false.
return false;
}

/**
* Returns the time when the items of this context have last been updated.
* @return int
Expand Down
151 changes: 149 additions & 2 deletions classes/teststrategy/feedbackgenerator/personabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@

use cache;
use core\chart_bar;
use core\chart_line;
use core\chart_series;
use local_catquiz\catquiz;
use local_catquiz\catscale;
use local_catquiz\feedback\feedbackclass;
use local_catquiz\output\catscalemanager\questions\cards\questionpreview;
use local_catquiz\teststrategy\feedbackgenerator;
use local_catquiz\teststrategy\feedbacksettings;
use local_catquiz\local\model\model_strategy;
use stdClass;

/**
Expand Down Expand Up @@ -109,6 +111,7 @@ protected function get_studentfeedback(array $data): array {
'standarderrorpersubscales' => $data['standarderrorpersubscales'],
'progressindividual' => $data['progressindividual'],
'progresscomparison' => $data['progresscomparison'],
'abilityprofile' => $data['abilityprofile'],
]
);

Expand Down Expand Up @@ -295,6 +298,7 @@ public function generate_feedback(array $initialcontext, $personabilities, $cach
(array)$initialcontext,
$catscales[$selectedscaleid]);

$abilityprofile = $this->render_abilityprofile_chart((array)$initialcontext, $catscales[$selectedscaleid]);
$standarderrorpersubscales = $quizsettings->catquiz_standarderrorpersubscale ?? "";
return [
'feedback_personabilities' => $data,
Expand All @@ -303,9 +307,152 @@ public function generate_feedback(array $initialcontext, $personabilities, $cach
'cached_contexts' => $cachedcontexts,
'progressindividual' => $abilityprogress['individual'],
'progresscomparison' => $abilityprogress['comparison'],
'abilityprofile' => $abilityprofile,
];
}

/**
* Render chart for histogram of personabilities.
*
* @param array $initialcontext
* @param stdClass $primarycatscale
*
* @return array
*
*/
private function render_abilityprofile_chart(array $initialcontext, $primarycatscale) {
global $OUTPUT, $DB;

$abilitysteps = [];
$abilitystep = 0.25;
$interval = $abilitystep * 2;
$ul = LOCAL_CATQUIZ_PERSONABILITY_UPPER_LIMIT;
$ll = LOCAL_CATQUIZ_PERSONABILITY_LOWER_LIMIT;
for ($i = $ll + $abilitystep; $i <= $ul - $abilitystep; $i += $interval) {
$abilitysteps[] = $i;
}

$catscale = new catscale($primarycatscale->id);

// Prepare data for test information line.
$items = $catscale->get_testitems($initialcontext['contextid'], true);
$models = model_strategy::get_installed_models();
$fisherinfos = [];
foreach ($items as $item) {
$key = $item->model;
$model = $models[$key] ?? $models['raschbirnbaumb'];
foreach ($model::get_parameter_names() as $paramname) {
$params[$paramname] = floatval($item->$paramname);
}
foreach ($abilitysteps as $ability) {
$fisherinformation = $model::fisher_info(
['ability' => $ability],
$params
);
$stringkey = strval($ability);

if (!isset($fisherinfos[$stringkey])) {
$fisherinfos[$stringkey] = $fisherinformation;
} else {
$fisherinfos[$stringkey] += $fisherinformation;
}
}

}

// Prepare data for scorecounter bars.
$abilityrecords = $DB->get_records('local_catquiz_personparams', ['catscaleid' => $primarycatscale->id]);
$abilityseries = [];
foreach ($abilitysteps as $as) {
$counter = 0;
foreach ($abilityrecords as $record) {
$a = floatval($record->ability);
$ability = $this->round_to_customsteps($a, $abilitystep, $interval);
if ($ability != $as) {
continue;
} else {
$counter ++;
}
}
$colorvalue = $this->get_color_for_personability(
$initialcontext['quizsettings'],
$as,
intval($primarycatscale->id)
);
$abilitystring = strval($as);
$abilityseries['counter'][$abilitystring] = $counter;
$abilityseries['colors'][$abilitystring] = $colorvalue;
}
// Scale the values of $fisherinfos before creating chart series.
$scaledtiseries = $this->scalevalues(array_values($fisherinfos), array_values($abilityseries['counter']));

$aserieslabel = get_string('scalescorechartlabel', 'local_catquiz', $catscale->catscale->name);
$aseries = new chart_series($aserieslabel, array_values($abilityseries['counter']));
$aseries->set_colors(array_values($abilityseries['colors']));

$testinfolabel = get_string('testinfolabel', 'local_catquiz');
$tiseries = new chart_series($testinfolabel, $scaledtiseries);
$tiseries->set_type(\core\chart_series::TYPE_LINE);

$chart = new chart_bar();
$chart->add_series($tiseries);
$chart->add_series($aseries);
$chart->set_labels(array_keys($fisherinfos));

$out = $OUTPUT->render($chart);
return [
'chart' => $out,
'charttitle' => get_string('abilityprofile', 'local_catquiz', $primarycatscale->name),
];
}

/**
* Round float to steps as defined.
*
* @param float $number
* @param float $step
* @param float $interval
*
* @return float
*/
private function round_to_customsteps(float $number, float $step, float $interval):float {
$roundedvalue = round($number / $step) * $step;

// Exclude rounding to steps defined in $interval.
if ($roundedvalue - floor($roundedvalue) == $interval) {
$roundedvalue = floor($roundedvalue) + $step;
}

return $roundedvalue;
}

/**
* Scale values of testinfo (sum of fisherinfos) for better display in chart.
*
* @param array $fisherinfos
* @param array $attemptscounter
*
* @return array
*/
private function scalevalues($fisherinfos, $attemptscounter) {
// Find the maximum values in arrays.
$maxattempts = max($attemptscounter);
$maxfisherinfo = max($fisherinfos);

// Avoid division by zero.
if ($maxfisherinfo == 0 || $maxattempts == 0) {
return $fisherinfos;
}

$scalingfactor = $maxattempts / $maxfisherinfo;

// Scale the values in $fisherinfos based on the scaling factor.
foreach ($fisherinfos as &$value) {
$value *= $scalingfactor;
}
return $fisherinfos;
}

/**
* Render chart for personabilities.
*
Expand Down Expand Up @@ -769,7 +916,7 @@ private function render_chart(array $personabilities, array $quizsettings, $prim
$colorvalue = self::get_color_for_personability(
$quizsettings,
floatval($subscaleability),
intval($subscaleid)
intval($primarycatscaleid)
);
$series->set_colors([0 => $colorvalue]);
$chart->add_series($series);
Expand All @@ -794,7 +941,7 @@ private function render_chart(array $personabilities, array $quizsettings, $prim
*
*/
public static function get_color_for_personability(array $quizsettings, float $personability, int $catscaleid): string {
$default = "#000000";
$default = "#878787";
if (!$quizsettings ||
$personability < LOCAL_CATQUIZ_PERSONABILITY_LOWER_LIMIT ||
$personability > LOCAL_CATQUIZ_PERSONABILITY_UPPER_LIMIT) {
Expand Down
2 changes: 2 additions & 0 deletions lang/de/local_catquiz.php
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,8 @@
$string['attemptchartstitle'] = 'Anzahl und Ergebnisse der Testversuche für Skala "{$a}"';
$string['labelforrelativepersonabilitychart'] = 'Relative Fähigkeit';
$string['personabilityrangestring'] = '{$a->rangestart} - {$a->rangeend}';
$string['testinfolabel'] = 'Testinformation';
$string['scalescorechartlabel'] = '{$a}-Score';

// Check display line breaks etc.
$string['choosesubscaleforfeedback_help'] = 'Für die angezeigten Skalen können Sie nun {$a} Feedback-Angaben hinterlegen. Wählen Sie die jeweilige (Sub-)Skala an, um Ihr Feedback einzugeben. Die farbigen Symbole zeigen Ihnen den aktuellen Stand der Bearbeitung an, gemessen an den vor Ihnen hinterlegten Anzahl an Feedback-Optionen:
Expand Down
2 changes: 2 additions & 0 deletions lang/en/local_catquiz.php
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,8 @@
$string['labelforrelativepersonabilitychart'] = 'Relative Ability';
$string['attemptchartstitle'] = 'Number and results of attempts in scale "{$a}"';
$string['personabilityrangestring'] = '{$a->rangestart} - {$a->rangeend}';
$string['testinfolabel'] = 'Test information';
$string['scalescorechartlabel'] = '{$a}-Score';

// Check display line breaks etc.
$string['choosesubscaleforfeedback_help'] = 'You can now store <number of options> feedback informations for the subscales displayed. Select a (sub-)scale to enter your feedback. The colored symbols indicate the current status of processing, measured by the number of feedback options you entered:
Expand Down
8 changes: 7 additions & 1 deletion templates/feedback/personabilities.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,10 @@
{{charttitle}}
</h5>
{{{chart}}}
{{/progresscomparison}}
{{/progresscomparison}}
{{#abilityprofile}}
<h5>
{{charttitle}}
</h5>
{{{chart}}}
{{/abilityprofile}}
Loading