Polished up badge view for users

This commit is contained in:
PMKuipers 2023-11-01 23:47:54 +01:00
parent 3fcf0095ea
commit dd7fa5f620
9 changed files with 191 additions and 86 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -96,6 +96,8 @@ export default {
dateexpire: "dateexpire",
badgeinfo: "badgeinfo",
badgeissuedstats: "badgeissuedstats",
completion_incomplete: "completion_incomplete_badge",
completion_completed: "completion_completed_badge",
},
course: {
completion_incomplete: "completion_incomplete",
@ -2261,7 +2263,7 @@ export default {
},
data() {
return {
txt: strings
text: strings.badge,
};
},
computed: {
@ -2293,17 +2295,60 @@ export default {
} else {
return ""; // no path
}
},
arcpath_progress(){
if(this.value.badge.completion){
const fraction = this.value.badge.completion.progress/this.value.badge.completion.count;
return this.arcpath(0,fraction);
} else {
return ""; // no path
}
},
badgeinprogress(){
return ( this.value.badge.issued
|| this.teachermode
|| (this.value.badge.completion
&& this.value.badge.completion.progress >= this.value.badge.completion.count)
);
}
},
methods: {
arcpath(start, end) {
const r = 44;
const t1 = start * 2*π;
const Δ = end * 2*π;
const Δ = (end * 2*π-0.01);
return svgarcpath([50,50],[r,r],[t1,Δ], 1.5*π);
},
addTargetBlank(html) {
const m = /^([^<]*\< *a +)(.*)/.exec(html);
if(m){
return `${m[1]} target="_blank" ${m[2]}`;
} else {
return html;
}
},
completion_icon_rq(complete) {
if (complete) {
return "check-square-o";
} else {
return "square-o";
}
},
completion_icon(complete) {
if (complete) {
return "check-circle";
} else {
return "times-circle";
}
},
status(complete) {
if (complete) {
return "complete";
} else {
return "incomplete";
}
}
},
template: `
<div :class="'r-item-badge r-item-filter r-completion-'+completion" v-b-tooltip.hover :title="value.badge.name">
@ -2318,12 +2363,18 @@ export default {
</template>
<circle v-else-if="value.badge.issued" cx="50" cy="50" r="46"
style="stroke: currentcolor; stroke-width: 4; fill: currentcolor; fill-opacity: 0.5;"/>
<template v-else-if="value.badge.completion">
<circle cx="50" cy="50" r="44"
style="stroke: #ccc; stroke-width: 8; fill: #ddd; fill-opacity: 0.8;"/>
<path :d="arcpath_progress"
:style="'stroke-width: 8; stroke: var(--info); fill: none;'"/>
</template>
<circle v-else cx="50" cy="50" r="46"
stroke-dasharray="6 9"
style="stroke: #999; stroke-width: 6; fill: #ddd; fill-opacity: 0.8;"/>
<image class="badge-image" clip-path="circle() fill-box"
:href="value.badge.imageurl" x="12" y="12" width="76" height="76"
:style="(value.badge.issued||teachermode)?'':'opacity: 0.4;'" />
:style="(badgeinprogress)?'':'opacity: 0.4;'" />
</svg></a>
<b-modal
@ -2343,9 +2394,9 @@ export default {
</div>
<div class="r-course-detail-header-right" v-if="!teachermode">
<div class="r-completion-detail-header">
{{ txt.completion['completion_'+completion] }}
{{ text['completion_'+completion] }}
<i v-b-popover.hover :class="'fa fa-'+issued_icon+' r-completion-'+completion"
:title="txt.completion['completion_'+completion]"></i>
:title="text['completion_'+completion]"></i>
</div>
</div>
</template>
@ -2356,28 +2407,58 @@ export default {
<p>{{value.badge.description}}</p>
<ul v-if="value.badge.issued" class="list-unstyled pt-1 mb-1 border-grey border-top">
<li><strong><i class="fa fa-calendar-check-o r-completion-complete-pass"></i>
{{txt.badge.dateissued}}:</strong> {{ value.badge.dateissued }}</li>
{{text.dateissued}}:</strong> {{ value.badge.dateissued }}</li>
<li v-if='value.badge.dateexpired'
><strong><i class="fa fa-calendar-times-o r-completion-complete"></i>
{{txt.badge.dateexpired}}:</strong> {{ value.badge.dateexpired }}</li>
{{text.dateexpired}}:</strong> {{ value.badge.dateexpired }}</li>
<li><strong><i class="fa fa-share-alt r-completion-complete-pass"></i>
<a :href="value.badge.issuedlink">{{txt.badge.share_badge}}</a></strong> </li>
<a :href="value.badge.issuedlink">{{text.share_badge}}</a></strong> </li>
</ul>
<table v-if='value.badge.completion && !value.badge.issued' class="r-item-course-grade-details mb-2">
<tr v-if="value.badge.completion.types.length > 1">
<th colspan="2"><span v-html="value.badge.completion.title"></span></th>
</tr>
<template v-for='cgroup in value.badge.completion.types' >
<tr>
<td colspan="2" v-if="value.badge.completion.types.length > 1"
><span v-html="cgroup.title"></span></td>
<th colspan="2" v-else><span v-html="cgroup.title"></span></th>
</tr>
<template v-for='ci in cgroup.criteria'>
<tr>
<td class="pl-3"><span v-if='guestmode'><span v-html="ci.title"></span></span>
<a target='_blank' v-else-if='ci.link' :href='ci.link'
><span v-html="ci.title"></span></a>
<span v-else><span v-html="ci.title"></span></span>
<td><i :class="'fa fa-'+completion_icon(ci.completed)+' r-completion-'+status(ci.completed)"
:title="text['completion_'+status(ci.completed)]"></i>
</td>
</tr>
<template v-if="ci.requirements.length > 1">
<tr v-for="rq in ci.requirements">
<td class="pl-4" colspan="2"
><i :class="'fa fa-'+completion_icon_rq(rq.completed)+' r-completion-incomplete'"
:title="text['completion_'+status(rq.completed)]"></i>
<span class="t-badge-criteria-requirement"><span v-html="rq.title"></span></span></td>
</tr>
</template>
</template>
</template>
</table>
<ul class="list-unstyled w-100 border-grey border-top border-bottom pt-1 pb-1 mb-1"
v-if="value.badge.criteria"><li v-for="crit in value.badge.criteria"
><span v-html='crit'></span></li></ul>
<p v-if="(!guestmode)"><strong><i class="fa fa-link"></i>
<a :href="value.badge.infolink" target="_blank"
>{{ txt.badge.badgeinfo }}</a></strong></p>
>{{ text.badgeinfo }}</a></strong></p>
<p v-if="teachermode && !guestmode"
>{{txt.badge.badgeissuedstats}}:<br>
>{{text.badgeissuedstats}}:<br>
<r-completion-bar v-model="issuestats" :width="150" :height="15"></r-completion-bar>
</p>
</b-col></b-row>
</b-container>
</b-modal>
</div>
`,
});
},

View File

@ -27,6 +27,7 @@ require_once($CFG->libdir.'/externallib.php');
use award_criteria;
use \core_badges\badge;
use moodle_url;
/**
* Handle badge information in the same style as the other classes
@ -176,6 +177,7 @@ class badgeinfo {
"dateexpire" => new \external_value(PARAM_TEXT, 'date the badge will expire', VALUE_OPTIONAL),
"uniquehash" => new \external_value(PARAM_TEXT, 'badge issue hash', VALUE_OPTIONAL),
"issuedlink" => new \external_value(PARAM_TEXT, 'badge issue information link', VALUE_OPTIONAL),
"active" => new \external_value(PARAM_BOOL, 'badge is available'),
], "Badge info", $value);
}
@ -208,6 +210,7 @@ class badgeinfo {
'completion' => $this->badge_completion_data($userid),
'issued' => $issued,
'infolink' => (new \moodle_url('/badges/overview.php', ['id' => $this->badge->id]))->out(false),
"active" => $this->badge->is_active(),
];
@ -235,13 +238,14 @@ class badgeinfo {
'criteria' => new \external_multiple_structure(new \external_single_structure([
"title" => new \external_value(PARAM_RAW, 'criterion title'),
"description" => new \external_value(PARAM_RAW, 'criterion description'),
"link"=> new \external_value(PARAM_RAW, 'link to criterion resource',VALUE_OPTIONAL),
"requirements" => new \external_multiple_structure(new \external_single_structure([
"title" => new \external_value(PARAM_RAW, 'requirment title'),
"completed" => new \external_value(PARAM_BOOL, 'criterion is completed or not'),
"completed" => new \external_value(PARAM_BOOL, 'criterion is completed or not', VALUE_OPTIONAL),
]), "criterion specific requirements", VALUE_OPTIONAL),
"completed" => new \external_value(PARAM_BOOL, 'criterion is completed or not'),
]),'specific criteria'),
"type" => new \external_value(PARAM_TEXT, 'any|all'),
"title" => new \external_value(PARAM_RAW, 'type title'),
"aggregation" => new \external_value(PARAM_TEXT, 'any|all'),
"count" => new \external_value(PARAM_INT, 'effective number of critera for type progress'),
"progress" => new \external_value(PARAM_INT, 'effective number of completed criteria for type progress'),
@ -251,6 +255,7 @@ class badgeinfo {
"count" => new \external_value(PARAM_INT, 'total number of critera for progress'),
"progress" => new \external_value(PARAM_INT, 'number of completed criteria for progress'),
"fraction" => new \external_value(PARAM_FLOAT, 'fraction of completed criteria as float'),
"title" => new \external_value(PARAM_RAW, 'completion title'),
],'badge completion information', $value);
}
@ -262,15 +267,12 @@ class badgeinfo {
$badgeagg = $this->badge->get_aggregation_method();
$types = [];
$f = fopen("/tmp/badgedebug","a");
fputs($f,date("Y-m-d H:M:S")." Badge criteria\n");
fputs($f,print_r($this->badge->criteria,true)."\n\n");
fclose($f);
foreach ($this->badge->criteria as $type => $bc) {
if ($type != BADGE_CRITERIA_TYPE_OVERALL) {
$typeagg = $this->badge->get_aggregation_method($type);
$typecrit = $this->get_award_subcriteria($bc,$userid);
$typecount = count($typecrit);
$typeprogress = 0;
foreach ($typecrit as $subcrit) {
@ -290,8 +292,8 @@ class badgeinfo {
if($badgeagg == BADGE_CRITERIA_AGGREGATION_ANY) {
/* If ANY completion overall, count only the criteria type with the highest completion percentage -.
Overwrite data if current type is more complete */
$typefraction = $typeprogress / $typecount;
if ($typefraction > $fraction) {
$typefraction = ($typecount > 0)?($typeprogress / $typecount):0;
if ($typefraction > $fraction || ($fraction == 0 && $typecount > $count)) {
$fraction = $typefraction;
$count = $typecount;
$progress = $typeprogress;
@ -302,9 +304,10 @@ class badgeinfo {
$progress += $typeprogress;
}
$aggrgation_handle = ($typeagg == BADGE_CRITERIA_AGGREGATION_ALL)?"all":"any";
$typeinfo = [
'type' => $type,
'aggregation' => ($typeagg == BADGE_CRITERIA_AGGREGATION_ALL)?"all":"any",
'title' => ucfirst(get_string("criteria_descr_$type","badges", get_string($aggrgation_handle,"core"))),
'aggregation' => $aggrgation_handle,
'criteria' => $typecrit,
'count' => $typecount,
'progress' => $typeprogress,
@ -312,10 +315,13 @@ class badgeinfo {
];
$types[] = $typeinfo;
}
}
$aggrgation_handle = ($badgeagg == BADGE_CRITERIA_AGGREGATION_ALL)?"all":"any";
return [
"types" => $types,
"aggregation" => ($typeagg == BADGE_CRITERIA_AGGREGATION_ALL)?"all":"any",
"title" => ucfirst(get_string("criteria_descr_0","badges", mb_strtolower(get_string($aggrgation_handle,"core")))),
"aggregation" => $aggrgation_handle,
"count" => $count,
"progress" => $progress,
"fraction" => $fraction,
@ -388,7 +394,7 @@ class badgeinfo {
*
*/
protected function get_award_subcriteria(award_criteria $crit, $userid = null) : array {
global $DB;
global $DB, $CFG;
$list = [];
if ($crit->criteriatype == BADGE_CRITERIA_TYPE_ACTIVITY) {
@ -444,7 +450,7 @@ class badgeinfo {
if (isset($p["bydate"])) {
$date = $data->timemodified;
$check_date = ($date <= $p['bydate']);
$subcrit["requirements"]["completion"]["completed"] = $check_date;
$subcrit["requirements"]["bydate"]["completed"] = $check_date;
}
$subcrit["completed"] = $modcompleted && $check_date;
@ -471,7 +477,7 @@ class badgeinfo {
if(isset($userid)) {
$crit = $DB->get_record('badge_manual_award', array('issuerrole' => $p['role'], 'recipientid' => $userid, 'badgeid' => $crit->badgeid));
$subcrit["completed"] = $crit;
$subcrit["completed"] = $crit !== false;
}
$list[] = $subcrit;
}
@ -500,17 +506,18 @@ class badgeinfo {
$subcrit = [
"title" => $course->fullname,
"link" => (new moodle_url($CFG->wwwroot."/course/view.php",["id" => $course->id]))->out(),
"description" => $description,
"requirements" => [
'completion' => [
'title' => get_string('coursecompleted','core'),
'title' => get_string('coursecompleted','completion'),
]
]
];
if (isset($p["grade"])) {
$subcrit["requirements"]["grade"] = [
'title' => get_string('criteria_descr_bydate','badges',$p["grade"]),
'title' => get_string('criteria_descr_grade','badges',$p["grade"]),
];
}
@ -521,21 +528,24 @@ class badgeinfo {
}
if(isset($userid)) {
$info = new \completion_info($course);
$coursecompleted = $info->is_course_complete($userid);
$subcrit["requirements"]["completion"]["completed"] = $coursecompleted;
$coursecompletion = new \completion_completion(["userid" => $userid, "course" => $course->id]);
$coursecompleted = $coursecompletion->is_complete();
$f = fopen("/tmp/debug","a+");
fputs($f,$course->fullname." ".($coursecompleted?"(COMPLETED)":"(NOT completed)")."\n");
fputs($f,print_r($coursecompletion,true));
fclose($f);
$subcrit["requirements"]["completion"]["completed"] = (bool) $coursecompleted;
$check_grade = true;
if (isset($p["grade"])) {
$grade = \grade_get_course_grade($userid,$course->id);
$check_grade = ($grade->grade >= $p['grade']);
$subcrit["requirements"]["completion"]["completed"] = $check_grade;
$subcrit["requirements"]["grade"]["completed"] = (bool) $check_grade;
}
$check_date = true;
if (isset($p["bydate"])) {
$completion = new \completion_completion(["userid" => $userid, "course" => $course->id]);
$check_date = ($completion->timecompleted <= $p["bydate"]);
$subcrit["requirements"]["completion"]["completed"] = $check_date;
$check_date = ((bool) $coursecompletion->timecompleted) && ($coursecompletion->timecompleted <= $p["bydate"]);
$subcrit["requirements"]["bydate"]["completed"] = (bool) $check_date;
}
$subcrit["completed"] = $coursecompleted && $check_grade && $check_date;
@ -590,7 +600,7 @@ class badgeinfo {
$sql = "SELECT 1 FROM {user} u " . $join . " WHERE u.id = :userid $where";
$completed = $DB->record_exists_sql($sql, $sqlparams);
$subcrit["completed"] = $completed;
$subcrit["completed"] = (bool) $completed;
}
$list[] = $subcrit;
}
@ -601,13 +611,14 @@ class badgeinfo {
$title = get_string('error:nosuchbadge', 'badges');
$description = get_string('error:nosuchbadge', 'badges');
} else {
$title = \html_writer::tag('b', '"' . $badgename . '"');;
$title = $badgename;
$description = \html_writer::tag('b', '"' . $badgename . '"');;
}
$subcrit = [
"title" => $title,
"description" => $description,
"link" => (new \moodle_url($CFG->wwwroot."/badges/overview.php",["id" => $p["badge"]]))->out(),
"requirements" => []
];
@ -616,10 +627,11 @@ class badgeinfo {
// See if the user has earned this badge.
if($badge) {
$awarded = $DB->get_record('badge_issued', array('badgeid' => $p['badge'], 'userid' => $userid));
$awarded = isset($awarded);
} else {
$awarded = false;
}
$subcrit["completed"] = $awarded;
$subcrit["completed"] = (bool)$awarded;
}
$list[] = $subcrit;
}
@ -642,11 +654,7 @@ class badgeinfo {
if(isset($userid)) {
$cohort = $DB->get_record('cohort', array('id' => $p['cohort']));
if(\cohort_is_member($cohort->id, $userid)) {
$ismember = true;
} else {
$ismember = false;
}
$ismember = (bool) \cohort_is_member($cohort->id, $userid);
$subcrit["completed"] = $ismember;
}
$list[] = $subcrit;
@ -706,7 +714,7 @@ class badgeinfo {
$proficiency = $uc->get('proficiency');
}
$subcrit["completed"] = $proficiency;
$subcrit["completed"] = (bool) $proficiency;
}
$list[] = $subcrit;
}

View File

@ -707,6 +707,10 @@
left: 50%;
transform: translate(-50%, -50%);
}
.path-local-treestudyplan .t-badge-criteria-requirement,
.features-treestudyplan .t-badge-criteria-requirement {
color: var(--gray);
}
.path-local-treestudyplan .r-report-tabs .list-group-item-action,
.features-treestudyplan .r-report-tabs .list-group-item-action {
width: inherit;

View File

@ -180,6 +180,8 @@ $string["completion_completed"] = "Completed";
$string["completion_good"] = "Good";
$string["completion_excellent"] = "Excellent";
$string["completion_passed"] = "Passed";
$string["completion_incomplete_badge"] = "Not issued";
$string["completion_completed_badge"] = "Issued";
$string["incomplete"] = 'Not started';
$string["completed"] = 'Completed';

View File

@ -177,6 +177,8 @@ $string["completion_completed"] = "Voltooid";
$string["completion_good"] = "Goed";
$string["completion_excellent"] = "Uitstekend";
$string["completion_passed"] = "Behaald";
$string["completion_incomplete_badge"] = "Niet uitgegeven";
$string["completion_completed_badge"] = "Uitgegeven";
$string["incomplete"] = 'Niet gestart';
$string["completed"] = 'Voltooid';

View File

@ -603,6 +603,10 @@
transform: translate(-50%, -50%);
}
.t-badge-criteria-requirement {
color: var(--gray);
}
.r-report-tabs .list-group-item-action {
width: inherit;
}

View File

@ -707,6 +707,10 @@
left: 50%;
transform: translate(-50%, -50%);
}
.path-local-treestudyplan .t-badge-criteria-requirement,
.features-treestudyplan .t-badge-criteria-requirement {
color: var(--gray);
}
.path-local-treestudyplan .r-report-tabs .list-group-item-action,
.features-treestudyplan .r-report-tabs .list-group-item-action {
width: inherit;