-
Notifications
You must be signed in to change notification settings - Fork 4
CTP-4793 moderated marking behat tests #181
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
base: main
Are you sure you want to change the base?
Changes from all commits
d31029b
3327655
3974e1e
6a82714
ffdf716
0dd0220
c11656d
e7897f4
6a1c2b2
148714e
0782934
288c92f
c66f97f
d08a62f
0247374
b03b3a4
fab04ba
cf63821
bd33232
4d14921
a2a022c
91e9eb6
3174877
06a00b9
524d41b
a1e2a68
2a99682
38b634b
6a66d64
dfb23f8
49d70ad
ffc0ad8
b1ab327
25d3aaa
eed0f21
e0e5343
54ded6b
8595632
4acd88d
a681920
e0e2e62
c25a0ef
f0825cb
f2c1d5e
ce761c5
3d3b579
a073372
40e3f11
456b451
59691be
2f058c5
cdfad18
59b589c
9f5fb79
8a10025
ca332dc
133eb65
e59de35
6c8d0c6
4c33925
97c69a7
42d2c24
137a15d
af894d8
7eea472
d3c874f
cdcff4d
b4137bc
e41fff9
a8ab47c
0610dbe
eab83a0
3fd615e
b4958ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -901,6 +901,13 @@ public function the_coursework_start_date_is_disabled() { | |
| $this->coursework->update_attribute('startdate', 0); | ||
| } | ||
|
|
||
| /** | ||
| * @Given /^the coursework start date is now$/ | ||
| */ | ||
| public function the_coursework_start_date_is_now() { | ||
| $this->coursework->update_attribute('startdate', time()); | ||
| } | ||
|
|
||
| /** | ||
| * @Given /^the coursework start date is in the future$/ | ||
| */ | ||
|
|
@@ -2651,15 +2658,28 @@ public function i_have_a_submission() { | |
|
|
||
| /** | ||
| * Named student has a submission. | ||
| * @Given /^the student called "([\w]+)" has a( finalised)? submission*$/ | ||
| * @Given /^the student called "(?P<name>(?:[^"]|\\")*)" has a( finalised)? submission*$/ | ||
| */ | ||
| public function named_student_has_a_submission(string $firstname, bool $finalised = false) { | ||
| public function named_student_has_a_submission(string $fullname, bool $finalised = false) { | ||
| global $DB; | ||
| $generator = testing_util::get_data_generator()->get_plugin_generator('mod_coursework'); | ||
| $userid = $DB->get_field_sql( | ||
| "SELECT id FROM {user} WHERE firstname = ? AND lastname LIKE 'student%'", | ||
| [$firstname] | ||
| ); | ||
|
|
||
| // Check if the name consists of first- and last name. | ||
| $nameparts = explode(' ', $fullname, 2); | ||
| $firstname = $nameparts[0]; | ||
| $lastname = $nameparts[1] ?? ''; | ||
|
|
||
| if (empty($lastname)) { | ||
| $userid = $DB->get_field_sql( | ||
| "SELECT id FROM {user} WHERE firstname = ? AND lastname LIKE 'student%'", | ||
| [$firstname] | ||
| ); | ||
| } else { | ||
| $userid = $DB->get_field_sql( | ||
| "SELECT id FROM {user} WHERE firstname = ? AND lastname = ?", | ||
| [$firstname, $lastname] | ||
| ); | ||
| } | ||
| if ($userid) { | ||
| $submission = new stdClass(); | ||
| $submission->allocatableid = $userid; | ||
|
|
@@ -3360,6 +3380,25 @@ public function i_should_see_submitted_date($date) { | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * For matching a late submitted date ignoring the time part | ||
| * | ||
| * Example: I should see late submitted date 4 July 2025 | ||
| * | ||
| * @Given /^I should see late submitted date "(?P<date>(?:[^"]|\\")*)"$/ | ||
| */ | ||
| public function i_should_see_late_submitted_date($date) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just use a normal I should see step instead - add a data attribute if needed.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actually a clone of your function i_should_see_submitted_date() but with the added string component "Late" to make the match happen. So what is wrong with my approach that is right with yours? |
||
| $page = $this->getsession()->getpage(); | ||
| $match = $page->find('xpath', "//li[starts-with(normalize-space(string()), 'Submitted Late $date')]"); | ||
|
|
||
| if (!$match) { | ||
| throw new ExpectationException( | ||
| "Should have seen expected submitted late date $date, but it was not there", | ||
| $this->getsession() | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @Given /^sample marking includes student for stage (\d)$/ | ||
| */ | ||
|
|
@@ -3621,4 +3660,281 @@ public function i_set_the_field_to_replacing_line_breaks($field, $value) { | |
| $value = str_replace('\n', chr(10), $value); | ||
| $this->execute([behat_forms::class, 'i_set_the_field_to'], [$field, $value]); | ||
| } | ||
|
|
||
| /** | ||
| * Sets an extension deadline for a student in a coursework. | ||
| * | ||
| * Example: And the coursework extension for "Student 1" in "Coursework 1" is "1 January 2027 08:00" | ||
| * Example: And the coursework extension for "Student 1" in "Coursework 1" is "## + 1 month ##" | ||
| * | ||
| * @Given /^the coursework extension for "(?P<fullname_string>(?:[^"]|\\")*)" in "(?P<cwname>(?:[^"]|\\")*)" is "(?P<datestr>(?:[^"]|\\")*)"$/ | ||
| */ | ||
| public function set_extension_for_user($fullname, $cwname, $datestr) { | ||
| global $DB; | ||
|
|
||
| // Check date string. | ||
| if (is_int($datestr) || (is_string($datestr) && ctype_digit($datestr))) { | ||
opitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $extendeddeadline = (int)$datestr; | ||
| } else { | ||
| $extendeddeadline = strtotime($datestr); | ||
| if ($extendeddeadline === false) { | ||
| throw new \InvalidArgumentException('Invalid user extension date string: ' . $datestr); | ||
| } | ||
| } | ||
|
|
||
| // Find the coursework by name. | ||
| $cw = $DB->get_record('coursework', ['name' => $cwname], '*', MUST_EXIST); | ||
|
|
||
| $user = $this->get_user_from_fullname($fullname); | ||
|
|
||
| // See if an extension already exists. | ||
| $existing = $DB->get_record('coursework_extensions', [ | ||
| 'courseworkid' => $cw->id, | ||
| 'allocatableid' => $user->id, | ||
| 'allocatabletype' => 'user', | ||
| ]); | ||
|
|
||
| $record = new stdClass(); | ||
| $record->courseworkid = $cw->id; | ||
| $record->allocatableid = $user->id; | ||
| $record->allocatabletype = 'user'; | ||
opitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $record->extended_deadline = $extendeddeadline; | ||
| $record->createdbyid = 2; // Admin ID. | ||
|
|
||
| if ($existing) { | ||
| $record->id = $existing->id; | ||
| $DB->update_record('coursework_extensions', $record); | ||
| } else { | ||
| $DB->insert_record('coursework_extensions', $record); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @Given /^the following markers are allocated:$/ | ||
| * | ||
| * @param TableNode $allocations Students and their markers. | ||
| */ | ||
| public function the_following_markers_are_allocated(TableNode $allocations) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See my rebuild of the allocation table - with the refactoring done there this is now two steps of standard behat.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure I can follow... |
||
| global $DB; | ||
|
|
||
| $datahash = $allocations->getHash(); | ||
|
|
||
| foreach ($datahash as $allocate) { | ||
| $stages = ['assessor_1']; | ||
| if (isset($allocate['moderator'])) { | ||
| $stages[] = 'moderator'; | ||
| } else if (isset($allocate['assessor_2'])) { | ||
| $stages[] = 'assessor_2'; | ||
| } | ||
|
|
||
| $student = $this->get_user_from_fullname($allocate['student']); | ||
| foreach ($stages as $stage) { | ||
| $marker = $this->get_user_from_fullname($allocate[$stage]); | ||
|
|
||
| $record = $DB->get_record('coursework_allocation_pairs', [ | ||
| 'courseworkid' => $this->coursework->id, | ||
| 'allocatableid' => $student->id, | ||
| 'allocatableuser' => $student->id, | ||
| 'stageidentifier' => $stage, | ||
| 'allocatabletype' => 'user', | ||
| ]); | ||
| if ($record) { | ||
| $record->assessorid = $marker->id; | ||
| $record->ismanual = 1; | ||
| $DB->update_record('coursework_allocation_pairs', $record); | ||
| } else { | ||
| $record = new stdClass(); | ||
| $record->courseworkid = $this->coursework->id; | ||
| $record->allocatableid = $student->id; | ||
| $record->allocatableuser = $student->id; | ||
| $record->assessorid = $marker->id; | ||
| $record->stageidentifier = $stage; | ||
| $record->allocatabletype = 'user'; | ||
| $record->ismanual = 1; | ||
| $DB->insert_record('coursework_allocation_pairs', $record); | ||
| } | ||
| } | ||
| } | ||
opitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| allocation::remove_cache($this->coursework->id); | ||
| } | ||
|
|
||
| /** | ||
| * Return a user record from a given full name ("firstname lastname") | ||
| * | ||
| * @param string $fullname | ||
| * @return stdClass The user record | ||
| * @throws coding_exception When the full name is invalid or the user cannot be found | ||
| */ | ||
| private function get_user_from_fullname(string $fullname) { | ||
| global $DB; | ||
|
|
||
| // Check if the name consists of first- and last name. | ||
| $nameparts = explode(' ', $fullname, 2); | ||
| $firstname = $nameparts[0]; | ||
| $lastname = $nameparts[1] ?? ''; | ||
|
|
||
| if (empty($lastname)) { | ||
| throw new coding_exception("Full name '{$fullname}' must contain at least one space between first and last name."); | ||
| } | ||
|
|
||
| // Find user by full name (firstname + lastname). | ||
| $user = $DB->get_record('user', [ | ||
| 'firstname' => $firstname, | ||
| 'lastname' => $lastname, | ||
| ]); | ||
|
|
||
| if (!$user) { | ||
| throw new coding_exception("Could not find user with name '{$fullname}'."); | ||
| } | ||
| return $user; | ||
| } | ||
|
|
||
| /** | ||
| * Inserts a grade directly into coursework_feedbacks table. | ||
| * | ||
| * @When /^the submission from "(?P<studentfullname>[^"]*)" is marked by "(?P<markerfullname>[^"]*)" with:$/ | ||
| */ | ||
| public function mark_coursework_submission_directly( | ||
| string $studentfullname, | ||
| string $markerfullname, | ||
| TableNode $table | ||
| ) { | ||
| global $DB; | ||
|
|
||
| $student = $this->get_user_from_fullname($studentfullname); | ||
| $marker = $this->get_user_from_fullname($markerfullname); | ||
|
|
||
| // Resolve submission for this student. | ||
| $submission = $DB->get_record('coursework_submissions', [ | ||
| 'courseworkid' => $this->coursework->id, | ||
| 'allocatableid' => $student->id, | ||
| ]); | ||
| if (!$submission) { | ||
| throw new ExpectationException("Submission for '$studentfullname' not found", $this->getSession()); | ||
| } | ||
|
|
||
| // Resolve marker allocation. | ||
| $allocation = $DB->get_record('coursework_allocation_pairs', [ | ||
| 'courseworkid' => $this->coursework->id, | ||
| 'assessorid' => $marker->id, | ||
| 'allocatableid' => $student->id, | ||
| ]); | ||
| if (!$allocation) { | ||
| throw new ExpectationException("Marker '$markerfullname' for '$studentfullname' not found", $this->getSession()); | ||
| } | ||
|
|
||
| // Extract the provided table values. | ||
| $data = $table->getRowsHash(); | ||
|
|
||
| $mark = isset($data['Mark']) ? floatval($data['Mark']) : null; | ||
| $comment = $data['Comment'] ?? ''; | ||
| $finalised = $data['Finalised'] ?? ''; | ||
|
|
||
| if ($mark === null) { | ||
| throw new ExpectationException("Missing 'Mark' value in table", $this->getSession()); | ||
| } | ||
|
|
||
| // Check if there is already a feedback record. | ||
| $existing = $DB->get_record('coursework_feedbacks', [ | ||
| 'submissionid' => $submission->id, | ||
| 'assessorid' => $marker->id, | ||
| 'stageidentifier' => $allocation->stageidentifier, | ||
| ]); | ||
|
|
||
| // Insert/update feedback record. | ||
| $feedback = new stdClass(); | ||
| $feedback->submissionid = $submission->id; | ||
| $feedback->assessorid = $marker->id; | ||
| $feedback->stageidentifier = $allocation->stageidentifier; | ||
| $feedback->grade = $mark; | ||
| $feedback->feedbackcomment = $comment; | ||
| $feedback->lasteditedbyuser = $marker->id; | ||
| $feedback->finalised = $finalised; | ||
| $feedback->timecreated = time(); | ||
| $feedback->timemodified = time(); | ||
|
|
||
| if ($existing) { | ||
| $feedback->id = $existing->id; | ||
| $DB->update_record('coursework_feedbacks', $feedback); | ||
| } else { | ||
| $DB->insert_record('coursework_feedbacks', $feedback); | ||
| } | ||
| } | ||
andrewhancox marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Inserts a moderation directly into coursework_mod_agreements table. | ||
| * | ||
| * @When /^the submission from "(?P<studentfullname>[^"]*)" is moderated by "(?P<moderatorfullname>[^"]*)" with:$/ | ||
| */ | ||
| public function moderate_submission_directly( | ||
| string $studentfullname, | ||
| string $moderatorfullname, | ||
| TableNode $table | ||
| ) { | ||
| global $DB; | ||
|
|
||
| $student = $this->get_user_from_fullname($studentfullname); | ||
| $moderator = $this->get_user_from_fullname($moderatorfullname); | ||
|
|
||
| // Resolve submission for this student. | ||
| $submission = $DB->get_record('coursework_submissions', [ | ||
| 'courseworkid' => $this->coursework->id, | ||
| 'allocatableid' => $student->id, | ||
| ]); | ||
| if (!$submission) { | ||
| throw new ExpectationException("Submission for '$studentfullname' not found", $this->getSession()); | ||
| } | ||
|
|
||
| // Resolve moderator allocation. | ||
| $allocation = $DB->get_record('coursework_allocation_pairs', [ | ||
| 'courseworkid' => $this->coursework->id, | ||
| 'assessorid' => $moderator->id, | ||
| 'allocatableid' => $student->id, | ||
| 'stageidentifier' => 'moderator', | ||
| ]); | ||
| if (!$allocation) { | ||
| throw new ExpectationException("Moderator '$moderatorfullname' for '$studentfullname' not found", $this->getSession()); | ||
| } | ||
|
|
||
| // Resolve feedback. | ||
| $params = ['submissionid' => $submission->id]; | ||
| $sql = "SELECT * | ||
| FROM {coursework_feedbacks} | ||
| WHERE submissionid = :submissionid | ||
| AND stageidentifier LIKE 'assessor_%'"; | ||
| $feedback = $DB->get_record_sql($sql, $params); | ||
|
|
||
| if (!$feedback) { | ||
| throw new ExpectationException("Feedback for '$studentfullname' not found", $this->getSession()); | ||
| } | ||
|
|
||
| // Extract the provided table values. | ||
| $data = $table->getRowsHash(); | ||
| $agreementtext = $data['Agreement'] ?? ''; | ||
| $comment = $data['Comment'] ?? ''; | ||
|
|
||
| // Check if there is already an agreement record. | ||
| $existing = $DB->get_record('coursework_mod_agreements', [ | ||
| 'feedbackid' => $feedback->id, | ||
| 'moderatorid' => $moderator->id, | ||
| ]); | ||
|
|
||
| // Insert/update agreement record. | ||
| $agreement = new stdClass(); | ||
| $agreement->feedbackid = $feedback->id; | ||
| $agreement->moderatorid = $moderator->id; | ||
| $agreement->agreement = $agreementtext; | ||
| $agreement->modcomment = $comment; | ||
| $agreement->modcommentformat = FORMAT_HTML; | ||
| $agreement->lasteditedby = $moderator->id; | ||
| $agreement->timecreated = time(); | ||
| $agreement->timemodified = time(); | ||
|
|
||
| if ($existing) { | ||
| $agreement->id = $existing->id; | ||
| $DB->update_record('coursework_mod_agreements', $agreement); | ||
| } else { | ||
| $DB->insert_record('coursework_mod_agreements', $agreement); | ||
| } | ||
| } | ||
opitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe: