Bugfixed in completion reporting for users and teachers

This commit is contained in:
PMKuipers 2023-09-01 23:13:50 +02:00
parent 3614fa852f
commit 39e1e14dcd
10 changed files with 168 additions and 75 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

@ -1151,6 +1151,23 @@ export default {
} }
return false; return false;
}, },
requirementHTML(requirements) {
const rqs = requirements.split(/, */);
let html = "";
for(const ix in rqs) {
const rq = rqs[ix];
html += `${rq}<br>`;
}
return html;
},
addTargetBlank(html) {
const m = /^([^<]*\< *a +)(.*)/.exec(html);
if(m){
return `${m[1]} target="_blank" ${m[2]}`;
} else {
return html;
}
}
}, },
template: ` template: `
<table class="r-item-course-grade-details"> <table class="r-item-course-grade-details">
@ -1184,13 +1201,21 @@ export default {
</tr> </tr>
<tr v-for='ci in cgroup.items'> <tr v-for='ci in cgroup.items'>
<td><span v-if='guestmode'>{{ci.title}}</span> <td><span v-if='guestmode'>{{ci.title}}</span>
<span v-else v-html='ci.details.criteria'></span> <span v-else v-html='addTargetBlank(ci.details.criteria)'></span>
<a href="#" v-b-tooltip.click="{ customClass: 'r-tooltip ' + ci.status}" <a href="#" v-b-tooltip.click.hover.right.html="{ customClass: 'r-tooltip ' + ci.status}"
:title="ci.details.requirement" :title="requirementHTML(ci.details.requirement)"
:class="'s-required ' + ci.status"><i v-if="ci.details.requirement" class="text-primary"><i v-if="ci.details.requirement"
class='fa fa-question-circle' class='fa fa-question-circle'
></i></a> ></i></a>
<td><span :class="' r-completion-'+ci.status">{{ci.grade}}</span></td> <td
><span :class="' r-completion-'+ci.status">{{ci.grade}}</span>
<span v-if="ci.warning"
><i class="text-primary fa fa-exclamation-triangle"
v-b-tooltip.hover.right.click="{ customClass: 'r-tooltip info' }"
:title="ci.warning"
></i
></span
></td>
<td><i :class="'fa fa-'+completion_icon(ci.status)+' r-completion-'+ci.status" <td><i :class="'fa fa-'+completion_icon(ci.status)+' r-completion-'+ci.status"
:title="text['completion_'+ci.status]"></i> :title="text['completion_'+ci.status]"></i>
<i v-if='ci.pending' :title="text['completion_pending']" <i v-if='ci.pending' :title="text['completion_pending']"
@ -1305,7 +1330,7 @@ export default {
status.completed += itm.progress.completed; status.completed += itm.progress.completed;
status.completed_pass += itm.progress.completed_pass; status.completed_pass += itm.progress.completed_pass;
status.completed_fail += itm.progress.completed_fail; status.completed_fail += itm.progress.completed_fail;
status.ungraded += itm.progress.completed; status.ungraded += itm.progress.ungraded;
} }
} }
} }

View file

@ -172,15 +172,14 @@ class completionscanner {
$completedpass = 0; $completedpass = 0;
$completedfail = 0; $completedfail = 0;
foreach ($students as $userid) { foreach ($students as $userid) {
if ($this->pending($userid)) {
// First check if the completion needs grading.
$ungraded++;
} else {
$completion = $this->completioninfo->get_user_completion($userid, $this->crit); $completion = $this->completioninfo->get_user_completion($userid, $this->crit);
if ($this->crit->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) { if ($this->crit->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
// If it's an activity completion, add all the relevant activities as sub-items. // If it's an activity completion, add all the relevant activities as sub-items.
$completionstatus = $this->completioninfo->get_grade_completion($this->cm, $userid); // Retrieve data for this object.
$data = $this->completioninfo->get_data($this->cm, false, $userid);
// If it's an activity completion, add all the relevant activities as sub-items.
$completionstatus = $data->completionstate;
if ($completionstatus == COMPLETION_COMPLETE_PASS) { if ($completionstatus == COMPLETION_COMPLETE_PASS) {
$completedpass++; $completedpass++;
@ -188,6 +187,9 @@ class completionscanner {
$completedfail++; $completedfail++;
} else if ($completionstatus == COMPLETION_COMPLETE) { } else if ($completionstatus == COMPLETION_COMPLETE) {
$completed++; $completed++;
} else if ($this->pending($userid)) {
// Check if the completion needs grading.
$ungraded++;
} }
} else { } else {
if ($completion->is_complete()) { if ($completion->is_complete()) {
@ -195,7 +197,6 @@ class completionscanner {
} }
} }
} }
}
return [ return [
'ungraded' => $ungraded, 'ungraded' => $ungraded,

View file

@ -190,6 +190,7 @@ class corecompletioninfo {
'optional pending state, for submitted but not yet reviewed activities', VALUE_OPTIONAL), 'optional pending state, for submitted but not yet reviewed activities', VALUE_OPTIONAL),
"grade" => new \external_value(PARAM_TEXT, 'optional grade result for this subitem', VALUE_OPTIONAL), "grade" => new \external_value(PARAM_TEXT, 'optional grade result for this subitem', VALUE_OPTIONAL),
"feedback" => new \external_value(PARAM_RAW, 'optional feedback for this subitem ', VALUE_OPTIONAL), "feedback" => new \external_value(PARAM_RAW, 'optional feedback for this subitem ', VALUE_OPTIONAL),
"warning" => new \external_value(PARAM_TEXT, 'optional warning text', VALUE_OPTIONAL),
], 'completion type', $value); ], 'completion type', $value);
} }
@ -465,10 +466,6 @@ class corecompletioninfo {
foreach ($completions as $completion) { foreach ($completions as $completion) {
$criteria = $completion->get_criteria(); $criteria = $completion->get_criteria();
if ($completion->is_complete()) {
$progress += 1; // Add a point to the progress counter.
}
$iinfo = [ $iinfo = [
"id" => $criteria->id, "id" => $criteria->id,
"title" => $criteria->get_title_detailed(), "title" => $criteria->get_title_detailed(),
@ -480,11 +477,42 @@ class corecompletioninfo {
if ($type == COMPLETION_CRITERIA_TYPE_ACTIVITY) { if ($type == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
$cm = $this->modinfo->get_cm($criteria->moduleinstance); $cm = $this->modinfo->get_cm($criteria->moduleinstance);
// Retrieve data for this object.
$data = $this->completion->get_data($cm, false, $userid);
// If it's an activity completion, add all the relevant activities as sub-items. // If it's an activity completion, add all the relevant activities as sub-items.
$completionstatus = $this->completion->get_grade_completion($cm, $userid); $completionstatus = $data->completionstate;
$iinfo['status'] = self::completion_handle($completionstatus); $gradecompletion = $this->completion->get_grade_completion($cm, $userid);
/* To comply with the moodle completion report, only count COMPLETED_PASS as completed if
the completion is marked as complete by the system. Occasinally those don't match
and we want to show similar behaviour. This happens when completion data is reset
in a module
*/
if(!$completion->is_complete() && in_array($gradecompletion, [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS])) {
/* If a passing grade was provided, but the activity was not completed,
* most likely the completion data was erased.
*/
/*$completionstatus = COMPLETION_INCOMPLETE; */
if ( !is_null($cm->completiongradeitemnumber) || ($cm->completionpassgrade) ) {
// Show a warning if this activity has grade completions to help make sense of the completion.
$iinfo["warning"] = get_string("warning_incomplete_pass","local_treestudyplan");
} else {
// Show a warning if this activity has no grade requirment for completion.
$iinfo["warning"] = get_string("warning_incomplete_nograderq","local_treestudyplan");
}
}
$iinfo['status'] = self::completion_handle($data->completionstate);
// Re-evaluate the completed value, to make sure COMPLETE_FAIL doesn't creep in as completed. // Re-evaluate the completed value, to make sure COMPLETE_FAIL doesn't creep in as completed.
$iinfo['completed'] = in_array($completionstatus, [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS]); if (($data->completionstate == COMPLETION_INCOMPLETE) || ($data->completionstate == COMPLETION_COMPLETE_FAIL)) {
$iinfo['completed'] = false;
} else {
$iinfo['completed'] = true;
$progress += 1; // Add a point to the progress counter.
}
// Determine the grade (retrieve from grade item, not from completion). // Determine the grade (retrieve from grade item, not from completion).
$grade = $this->get_grade($cm, $userid); $grade = $this->get_grade($cm, $userid);
$iinfo['grade'] = $grade->grade; $iinfo['grade'] = $grade->grade;
@ -510,6 +538,15 @@ class corecompletioninfo {
$cinfo["status"] = "progress"; $cinfo["status"] = "progress";
} }
} }
if ($completion->is_complete()) {
$progress += 1; // Add a point to the progress counter.
}
}
else {
if ($completion->is_complete()) {
$progress += 1; // Add a point to the progress counter.
}
} }
// Finally add the item to the items list. // Finally add the item to the items list.
$cinfo["items"][] = $iinfo; $cinfo["items"][] = $iinfo;

View file

@ -1232,13 +1232,29 @@
.features-treestudyplan .s-required.neutral { .features-treestudyplan .s-required.neutral {
color: #aaa; color: #aaa;
} }
.path-local-treestudyplan .r-tooltip.warning .tooltip-inner,
.features-treestudyplan .r-tooltip.warning .tooltip-inner {
background-color: color-mix(in srgb, var(--warning) 60%, #000);
}
.path-local-treestudyplan .r-tooltip.warning .arrow::before,
.features-treestudyplan .r-tooltip.warning .arrow::before {
border-right-color: color-mix(in srgb, var(--warning) 60%, #000);
}
.path-local-treestudyplan .r-tooltip.info .tooltip-inner,
.features-treestudyplan .r-tooltip.info .tooltip-inner {
background-color: color-mix(in srgb, var(--info) 60%, #000);
}
.path-local-treestudyplan .r-tooltip.info .arrow::before,
.features-treestudyplan .r-tooltip.info .arrow::before {
border-right-color: color-mix(in srgb, var(--info) 60%, #000);
}
.path-local-treestudyplan .r-tooltip.incomplete .tooltip-inner, .path-local-treestudyplan .r-tooltip.incomplete .tooltip-inner,
.path-local-treestudyplan .r-tooltip.complete-fail .tooltip-inner, .path-local-treestudyplan .r-tooltip.complete-fail .tooltip-inner,
.path-local-treestudyplan .r-tooltip.completed-fail .tooltip-inner, .path-local-treestudyplan .r-tooltip.completed-fail .tooltip-inner,
.features-treestudyplan .r-tooltip.incomplete .tooltip-inner, .features-treestudyplan .r-tooltip.incomplete .tooltip-inner,
.features-treestudyplan .r-tooltip.complete-fail .tooltip-inner, .features-treestudyplan .r-tooltip.complete-fail .tooltip-inner,
.features-treestudyplan .r-tooltip.completed-fail .tooltip-inner { .features-treestudyplan .r-tooltip.completed-fail .tooltip-inner {
background-color: var(--danger); background-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.path-local-treestudyplan .r-tooltip.incomplete .arrow::before, .path-local-treestudyplan .r-tooltip.incomplete .arrow::before,
.path-local-treestudyplan .r-tooltip.complete-fail .arrow::before, .path-local-treestudyplan .r-tooltip.complete-fail .arrow::before,
@ -1246,19 +1262,19 @@
.features-treestudyplan .r-tooltip.incomplete .arrow::before, .features-treestudyplan .r-tooltip.incomplete .arrow::before,
.features-treestudyplan .r-tooltip.complete-fail .arrow::before, .features-treestudyplan .r-tooltip.complete-fail .arrow::before,
.features-treestudyplan .r-tooltip.completed-fail .arrow::before { .features-treestudyplan .r-tooltip.completed-fail .arrow::before {
border-top-color: var(--danger); border-right-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.path-local-treestudyplan .r-tooltip.complete .tooltip-inner, .path-local-treestudyplan .r-tooltip.complete .tooltip-inner,
.path-local-treestudyplan .r-tooltip.completed .tooltip-inner, .path-local-treestudyplan .r-tooltip.completed .tooltip-inner,
.features-treestudyplan .r-tooltip.complete .tooltip-inner, .features-treestudyplan .r-tooltip.complete .tooltip-inner,
.features-treestudyplan .r-tooltip.completed .tooltip-inner { .features-treestudyplan .r-tooltip.completed .tooltip-inner {
background-color: var(--info); background-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.path-local-treestudyplan .r-tooltip.complete .arrow::before, .path-local-treestudyplan .r-tooltip.complete .arrow::before,
.path-local-treestudyplan .r-tooltip.completed .arrow::before, .path-local-treestudyplan .r-tooltip.completed .arrow::before,
.features-treestudyplan .r-tooltip.complete .arrow::before, .features-treestudyplan .r-tooltip.complete .arrow::before,
.features-treestudyplan .r-tooltip.completed .arrow::before { .features-treestudyplan .r-tooltip.completed .arrow::before {
border-top-color: var(--info); border-right-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.path-local-treestudyplan .r-tooltip.complete-pass .tooltip-inner, .path-local-treestudyplan .r-tooltip.complete-pass .tooltip-inner,
.path-local-treestudyplan .r-tooltip.completed-pass .tooltip-inner, .path-local-treestudyplan .r-tooltip.completed-pass .tooltip-inner,
@ -1270,15 +1286,7 @@
.path-local-treestudyplan .r-tooltip.completed-pass .arrow::before, .path-local-treestudyplan .r-tooltip.completed-pass .arrow::before,
.features-treestudyplan .r-tooltip.complete-pass .arrow::before, .features-treestudyplan .r-tooltip.complete-pass .arrow::before,
.features-treestudyplan .r-tooltip.completed-pass .arrow::before { .features-treestudyplan .r-tooltip.completed-pass .arrow::before {
border-top-color: var(--success); border-right-color: var(--success);
}
.path-local-treestudyplan .r-tooltip.incomplete .tooltip-inner,
.features-treestudyplan .r-tooltip.incomplete .tooltip-inner {
background-color: var(--danger);
}
.path-local-treestudyplan .r-tooltip.incomplete .arrow::before,
.features-treestudyplan .r-tooltip.incomplete .arrow::before {
border-top-color: var(--danger);
} }
.path-local-treestudyplan .m-buttonbar, .path-local-treestudyplan .m-buttonbar,
.features-treestudyplan .m-buttonbar { .features-treestudyplan .m-buttonbar {

View file

@ -336,3 +336,6 @@ $string["completion_not_enabled"] = 'Completion is not enabled in this course';
$string["student_from_plan_enrolled"] = 'student in this study plan is enrolled in the course'; $string["student_from_plan_enrolled"] = 'student in this study plan is enrolled in the course';
$string["students_from_plan_enrolled"] = 'students in this study plan are enrolled in the course'; $string["students_from_plan_enrolled"] = 'students in this study plan are enrolled in the course';
$string["not_enrolled"] = "Not enrolled"; $string["not_enrolled"] = "Not enrolled";
$string["warning_incomplete_pass"] = 'Completion data was reset for this activity, causing your passing grade not to be registered. Ask your teacher to re-grade this grade to remedy this.';
$string["warning_incomplete_nograderq"] = 'Because the grade is not marked as a requirement, your passing grade is not registering completion.';

View file

@ -337,3 +337,6 @@ $string["completion_not_enabled"] = 'Cursusvoltooiing is niet ingeschakeld';
$string["student_from_plan_enrolled"] = 'student in dit studieplan is in deze cursus ingeschreven'; $string["student_from_plan_enrolled"] = 'student in dit studieplan is in deze cursus ingeschreven';
$string["students_from_plan_enrolled"] = 'studenten in dit studieplan zijn in deze cursus ingeschreven'; $string["students_from_plan_enrolled"] = 'studenten in dit studieplan zijn in deze cursus ingeschreven';
$string["not_enrolled"] = "Niet ingeschreven"; $string["not_enrolled"] = "Niet ingeschreven";
$string["warning_incomplete_pass"] = 'Door een storing wordt je voldoende resultaat niet als voltooid meegenomen. Vraag je docent om opnieuw je beoordeling vast te stellen. ';
$string["warning_incomplete_nograderq"] = 'Omdat het behalen van een cijfer niet als voorwaarde is aangegeven, telt het behalen van een voldoende resultaat niet mee voor voitooiing';

View file

@ -1064,24 +1064,39 @@
color: #aaa; color: #aaa;
} }
.r-tooltip.warning .tooltip-inner
{
background-color: color-mix(in srgb, var(--warning) 60%, #000);
}
.r-tooltip.warning .arrow::before {
border-right-color: color-mix(in srgb, var(--warning) 60%, #000);
}
.r-tooltip.info .tooltip-inner
{
background-color: color-mix(in srgb, var(--info) 60%, #000);
}
.r-tooltip.info .arrow::before {
border-right-color: color-mix(in srgb, var(--info) 60%, #000);
}
.r-tooltip.incomplete .tooltip-inner, .r-tooltip.incomplete .tooltip-inner,
.r-tooltip.complete-fail .tooltip-inner, .r-tooltip.complete-fail .tooltip-inner,
.r-tooltip.completed-fail .tooltip-inner { .r-tooltip.completed-fail .tooltip-inner {
background-color: var(--danger); background-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.r-tooltip.incomplete .arrow::before, .r-tooltip.incomplete .arrow::before,
.r-tooltip.complete-fail .arrow::before, .r-tooltip.complete-fail .arrow::before,
.r-tooltip.completed-fail .arrow::before { .r-tooltip.completed-fail .arrow::before {
border-top-color: var(--danger); border-right-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.r-tooltip.complete .tooltip-inner, .r-tooltip.complete .tooltip-inner,
.r-tooltip.completed .tooltip-inner { .r-tooltip.completed .tooltip-inner {
background-color: var(--info); background-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.r-tooltip.complete .arrow::before, .r-tooltip.complete .arrow::before,
.r-tooltip.completed .arrow::before { .r-tooltip.completed .arrow::before {
border-top-color: var(--info); border-right-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.r-tooltip.complete-pass .tooltip-inner, .r-tooltip.complete-pass .tooltip-inner,
@ -1090,14 +1105,7 @@
} }
.r-tooltip.complete-pass .arrow::before, .r-tooltip.complete-pass .arrow::before,
.r-tooltip.completed-pass .arrow::before { .r-tooltip.completed-pass .arrow::before {
border-top-color: var(--success); border-right-color: var(--success);
}
.r-tooltip.incomplete .tooltip-inner {
background-color: var(--danger);
}
.r-tooltip.incomplete .arrow::before {
border-top-color: var(--danger);
} }
.m-buttonbar { .m-buttonbar {

View file

@ -1232,13 +1232,29 @@
.features-treestudyplan .s-required.neutral { .features-treestudyplan .s-required.neutral {
color: #aaa; color: #aaa;
} }
.path-local-treestudyplan .r-tooltip.warning .tooltip-inner,
.features-treestudyplan .r-tooltip.warning .tooltip-inner {
background-color: color-mix(in srgb, var(--warning) 60%, #000);
}
.path-local-treestudyplan .r-tooltip.warning .arrow::before,
.features-treestudyplan .r-tooltip.warning .arrow::before {
border-right-color: color-mix(in srgb, var(--warning) 60%, #000);
}
.path-local-treestudyplan .r-tooltip.info .tooltip-inner,
.features-treestudyplan .r-tooltip.info .tooltip-inner {
background-color: color-mix(in srgb, var(--info) 60%, #000);
}
.path-local-treestudyplan .r-tooltip.info .arrow::before,
.features-treestudyplan .r-tooltip.info .arrow::before {
border-right-color: color-mix(in srgb, var(--info) 60%, #000);
}
.path-local-treestudyplan .r-tooltip.incomplete .tooltip-inner, .path-local-treestudyplan .r-tooltip.incomplete .tooltip-inner,
.path-local-treestudyplan .r-tooltip.complete-fail .tooltip-inner, .path-local-treestudyplan .r-tooltip.complete-fail .tooltip-inner,
.path-local-treestudyplan .r-tooltip.completed-fail .tooltip-inner, .path-local-treestudyplan .r-tooltip.completed-fail .tooltip-inner,
.features-treestudyplan .r-tooltip.incomplete .tooltip-inner, .features-treestudyplan .r-tooltip.incomplete .tooltip-inner,
.features-treestudyplan .r-tooltip.complete-fail .tooltip-inner, .features-treestudyplan .r-tooltip.complete-fail .tooltip-inner,
.features-treestudyplan .r-tooltip.completed-fail .tooltip-inner { .features-treestudyplan .r-tooltip.completed-fail .tooltip-inner {
background-color: var(--danger); background-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.path-local-treestudyplan .r-tooltip.incomplete .arrow::before, .path-local-treestudyplan .r-tooltip.incomplete .arrow::before,
.path-local-treestudyplan .r-tooltip.complete-fail .arrow::before, .path-local-treestudyplan .r-tooltip.complete-fail .arrow::before,
@ -1246,19 +1262,19 @@
.features-treestudyplan .r-tooltip.incomplete .arrow::before, .features-treestudyplan .r-tooltip.incomplete .arrow::before,
.features-treestudyplan .r-tooltip.complete-fail .arrow::before, .features-treestudyplan .r-tooltip.complete-fail .arrow::before,
.features-treestudyplan .r-tooltip.completed-fail .arrow::before { .features-treestudyplan .r-tooltip.completed-fail .arrow::before {
border-top-color: var(--danger); border-right-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.path-local-treestudyplan .r-tooltip.complete .tooltip-inner, .path-local-treestudyplan .r-tooltip.complete .tooltip-inner,
.path-local-treestudyplan .r-tooltip.completed .tooltip-inner, .path-local-treestudyplan .r-tooltip.completed .tooltip-inner,
.features-treestudyplan .r-tooltip.complete .tooltip-inner, .features-treestudyplan .r-tooltip.complete .tooltip-inner,
.features-treestudyplan .r-tooltip.completed .tooltip-inner { .features-treestudyplan .r-tooltip.completed .tooltip-inner {
background-color: var(--info); background-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.path-local-treestudyplan .r-tooltip.complete .arrow::before, .path-local-treestudyplan .r-tooltip.complete .arrow::before,
.path-local-treestudyplan .r-tooltip.completed .arrow::before, .path-local-treestudyplan .r-tooltip.completed .arrow::before,
.features-treestudyplan .r-tooltip.complete .arrow::before, .features-treestudyplan .r-tooltip.complete .arrow::before,
.features-treestudyplan .r-tooltip.completed .arrow::before { .features-treestudyplan .r-tooltip.completed .arrow::before {
border-top-color: var(--info); border-right-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.path-local-treestudyplan .r-tooltip.complete-pass .tooltip-inner, .path-local-treestudyplan .r-tooltip.complete-pass .tooltip-inner,
.path-local-treestudyplan .r-tooltip.completed-pass .tooltip-inner, .path-local-treestudyplan .r-tooltip.completed-pass .tooltip-inner,
@ -1270,15 +1286,7 @@
.path-local-treestudyplan .r-tooltip.completed-pass .arrow::before, .path-local-treestudyplan .r-tooltip.completed-pass .arrow::before,
.features-treestudyplan .r-tooltip.complete-pass .arrow::before, .features-treestudyplan .r-tooltip.complete-pass .arrow::before,
.features-treestudyplan .r-tooltip.completed-pass .arrow::before { .features-treestudyplan .r-tooltip.completed-pass .arrow::before {
border-top-color: var(--success); border-right-color: var(--success);
}
.path-local-treestudyplan .r-tooltip.incomplete .tooltip-inner,
.features-treestudyplan .r-tooltip.incomplete .tooltip-inner {
background-color: var(--danger);
}
.path-local-treestudyplan .r-tooltip.incomplete .arrow::before,
.features-treestudyplan .r-tooltip.incomplete .arrow::before {
border-top-color: var(--danger);
} }
.path-local-treestudyplan .m-buttonbar, .path-local-treestudyplan .m-buttonbar,
.features-treestudyplan .m-buttonbar { .features-treestudyplan .m-buttonbar {