Skip to content

Commit c282bc9

Browse files
authored
restructuring the review attempts page and logic (#11)
* restructuring the review attempts page and logic * only display tasks after lab is launched * moving return button to the button * adding description field and ability to show it on course page * adding popup for start and end lab buttons * removing run task button from viewattempt page * removing duplicates * last minute fixes * improving task filter function and redirecting user after ending lab
1 parent 2831bc3 commit c282bc9

File tree

11 files changed

+205
-80
lines changed

11 files changed

+205
-80
lines changed

classes/crucible.php

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,52 @@ public function getall_attempts($state = 'all', $review = false, $userid = 0) {
285285
return $attempts;
286286
}
287287

288+
public function get_attempts_by_user($userid, $state = 'all', $review = false) {
289+
global $DB;
290+
291+
$sqlparams = [];
292+
$where = [];
293+
294+
$where[] = '{crucible_attempts}.crucibleid = ?';
295+
$sqlparams[] = $this->crucible->id;
296+
297+
switch ($state) {
298+
case 'open':
299+
$where[] = '{crucible_attempts}.state = ?';
300+
$sqlparams[] = crucible_attempt::INPROGRESS;
301+
break;
302+
case 'closed':
303+
$where[] = '{crucible_attempts}.state = ?';
304+
$sqlparams[] = crucible_attempt::FINISHED;
305+
break;
306+
default:
307+
// No additional state filtering
308+
}
309+
310+
// Always filter by user
311+
$where[] = '({crucible_attempts}.userid = ? OR {crucible_attempt_users}.userid = ?)';
312+
$sqlparams[] = $userid;
313+
$sqlparams[] = $userid;
314+
315+
$wherestring = implode(' AND ', $where);
316+
317+
$sql = "SELECT {crucible_attempts}.*
318+
FROM {crucible_attempts}
319+
LEFT JOIN {crucible_attempt_users}
320+
ON {crucible_attempts}.id = {crucible_attempt_users}.attemptid
321+
WHERE $wherestring
322+
ORDER BY timemodified DESC";
323+
324+
$dbattempts = $DB->get_records_sql($sql, $sqlparams);
325+
326+
$attempts = [];
327+
foreach ($dbattempts as $dbattempt) {
328+
$attempts[] = new crucible_attempt($dbattempt);
329+
}
330+
331+
return $attempts;
332+
}
333+
288334
public function init_attempt() {
289335
global $DB, $USER;
290336

@@ -357,24 +403,47 @@ public function init_attempt() {
357403
}
358404

359405
// filter for tasks the user can see and sort by name
360-
function filter_scenario_tasks($tasks, $visible = 0, $gradable = 0) {
361-
406+
function filter_scenario_tasks($tasks, $isVisible = false, $isExecutable = false) {
362407
global $DB;
408+
363409
if (is_null($tasks)) {
364-
return;
410+
return [];
365411
}
366-
$filtered = array();
412+
413+
$filtered = [];
414+
367415
foreach ($tasks as $task) {
368-
if ($task->userExecutable) {
416+
$include = true;
417+
418+
// Filter by visibility from DB
419+
if ($isVisible) {
420+
$sql = 'SELECT visible FROM {crucible_tasks} WHERE ' .
421+
$DB->sql_compare_text('name') . ' = ' . $DB->sql_compare_text(':name');
422+
423+
$rec = $DB->get_record_sql($sql, ['name' => $task->name]);
424+
425+
if (!$rec || empty($rec->visible)) {
426+
$include = false;
427+
}
428+
}
429+
430+
// Filter by executable flag from task object
431+
if ($isExecutable && empty($task->userExecutable)) {
432+
$include = false;
433+
}
434+
435+
// Include task if it passes all checks
436+
if ($include) {
437+
if (!empty($task->userExecutable)) {
438+
$task->points = $task->totalScore ?? 0;
439+
}
369440
$filtered[] = $task;
370-
$task->points = $task->totalScore;
371441
}
372442
}
373-
443+
374444
usort($filtered, "tasksort");
375445
return $filtered;
376-
377-
}
446+
}
378447

379448
public function get_all_attempts_for_form() {
380449
global $DB, $USER;

lang/en/crucible.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@
118118
$string['taskname'] = 'Task Name';
119119
$string['taskresult'] = 'Latest Task Result';
120120
$string['taskaction'] = 'Action';
121-
$string['taskexecute'] = 'Run Task';
122-
$string['tasknoexecute'] = 'No Action';
121+
$string['taskexecute'] = 'Execute Task';
122+
$string['tasknoexecute'] = 'Not Executable';
123123

124124
// view
125125
$string['eventwithoutattempt'] = 'Event exists but attempt does not exist in moodle db.';
@@ -157,4 +157,7 @@
157157

158158
$string['notasksavailable'] = 'There are no tasks available for this activity.';
159159
$string['backtocruclanding'] = 'Back to Activity View';
160+
$string['start_attempt_confirm'] = 'Are you sure you want to launch the lab?';
161+
$string['stop_attempt_confirm'] = 'Are you sure you want to end the lab?';
162+
160163

lib.php

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,18 @@ function crucible_delete_instance($id) {
216216
* @return cached_cm_info info
217217
*/
218218
function crucible_get_coursemodule_info($coursemodule) {
219+
global $DB;
220+
221+
$crucible = $DB->get_record('crucible', array('id' => $coursemodule->instance), '*', MUST_EXIST);
222+
223+
$info = new cached_cm_info();
224+
$info->name = $crucible->name;
225+
226+
if ($coursemodule->showdescription) {
227+
$info->content = format_module_intro('crucible', $crucible, $coursemodule->id, false);
228+
}
229+
230+
return $info;
219231
}
220232

221233
/*
@@ -404,13 +416,11 @@ function crucible_extend_settings_navigation($settingsnav, $context) {
404416
$beforekey = $keys[$i + 1];
405417
}
406418

407-
if (has_capability('mod/crucible:manage', $PAGE->cm->context)) {
408-
$url = new moodle_url('/mod/crucible/review.php', array('id' => $PAGE->cm->id));
409-
$node = navigation_node::create(get_string('reviewtext', 'mod_crucible'),
410-
new moodle_url($url),
411-
navigation_node::TYPE_SETTING, null, 'mod_crucible_review', new pix_icon('i/grades', 'grades'));
412-
$context->add_node($node, $beforekey);
413-
}
419+
$url = new moodle_url('/mod/crucible/review.php', array('id' => $PAGE->cm->id));
420+
$node = navigation_node::create(get_string('reviewtext', 'mod_crucible'),
421+
new moodle_url($url),
422+
navigation_node::TYPE_SETTING, null, 'mod_crucible_review', new pix_icon('i/grades', 'grades'));
423+
$context->add_node($node, $beforekey);
414424

415425
if (has_capability('mod/crucible:manage', $PAGE->cm->context)) {
416426
$url = new moodle_url('/mod/crucible/manage.php', array('c' => $PAGE->cm->course));

mod_form.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ function definition() {
5353
$config = get_config('crucible');
5454

5555
// Adding the standard "intro" and "introformat" fields.
56-
//$this->standard_intro_elements();
56+
$this->standard_intro_elements();
5757
//TODO remove ability to edit the description and just show the select and dropdown
5858
//$mform->removeElement('introeditor');
5959
//TODO figure out why the description doesnt appear
@@ -192,8 +192,9 @@ function data_postprocessing($data) {
192192
}
193193
$index = array_search($data->eventtemplateid, array_column($this->eventtemplates, 'id'), true);
194194
$data->name = $this->eventtemplates[$index]->name;
195-
$data->intro = $this->eventtemplates[$index]->description;
196-
$data->introeditor['format'] = FORMAT_PLAIN;
195+
$rawdescription = $this->eventtemplates[$index]->description;
196+
$data->intro = strip_tags($rawdescription); // Removes all HTML tags
197+
$data->introformat = FORMAT_PLAIN;
197198

198199
// TODO if grade method changed, update all grades
199200
}

renderer.php

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class mod_crucible_renderer extends plugin_renderer_base {
3939
function display_detail ($crucible, $duration) {
4040
$data = new stdClass();
4141
$data->name = $crucible->name;
42-
$data->intro = $crucible->intro;
42+
$data->intro = strip_tags($crucible->intro);
4343
$data->durationtext = get_string('durationtext', 'mod_crucible');
4444
$data->duration = $duration;
4545
echo $this->render_from_template('mod_crucible/detail', $data);
@@ -192,7 +192,7 @@ function display_attempts($attempts, $showgrade, $showuser = false, $showdetail
192192
global $DB;
193193
$data = new stdClass();
194194
$data->tableheaders = new stdClass();
195-
$data->tabledata[] = array();
195+
$data->tabledata = [];
196196

197197
if ($showuser) {
198198
$data->tableheaders->username = get_string('username', 'mod_crucible');
@@ -262,8 +262,12 @@ function display_tasks($tasks) {
262262
$rowdata = array();
263263
//$rowdata[] = $task->id;
264264
$rowdata[] = $task->name;
265-
$rowdata[] = $task->description;
266-
$rowdata[] = $task->points;
265+
if ($task->description) {
266+
$rowdata[] = $task->description;
267+
} else {
268+
$rowdata[] = "No description available";
269+
}
270+
$rowdata[] = $task->score;
267271
$data->tabledata[] = $rowdata;
268272
}
269273

@@ -353,15 +357,20 @@ function display_results($tasks, $review = false) {
353357
if (isset($task->totalStatus)) {
354358
$rowdata->result = $task->totalStatus;
355359
}
356-
// check whether we can execute the task
357-
// if ((isset($task->triggerCondition)) && ($task->triggerCondition === "Manual")) {
358-
if ($task->executable) {
359-
$rowdata->action = get_string('taskexecute', 'mod_crucible');
360+
361+
if (!$review) {
362+
if ($task->userExecutable) {
363+
$rowdata->action = get_string('taskexecute', 'mod_crucible');
364+
}
365+
else {
366+
$rowdata->noaction = get_string('tasknoexecute', 'mod_crucible');
367+
}
360368
} else {
361-
$rowdata->action = get_string('tasknoexecute', 'mod_crucible');
362-
}
369+
$rowdata->noaction = get_string('tasknoexecute', 'mod_crucible');
370+
}
371+
363372
if ($review) {
364-
if ($task->comment) {
373+
if (isset($task->comment)) {
365374
$rowdata->comment = $task->comment;
366375
} else {
367376
$rowdata->comment = "-";

review.php

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,29 +91,31 @@
9191
$scenariotemplateid = "";
9292
}
9393

94-
if (!$object->is_instructor()) {
95-
redirect($returnurl);
96-
}
97-
9894
$renderer = $PAGE->get_renderer('mod_crucible');
9995
echo $renderer->header();
10096
$renderer->display_detail($crucible, $object->eventtemplate->durationHours);
101-
$renderer->display_return_form($returnurl, $id);
10297

10398
if ($scenariotemplateid) {
10499
$tasks = get_scenariotemplatetasks($object->systemauth, $scenariotemplateid);
105100

106101
if ($tasks) {
107102
// run as system account
108-
$filtered = $object->filter_scenario_tasks($tasks, $visible = 1);
103+
$filtered = $object->filter_scenario_tasks($tasks, true, false);
104+
if ($filtered) {
105+
$renderer->display_tasks($filtered);
106+
}
109107
}
108+
}
110109

111-
if (!is_null($tasks)) {
112-
$renderer->display_tasks($filtered);
113-
}
110+
if ($object->is_instructor()) {
111+
$attempts = $object->getall_attempts('closed', $review = true);
112+
echo $renderer->display_attempts($attempts, $showgrade = true, $showuser = true);
113+
} else {
114+
$userid = $USER->id;
115+
$attempts = $object->get_attempts_by_user($userid, 'closed');
116+
echo $renderer->display_attempts($attempts, $showgrade = true, $showuser = false);
114117
}
115118

116-
$attempts = $object->getall_attempts('all', $review = true);
117-
echo $renderer->display_attempts($attempts, $showgrade = true, $showuser = true);
119+
$renderer->display_return_form($returnurl, $id);
118120

119121
echo $renderer->footer();

templates/form.mustache

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,54 @@ DM20-0196
3737
}}
3838

3939
<span>
40-
<form action="{{url}}" method="post" style="display:inline">
41-
<input id="event" type="hidden" name="event" value=""/>
40+
<form id="lab_form" action="{{url}}" method="post" style="display:inline">
41+
<input id="event" type="hidden" name="event" value="" />
4242
<input id="eventtemplateid" type="hidden" name="eventtemplateid" value="{{eventtemplateid}}" />
43-
<input id="end_button" style="display:none" class="btn btn-primary" type="submit" name="stop" value="End Lab"
44-
{{#joinedAttempt}}
45-
disabled="disabled"
46-
{{/joinedAttempt}}
47-
/>
43+
<input id="start_confirmed" type="hidden" name="start_confirmed" value="" />
44+
<input id="stop_confirmed" type="hidden" name="stop_confirmed" value="" />
45+
4846
<input id="launch_button" style="display:none" class="btn btn-primary" type="submit" name="start" value="Launch Lab" />
47+
<input id="end_button" style="display:none" class="btn btn-danger" type="submit" name="stop" value="End Lab"
48+
{{#joinedAttempt}}disabled="disabled"{{/joinedAttempt}} />
4949
</form>
5050

51-
<button id="wait" style="display:none; width:300px" class="btn btn-primary" type="button" name="start">
51+
<button id="wait" style="display:none; width:300px" class="btn btn-primary" type="button" name="wait">
5252
<span class="spinner-border spinner-border-sm" role="status"></span>
53-
&nbsp;&nbsp
54-
Please wait, system processing
53+
&nbsp;&nbsp;Please wait, system processing
5554
</button>
56-
<input id="failed" style="display:none; width:300px" class="btn btn-primary" type="" name="failed" value="Your operation has failed." />
55+
56+
<input id="failed" style="display:none; width:300px" class="btn btn-secondary" type="button" name="failed" value="Your operation has failed." />
5757

5858
<p id="enable-fullscreen" style="display: none;" class="btn btn-primary">{{fullscreen}}</p>
5959
</span>
60+
61+
<script type="text/javascript">
62+
document.addEventListener('DOMContentLoaded', function () {
63+
const launchBtn = document.getElementById('launch_button');
64+
const endBtn = document.getElementById('end_button');
65+
const startConfirm = document.getElementById('start_confirmed');
66+
const stopConfirm = document.getElementById('stop_confirmed');
67+
68+
if (launchBtn) {
69+
launchBtn.addEventListener('click', function (e) {
70+
const confirmed = confirm("{{#str}} start_attempt_confirm, mod_crucible {{/str}}");
71+
if (!confirmed) {
72+
e.preventDefault();
73+
} else {
74+
startConfirm.value = "yes";
75+
}
76+
});
77+
}
78+
79+
if (endBtn) {
80+
endBtn.addEventListener('click', function (e) {
81+
const confirmed = confirm("{{#str}} stop_attempt_confirm, mod_crucible {{/str}}");
82+
if (!confirmed) {
83+
e.preventDefault();
84+
} else {
85+
stopConfirm.value = "yes";
86+
}
87+
});
88+
}
89+
});
90+
</script>

templates/results.mustache

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ DM20-0196
5252
<tr>
5353
<td>{{name}}</td>
5454
<td>{{desc}}</td>
55-
<td><button id="{{id}}" class="btn btn-primary exec-task">{{action}}</button></td>
55+
{{#action}}<td><button id="{{id}}" class="btn btn-primary exec-task">{{action}}</button></td>{{/action}}
56+
{{#noaction}}<td><button id="{{id}}" class="btn btn-secondary" disabled>{{noaction}}</button></td>{{/noaction}}
5657
{{#comment}}
5758
<td>{{comment}}</td>
5859
{{/comment}}

version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
defined('MOODLE_INTERNAL') || die();
3838

3939
// This is the version of the plugin.
40-
$plugin->version = 2025052101;
40+
$plugin->version = 2025052200;
4141

4242
// This is the version of Moodle this plugin requires.
4343
$plugin->requires = 2022112801;

0 commit comments

Comments
 (0)