MDL-86006 mod_assign: Multimarking Unit/Behat tests

This commit is contained in:
David Woloszyn
2026-03-05 16:16:33 +11:00
parent bb03ef4171
commit 05971060c4
15 changed files with 1233 additions and 95 deletions
@@ -127,6 +127,9 @@ final class restore_date_test extends \restore_date_testcase {
'timelimit' => DAYSECS,
'submissionattachments' => 1,
'gradepenalty' => 1,
'markercount' => 5,
'multimarkmethod' => 'average',
'multimarkrounding' => 0,
];
$assign = $this->getDataGenerator()->create_module('assign', $record);
@@ -0,0 +1,95 @@
@mod @mod_assign @javascript
Feature: Allocate markers to student submissions
In order to use the marking workflow and record provisional marks before calculating a final grade
As a teacher
I need to allocate markers to student submissions
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | One | student1@example.com |
| student2 | Student | Two | student2@example.com |
| teacher1 | Teacher | One | teacher1@example.com |
| teacher2 | Teacher | Two | teacher2@example.com |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | editingteacher |
And the following "activity" exists:
| activity | assign |
| course | C1 |
| idnumber | A1 |
| name | Assignment 1 |
| section | 1 |
| completion | 1 |
| markingworkflow | 1 |
| markingallocation | 1 |
| markercount | 2 |
| grade[modgrade_type] | point |
| grade[modgrade_point] | 100 |
Scenario: Allocate markers to students via the Allocate Markers page
Given I am on the "A1" "assign activity" page logged in as teacher1
And I navigate to "Submissions" in current page administration
And I set the field "selectall" to "1"
And I click on "Allocate marker" "button" in the "sticky-footer" "region"
And I click on "Allocate marker" "button" in the ".modal-footer" "css_element"
When I select "Teacher One" from the "Allocated marker 1" singleselect
And I select "Teacher Two" from the "Allocated marker 2" singleselect
And I press "Save changes"
Then the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 |
| Student One | Teacher One | Teacher Two |
| Student Two | Teacher One | Teacher Two |
Scenario: Allocate markers to students via the Quick Grading page
Given I am on the "A1" "assign activity" page logged in as teacher1
And I navigate to "Submissions" in current page administration
And I click on "Quick grading" "checkbox"
When I set the field "Allocated marker 1" in the "Student One" "table_row" to "Teacher One"
And I set the field "Allocated marker 1" in the "Student Two" "table_row" to "Teacher One"
And I set the field "Allocated marker 2" in the "Student One" "table_row" to "Teacher Two"
And I set the field "Allocated marker 2" in the "Student Two" "table_row" to "Teacher Two"
And I click on "Save" "button" in the "sticky-footer" "region"
And I press "Continue"
And I click on "Quick grading" "checkbox"
Then the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 |
| Student One | Teacher One | Teacher Two |
| Student Two | Teacher One | Teacher Two |
Scenario: Allocate markers to students via the Grader page
Given I am on the "A1" "assign activity" page logged in as teacher1
And I go to "Student One" "Assignment 1" activity advanced grading page
When I set the field "Marker 1" to "Teacher One"
And I set the field "Marker 2" to "Teacher Two"
And I press "Save changes"
And I am on the "A1" "assign activity" page
And I navigate to "Submissions" in current page administration
Then the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 |
| Student One | Teacher One | Teacher Two |
| Student Two | | |
Scenario: Allocate a duplicate marker to a student
Given I am on the "A1" "assign activity" page logged in as teacher1
And I navigate to "Submissions" in current page administration
And I set the field "selectall" to "1"
And I click on "Allocate marker" "button" in the "sticky-footer" "region"
And I click on "Allocate marker" "button" in the ".modal-footer" "css_element"
When I select "Teacher One" from the "Allocated marker 1" singleselect
And I select "Teacher One" from the "Allocated marker 2" singleselect
And I press "Save changes"
Then the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 |
| Student One | Teacher One | |
| Student Two | Teacher One | |
And the following should not exist in the "submissions" table:
| Marker 2 |
| Teacher One |
| Teacher One |
@@ -0,0 +1,168 @@
@mod @mod_assign @javascript
Feature: Allocate marks to student submissions
In order to assess a submission with multiple markers
As a teacher
I need to allocate marks to student submissions
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | One | student1@example.com |
| student2 | Student | Two | student2@example.com |
| teacher1 | Teacher | One | teacher1@example.com |
| teacher2 | Teacher | Two | teacher2@example.com |
| teacher3 | Teacher | Three | teacher3@example.com |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | editingteacher |
| teacher3 | C1 | editingteacher |
And the following "activity" exists:
| activity | assign |
| course | C1 |
| idnumber | A1 |
| name | Assignment 1 |
| section | 1 |
| completion | 1 |
| markingworkflow | 1 |
| markingallocation | 1 |
| markercount | 2 |
| multimarkmethod | maximum |
| grade[modgrade_type] | point |
| grade[modgrade_point] | 100 |
| assignfeedback_comments_enabled | 1 |
And the following "mod_assign > marker_allocations" exist:
| assign | user | marker |
| Assignment 1 | student1 | teacher1 |
| Assignment 1 | student1 | teacher2 |
| Assignment 1 | student2 | teacher1 |
| Assignment 1 | student2 | teacher2 |
Scenario: Allocate marks to students via the Quick Grading page
Given I am on the "A1" "assign activity" page logged in as teacher1
And I navigate to "Submissions" in current page administration
And I click on "Quick grading" "checkbox"
When I set the field "User mark" in the "Student One" "table_row" to "99"
And I click on "Save" "button" in the "sticky-footer" "region"
And I press "Continue"
And I click on "Quick grading" "checkbox"
Then the following should exist in the "submissions" table:
| First name | Marker 1 |
| Student One | 99 |
Scenario: Allocate marks to students via the Advanced Marker window
Given I am on the "A1" "assign activity" page logged in as teacher1
And I go to "Student One" "Assignment 1" activity advanced marking page
When I set the field "Mark out of 100" to "50"
And I press "Save changes"
And I am on the "A1" "assign activity" page
And I navigate to "Submissions" in current page administration
Then the following should exist in the "submissions" table:
| First name | Marker 1 |
| Student One | 50 |
Scenario: Set workflow state for an allocated mark via Advanced Marker window
Given I am on the "A1" "assign activity" page logged in as teacher1
And I go to "Student One" "Assignment 1" activity advanced marking page
When I set the field "Mark out of 100" to "42"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I am on the "A1" "assign activity" page
And I navigate to "Submissions" in current page administration
Then the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status |
| Student One | Marking completed | Not marked | In marking |
| Student Two | | | Not marked |
Scenario: Bulk set workflow state as allocated markers
Given I am on the "A1" "assign activity" page logged in as teacher1
And I navigate to "Submissions" in current page administration
And I set the field "selectall" to "1"
And I click on "Change marking state" "button" in the "sticky-footer" "region"
And I click on "Change marking state" "button" in the ".modal-footer" "css_element"
And I select "Mark" from the "Workflow context" singleselect
When I select "Marking completed" from the "Marking workflow state" singleselect
And I press "Save changes"
Then the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status |
| Student One | Marking completed | Not marked | In marking |
| Student Two | Marking completed | Not marked | In marking |
# Log in as the other teacher and change their workflow status too.
And I am on the "A1" "assign activity" page logged in as teacher2
And I navigate to "Submissions" in current page administration
And I set the field "selectall" to "1"
And I click on "Change marking state" "button" in the "sticky-footer" "region"
And I click on "Change marking state" "button" in the ".modal-footer" "css_element"
And I select "Mark" from the "Workflow context" singleselect
And I select "Marking completed" from the "Marking workflow state" singleselect
And I press "Save changes"
And the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status |
| Student One | Marking completed | Marking completed | Marking completed |
| Student Two | Marking completed | Marking completed | Marking completed |
Scenario: Grades are only calculated after all marks are given
Given I am on the "A1" "assign activity" page logged in as teacher1
And I go to "Student One" "Assignment 1" activity advanced marking page
When I set the field "Mark out of 100" to "42"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I am on the "A1" "assign activity" page
And I navigate to "Submissions" in current page administration
Then the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status |
| Student One | Marking completed | Not marked | In marking |
And the following should not exist in the "submissions" table:
| Grade |
| 42 |
# Now, provide marks as teacher2.
And I am on the "A1" "assign activity" page logged in as teacher2
And I go to "Student One" "Assignment 1" activity advanced marking page
And I set the field "Mark out of 100" to "55"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I am on the "A1" "assign activity" page
And I navigate to "Submissions" in current page administration
And the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status | Grade |
| Student One | Marking completed | Marking completed | Marking completed | 55 |
Scenario: Grade updates when new allocated marker provides mark
Given I am on the "A1" "assign activity" page logged in as teacher1
And I change window size to "large"
And I go to "Student One" "Assignment 1" activity advanced marking page
When I set the field "Mark out of 100" to "50"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
# Now, provide marks as teacher2.
And I am on the "A1" "assign activity" page logged in as teacher2
And I go to "Student One" "Assignment 1" activity advanced marking page
And I set the field "Mark out of 100" to "51"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I am on the "A1" "assign activity" page
And I navigate to "Submissions" in current page administration
Then the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status | Grade |
| Student One | Marking completed | Marking completed | Marking completed | 51 |
# Swap teacher2 for teacher3 and provide a new mark.
And I am on the "A1" "assign activity" page logged in as teacher3
And I go to "Student One" "Assignment 1" activity advanced grading page
And I set the field "Marking workflow state" to "In marking"
And I set the field "Marker 2" to "Teacher Three"
And I press "Save changes"
And I am on the "A1" "assign activity" page
And I go to "Student One" "Assignment 1" activity advanced marking page
And I set the field "Mark out of 100" to "52"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I am on the "A1" "assign activity" page
And I navigate to "Submissions" in current page administration
And the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status | Grade |
| Student One | Marking completed | Marking completed | Marking completed | 52 |
@@ -86,4 +86,31 @@ class behat_mod_assign extends behat_base {
$rule = ['contextid' => 1, 'overdueby' => DAYSECS, 'penalty' => 10, 'sortorder' => 0];
$DB->insert_record('gradepenalty_duedate_rule', (object) $rule);
}
/**
* Goes to the student's advanced marking page.
*
* @Given /^I go to "(?P<user_fullname>(?:[^"]|\\")*)" "(?P<activity_name>(?:[^"]|\\")*)" activity advanced marking page$/
* @param string $userfullname The user's full name including firstname and lastname.
* @param string $activityname The activity name
*/
public function i_go_to_activity_advanced_marking_page(string $userfullname, string $activityname): void {
// Step to access the user grade page from the grading page.
$this->execute('behat_navigation::go_to_breadcrumb_location', $this->escape($activityname));
$this->execute('behat_general::click_link', get_string('gradeitem:submissions', 'mod_assign'));
$this->execute(
'behat_general::i_click_on_in_the',
[
$this->escape(get_string('markactions', 'assign')),
'actionmenu',
$this->escape($userfullname),
'table_row',
]
);
$this->execute('behat_action_menu::i_choose_in_the_open_action_menu', get_string('markverb', 'mod_assign'));
}
}
@@ -57,7 +57,7 @@ Feature: In an assignment, teachers can filter displayed submissions by assigned
And I press "Save and display"
And I am on the "Test assignment name" "assign activity" page
And I go to "Student 1" "Test assignment name" activity advanced grading page
And I set the field "allocatedmarker" to "Marker 1"
And I set the field "Marker 1" to "Marker 1"
And I set the field "Notify student" to "0"
And I press "Save changes"
And I am on the "Test assignment name" Activity page
@@ -130,7 +130,7 @@ Feature: In an assignment, teachers can filter displayed submissions by assigned
And I change window size to "large"
And I go to "Student 1" "Test assignment name" activity advanced grading page
# Allocate Marker 1 as the marker of Student 1.
And I set the field "allocatedmarker" to "Marker 1"
And I set the field "Marker 1" to "Marker 1"
And I set the field "Notify student" to "0"
And I press "Save changes"
And I follow "View all submissions"
@@ -32,6 +32,7 @@ Feature: In an assignment, teachers can change filters in the grading app
| assignsubmission_file_enabled | 0 |
| markingworkflow | 1 |
| markingallocation | 1 |
| markercount | 1 |
@javascript
Scenario: Set filters in the grading table and see them in the grading app
@@ -40,7 +41,7 @@ Feature: In an assignment, teachers can change filters in the grading app
And I should not see "Course 1 &amp;"
And the "title" attribute of "a[title='Course: Course 1 &']" "css_element" should not contain "&amp;"
And I should not see "Test assignment name &amp;"
And I set the field "allocatedmarker" to "Marker 1"
And I set the field "Marker 1" to "Marker 1"
And I set the field "workflowstate" to "In marking"
And I set the field "Notify student" to "0"
And I press "Save changes"
@@ -64,7 +65,7 @@ Feature: In an assignment, teachers can change filters in the grading app
Scenario: Set filters in the grading app and see them in the grading table
Given I am on the "Test assignment name &" Activity page logged in as teacher1
And I go to "Student 1" "Test assignment name &" activity advanced grading page
And I set the field "allocatedmarker" to "Marker 1"
And I set the field "Marker 1" to "Marker 1"
And I set the field "workflowstate" to "In marking"
And I set the field "Notify student" to "0"
And I press "Save changes"
@@ -0,0 +1,241 @@
@mod @mod_assign @javascript
Feature: Complete multi-marking workflow
In order to conduct an assignment with multi-marking
As a teacher
I need to:
- Allocate markers to the student(s)
- Allocate marks to the student(s) submission(s)
- Calculate a final grade based on the configured multi-marking agreement method
- Release the final grade to the student(s)
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | One | student1@example.com |
| student2 | Student | Two | student2@example.com |
| student3 | Student | Three | student3@example.com |
| student4 | Student | Four | student4@example.com |
| teacher1 | Teacher | One | teacher1@example.com |
| teacher2 | Teacher | Two | teacher2@example.com |
| teacher3 | Teacher | Three | teacher3@example.com |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | editingteacher |
| teacher3 | C1 | editingteacher |
And the following "groups" exist:
| name | course | idnumber | participation |
| Group 1 | C1 | G1 | 1 |
| Group 2 | C1 | G2 | 1 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| student2 | G1 |
| student3 | G2 |
| student4 | G2 |
And the following "activity" exists:
| activity | assign |
| course | C1 |
| idnumber | A1 |
| name | Assignment 1 |
| section | 1 |
| completion | 1 |
| markingworkflow | 1 |
| markingallocation | 1 |
| markercount | 2 |
| multimarkmethod | average |
| multimarkrounding | 1 |
| grade[modgrade_type] | point |
| grade[modgrade_point] | 100 |
And the following "activity" exists:
| activity | assign |
| course | C1 |
| idnumber | A2 |
| name | Assignment 2 |
| section | 1 |
| completion | 1 |
| markingworkflow | 1 |
| markingallocation | 1 |
| markercount | 2 |
| multimarkmethod | maximum |
| multimarkrounding | 1 |
| teamsubmission | 1 |
| grade[modgrade_type] | point |
| grade[modgrade_point] | 100 |
Scenario: Complete workflow of multi-marking
# Firstly, allocate the markers to the students.
Given I am on the "A1" "assign activity" page logged in as teacher1
And I change window size to "large"
And I navigate to "Submissions" in current page administration
And I set the field "selectall" to "1"
When I click on "Allocate marker" "button" in the "sticky-footer" "region"
And I click on "Allocate marker" "button" in the ".modal-footer" "css_element"
And I select "Teacher One" from the "Allocated marker 1" singleselect
And I select "Teacher Two" from the "Allocated marker 2" singleselect
And I press "Save changes"
Then the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status |
| Student One | Teacher One | Teacher Two | Not marked |
| Student Two | Teacher One | Teacher Two | Not marked |
| Student Three | | | Not marked |
| Student Four | | | Not marked |
# Then allocate marks to the student submissions as teacher1.
And I go to "Student One" "Assignment 1" activity advanced marking page
And I set the field "Mark out of 100" to "99"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I go to "Student Two" "Assignment 1" activity advanced marking page
And I set the field "Mark out of 100" to "11"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I am on the "A1" "assign activity" page
And I navigate to "Submissions" in current page administration
And the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status |
| Student One | 99 | | In marking |
| Student Two | 11 | | In marking |
| Student Three | | | Not marked |
| Student Four | | | Not marked |
# Then allocate marks to the student submissions as teacher2.
And I am on the "A1" "assign activity" page logged in as teacher2
And I go to "Student One" "Assignment 1" activity advanced marking page
And I set the field "Mark out of 100" to "88"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I go to "Student Two" "Assignment 1" activity advanced marking page
And I set the field "Mark out of 100" to "22"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I am on the "A1" "assign activity" page
And I navigate to "Submissions" in current page administration
And the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status | Grade |
| Student One | 99 | 88 | Marking completed | 94 |
| Student Two | 11 | 22 | Marking completed | 17 |
| Student Three | | | Not marked | |
| Student Four | | | Not marked | |
# Then we check the calculated final grade and release them to the students.
And I am on the "A1" "assign activity" page logged in as teacher1
And I navigate to "Submissions" in current page administration
And I set the field "selectall" to "1"
And I click on "Change marking state" "button" in the "sticky-footer" "region"
And I click on "Change marking state" "button" in the "Set marking workflow state" "dialogue"
And I set the field "Workflow context" to "Grade"
And I set the field "Marking workflow state" to "Released"
And I press "Save changes"
And the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status | Final grade |
| Student One | 99 | 88 | Released | 94 |
| Student Two | 11 | 22 | Released | 17 |
| Student Three | | | Released | |
| Student Four | | | Released | |
Scenario: Complete workflow of multi-marking with group submissions
# Firstly, allocate the markers to the students (to test thoroughly, one group will have the same marker
# for both students. The other group won't).
Given I am on the "A2" "assign activity" page logged in as teacher1
And I change window size to "large"
And I navigate to "Submissions" in current page administration
And I click on "Quick grading" "checkbox"
When I set the field "Allocated marker 1" in the "Student One" "table_row" to "Teacher One"
And I set the field "Allocated marker 1" in the "Student Two" "table_row" to "Teacher One"
And I set the field "Allocated marker 1" in the "Student Three" "table_row" to "Teacher One"
And I set the field "Allocated marker 1" in the "Student Four" "table_row" to "Teacher One"
And I set the field "Allocated marker 2" in the "Student One" "table_row" to "Teacher Two"
And I set the field "Allocated marker 2" in the "Student Two" "table_row" to "Teacher Two"
And I set the field "Allocated marker 2" in the "Student Three" "table_row" to "Teacher Two"
And I set the field "Allocated marker 2" in the "Student Four" "table_row" to "Teacher Three"
And I click on "Save" "button" in the "sticky-footer" "region"
And I press "Continue"
And I click on "Quick grading" "checkbox"
Then the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status |
| Student One | Teacher One | Teacher Two | |
| Student Two | Teacher One | Teacher Two | |
| Student Three | Teacher One | Teacher Two | |
| Student Four | Teacher One | Teacher Three | |
# Next we test adding a mark as Marker 1 (teacher1) to a student in Group 1. This should populate to the other
# student in Group 1, but not the student with this same marker, who is not in Group 1.
And I go to "Student One" "Assignment 2" activity advanced marking page
And I set the field "Mark out of 100" to "50"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I go to "Student Four" "Assignment 2" activity advanced marking page
And I set the field "Mark out of 100" to "60"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I am on the "A2" "assign activity" page
And I navigate to "Submissions" in current page administration
And the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status |
| Student One | 50 | | In marking |
| Student Two | 50 | | In marking |
| Student Three | 60 | | In marking |
| Student Four | 60 | | In marking |
# Next we add a mark as teacher2 to a student in both groups. The mark given to the student in Group 1 should
# populate to the other student in Group 1. The mark given to the student in Group 2 should not, as they are not
# an allocated marker for that final student.
And I am on the "A2" "assign activity" page logged in as teacher2
And I go to "Student One" "Assignment 2" activity advanced marking page
And I set the field "Mark out of 100" to "30"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I am on the "A2" "assign activity" page
And I go to "Student Three" "Assignment 2" activity advanced marking page
And I set the field "Mark out of 100" to "15"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I am on the "A2" "assign activity" page
And I navigate to "Submissions" in current page administration
And the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status |
| Student One | 50 | 30 | Marking completed |
| Student Two | 50 | 30 | Marking completed |
| Student Three | 60 | 15 | Marking completed |
| Student Four | 60 | | In marking |
# Then we login as teacher3 and add that last mark to the final student.
And I am on the "A2" "assign activity" page logged in as teacher3
And I go to "Student Four" "Assignment 2" activity advanced marking page
And I set the field "Mark out of 100" to "99"
And I set the field "Marking workflow state" to "Marking completed"
And I press "Save changes"
And I am on the "A2" "assign activity" page
And I navigate to "Submissions" in current page administration
And the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status |
| Student One | 50 | 30 | Marking completed |
| Student Two | 50 | 30 | Marking completed |
| Student Three | 60 | 15 | Marking completed |
| Student Four | 60 | 99 | Marking completed |
# Then we check that the grades have been calculated correctly.
And I am on the "A2" "assign activity" page logged in as teacher1
And I navigate to "Submissions" in current page administration
And the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status | Grade |
| Student One | 50 | 30 | Marking completed | 50 |
| Student Two | 50 | 30 | Marking completed | 50 |
| Student Three | 60 | 15 | Marking completed | 60 |
| Student Four | 60 | 99 | Marking completed | 99 |
# Then we release them all and check the final grade column.
And I am on the "A2" "assign activity" page logged in as teacher1
And I navigate to "Submissions" in current page administration
And I set the field "selectall" to "1"
And I click on "Change marking state" "button" in the "sticky-footer" "region"
And I click on "Change marking state" "button" in the "Set marking workflow state" "dialogue"
And I set the field "Workflow context" to "Grade"
And I set the field "Marking workflow state" to "Released"
And I press "Save changes"
And the following should exist in the "submissions" table:
| First name | Marker 1 | Marker 2 | Status | Final grade |
| Student One | 50 | 30 | Released | 50 |
| Student Two | 50 | 30 | Released | 50 |
| Student Three | 60 | 15 | Released | 60 |
| Student Four | 60 | 100 | Released | 99 |
@@ -1076,7 +1076,8 @@ final class events_test extends \advanced_testcase {
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course);
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
$assign = $this->create_instance($course, ['markercount' => 1]);
// Trigger and capture the event.
$sink = $this->redirectEvents();
+3 -8
View File
@@ -735,7 +735,6 @@ final class externallib_test extends \mod_assign\externallib_advanced_testcase {
$userflag->mailed = 0;
$userflag->extensionduedate = 0;
$userflag->workflowstate = 'inmarking';
$userflag->allocatedmarker = $USER->id;
$DB->insert_record('assign_user_flags', $userflag);
@@ -757,7 +756,6 @@ final class externallib_test extends \mod_assign\externallib_advanced_testcase {
$this->assertEquals(0, $userflag['mailed']);
$this->assertEquals(0, $userflag['extensionduedate']);
$this->assertEquals('inmarking', $userflag['workflowstate']);
$this->assertEquals($USER->id, $userflag['allocatedmarker']);
}
/**
@@ -1742,7 +1740,6 @@ final class externallib_test extends \mod_assign\externallib_advanced_testcase {
$userflags = array();
$userflag['userid'] = $student->id;
$userflag['workflowstate'] = 'inmarking';
$userflag['allocatedmarker'] = $USER->id;
$userflags = array($userflag);
$createduserflags = mod_assign_external::set_user_flags($assign->id, $userflags);
@@ -1759,7 +1756,6 @@ final class externallib_test extends \mod_assign\externallib_advanced_testcase {
$this->assertEquals(2, $createduserflag->mailed);
$this->assertEquals(0, $createduserflag->extensionduedate);
$this->assertEquals('inmarking', $createduserflag->workflowstate);
$this->assertEquals($USER->id, $createduserflag->allocatedmarker);
// Create update data.
$userflags = array();
@@ -1781,7 +1777,6 @@ final class externallib_test extends \mod_assign\externallib_advanced_testcase {
$this->assertEquals(2, $updateduserflag->mailed);
$this->assertEquals(0, $updateduserflag->extensionduedate);
$this->assertEquals('readyforreview', $updateduserflag->workflowstate);
$this->assertEquals($USER->id, $updateduserflag->allocatedmarker);
}
/**
@@ -2824,7 +2819,7 @@ final class externallib_test extends \mod_assign\externallib_advanced_testcase {
$DB->update_record('user', $student);
$this->setUser($teacher);
$participants = mod_assign_external::list_participants($assignment->id, 0, '', 0, 0, false, true, true);
$participants = mod_assign_external::list_participants($assignment->id, 0, '', 0, 0, false, true, true, false);
$participants = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants);
$this->assertCount(1, $participants);
@@ -2843,7 +2838,7 @@ final class externallib_test extends \mod_assign\externallib_advanced_testcase {
$this->assertEquals('', $participant['submissionstatus']);
$this->assertArrayHasKey('enrolledcourses', $participant);
$participants = mod_assign_external::list_participants($assignment->id, 0, '', 0, 0, false, false, true);
$participants = mod_assign_external::list_participants($assignment->id, 0, '', 0, 0, false, false, true, false);
$participants = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants);
// Check that the list of courses the participant is enrolled is not returned.
$participant = $participants[0];
@@ -2919,7 +2914,7 @@ final class externallib_test extends \mod_assign\externallib_advanced_testcase {
$this->setUser($teacher);
// Test mod_assign_external::list_participants.
$participants = mod_assign_external::list_participants($assignmodule->id, $group->id, '', 0, 0, false, true, true);
$participants = mod_assign_external::list_participants($assignmodule->id, $group->id, '', 0, 0, false, true, true, false);
$participants = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants);
$this->assertEquals($group->id, $participants[0]['groupid']);
$this->assertEquals(format_string($gname, true), $participants[0]['groupname']);
+1 -1
View File
@@ -75,7 +75,7 @@ class mod_assign_testable_assign extends assign {
$data = array();
$data['id'] = $this->get_course_module()->id;
$data['selectedusers'] = $selectedusers;
$data['allocatedmarker'] = $markerid;
$data['allocatedmarker1'] = $markerid;
$data['action'] = 'setbatchmarkingallocation';
mod_assign_batch_set_allocatedmarker_form::mock_submit($data);
@@ -43,6 +43,12 @@ class behat_mod_assign_generator extends behat_generator_base {
'required' => ['assign', 'user', 'extensionduedate'],
'switchids' => ['assign' => 'cmid', 'user' => 'userid'],
],
'marker_allocations' => [
'singular' => 'marker_allocation',
'datagenerator' => 'marker_allocation',
'required' => ['assign', 'user', 'marker'],
'switchids' => ['assign' => 'cmid', 'user' => 'userid', 'marker' => 'markerid'],
],
];
}
@@ -55,4 +61,15 @@ class behat_mod_assign_generator extends behat_generator_base {
protected function get_assign_id(string $identifier): int {
return $this->get_cm_by_activity_name('assign', $identifier)->id;
}
/**
* Get the user id for the marker based on their username.
*
* @param string $username The user's username
* @return int The user's ID
*/
protected function get_marker_id(string $username): int {
return $this->get_user_id($username);
}
}
+35
View File
@@ -59,6 +59,9 @@ class mod_assign_generator extends testing_module_generator {
'maxattempts' => 1,
'markingworkflow' => 0,
'markingallocation' => 0,
'markercount' => 0,
'multimarkmethod' => null,
'multimarkrounding' => null,
'markinganonymous' => 0,
'activityformat' => 0,
'timelimit' => 0,
@@ -203,4 +206,36 @@ class mod_assign_generator extends testing_module_generator {
$DB->insert_record('assign_overrides', (object) $data);
}
/**
* Create a marker allocation record.
*
* @param array $data Array containing: ['assignid', 'userid', 'markerid']
*/
public function create_marker_allocation(array $data): void {
global $DB;
if (!isset($data['cmid'])) {
throw new coding_exception('Must specify assign when creating a marker allocation.');
}
if (!isset($data['userid'])) {
throw new coding_exception('Must specify user when creating a marker allocation.');
}
if (!isset($data['markerid'])) {
throw new coding_exception('Must specify marker when creating a marker allocation.');
}
[$course, $cm] = get_course_and_cm_from_cmid($data['cmid'], 'assign');
$context = context_module::instance($cm->id);
$assign = new assign($context, $cm, $course);
$DB->insert_record('assign_allocated_marker', [
'student' => $data['userid'],
'assignment' => $assign->get_instance()->id,
'marker' => $data['markerid'],
]);
}
}
-40
View File
@@ -3349,46 +3349,6 @@ You can see it appended to your <a href="' . $assignurl .
$this->assertEquals(50, (int)$grades[$student->id]->rawgrade);
}
/**
* Test that a student allocated a specific marker is only shown to that marker.
*/
public function test_markerallocation(): void {
global $PAGE;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
$otherteacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
$assign = $this->create_instance($course, [
'markingworkflow' => 1,
'markingallocation' => 1,
]);
$PAGE->set_url(new \moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
// Allocate marker to submission.
$this->mark_submission($teacher, $assign, $student, null, [
'allocatedmarker' => $teacher->id,
]);
// Check the allocated marker can view the submission.
$this->setUser($teacher);
$users = $assign->list_participants(0, true);
$this->assertEquals(1, count($users));
$this->assertTrue(isset($users[$student->id]));
$cm = get_coursemodule_from_instance('assign', $assign->get_instance()->id);
$context = \context_module::instance($cm->id);
$assign = new mod_assign_testable_assign($context, $cm, $course);
// Check that other teachers can't view this submission.
$this->setUser($otherteacher);
$users = $assign->list_participants(0, true);
$this->assertEquals(0, count($users));
}
/**
* Ensure that a teacher cannot submit for students as standard.
*/
+508 -34
View File
@@ -16,11 +16,14 @@
namespace mod_assign;
use assign;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/lib/accesslib.php');
require_once($CFG->dirroot . '/course/lib.php');
require_once($CFG->dirroot . '/mod/assign/locallib.php');
/**
* Unit tests for (some of) mod/assign/markerallocaion_test.php.
@@ -29,6 +32,7 @@ require_once($CFG->dirroot . '/course/lib.php');
* @category test
* @copyright 2017 Andrés Melo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \assign
*/
final class markerallocation_test extends \advanced_testcase {
@@ -36,20 +40,24 @@ final class markerallocation_test extends \advanced_testcase {
private $course;
/**
* Create all the needed elements to test the difference between both functions.
* @var array Generated users
*/
public function test_markerusers(): void {
$this->resetAfterTest();
global $DB;
private array $users = [];
// Create a course, by default it is created with 5 sections.
$this->course = $this->getDataGenerator()->create_course();
// Setting assing module, markingworkflow and markingallocation set to 1 to enable marker allocation.
$record = new \stdClass();
$record->course = $this->course;
/**
* @var array Generated groups
*/
private array $groups = [];
/**
* Create the assignment object for testing.
*
* @param array $args Array of options that can be overwritten.
* @return assign
*/
private function create_assignment(array $args = []): assign {
$modulesettings = [
'course' => $this->course->id,
'alwaysshowdescription' => 1,
'submissiondrafts' => 1,
'requiresubmissionstatement' => 0,
@@ -58,63 +66,206 @@ final class markerallocation_test extends \advanced_testcase {
'sendlatenotifications' => 0,
'duedate' => 0,
'allowsubmissionsfromdate' => 0,
'grade' => 100,
'grade' => (!isset($args['scale'])) ? 100 : null,
'cutoffdate' => 0,
'teamsubmission' => 0,
'teamsubmission' => ($args['teamsubmission']) ?? 0,
'requireallteammemberssubmit' => 0,
'teamsubmissiongroupingid' => 0,
'blindmarking' => 0,
'attemptreopenmethod' => 'untilpass',
'maxattempts' => 1,
'markingworkflow' => 1,
'markingallocation' => 1,
'markercount' => ($args['markercount']) ?? ASSIGN_MULTIMARKING_DEFAULT_MARKERS,
'multimarkmethod' => ($args['multimarkmethod']) ?? ASSIGN_MULTIMARKING_METHOD_MANUAL,
'multimarkrounding' => ($args['multimarkrounding']) ?? null,
];
$assignelement = $this->getDataGenerator()->create_module('assign', $record, $modulesettings);
if (isset($args['scale'])) {
$scale = $this->getDataGenerator()->create_scale();
$modulesettings['gradetype'] = GRADE_TYPE_SCALE;
$modulesettings['gradescale'] = $scale->id;
}
$coursesectionid = course_add_cm_to_section($this->course->id, $assignelement->id, 1, null, 'assign');
$generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
$instance = $generator->create_instance($modulesettings);
[$course, $cm] = get_course_and_cm_from_instance($instance->id, 'assign');
$context = \core\context\module::instance($cm->id);
$assignment = new assign($context, $cm, $course);
return $assignment;
}
/**
* Setup all required test data.
*/
private function setup_data(): void {
global $DB;
$this->resetAfterTest();
// Create a course, by default it is created with 5 sections.
$this->course = $this->getDataGenerator()->create_course();
// Adding users to the course.
$userdata = array();
$userdata['firstname'] = 'teacher1';
$userdata['lasttname'] = 'lastname_teacher1';
$user1 = $this->getDataGenerator()->create_user($userdata);
$this->getDataGenerator()->enrol_user($user1->id, $this->course->id, 'teacher');
$this->users[0] = $this->getDataGenerator()->create_user($userdata);
$this->getDataGenerator()->enrol_user($this->users[0]->id, $this->course->id, 'editingteacher');
$userdata = array();
$userdata['firstname'] = 'teacher2';
$userdata['lasttname'] = 'lastname_teacher2';
$user2 = $this->getDataGenerator()->create_user($userdata);
$this->getDataGenerator()->enrol_user($user2->id, $this->course->id, 'teacher');
$this->users[1] = $this->getDataGenerator()->create_user($userdata);
$this->getDataGenerator()->enrol_user($this->users[1]->id, $this->course->id, 'editingteacher');
$userdata = array();
$userdata['firstname'] = 'student';
$userdata['lasttname'] = 'lastname_student';
$user3 = $this->getDataGenerator()->create_user($userdata);
$this->getDataGenerator()->enrol_user($user3->id, $this->course->id, 'student');
$this->users[2] = $this->getDataGenerator()->create_user($userdata);
$this->getDataGenerator()->enrol_user($this->users[2]->id, $this->course->id, 'student');
// Adding manager to the system.
$userdata = array();
$userdata['firstname'] = 'Manager';
$userdata['lasttname'] = 'lastname_Manager';
$user4 = $this->getDataGenerator()->create_user($userdata);
// Getting id of manager role.
$this->users[3] = $this->getDataGenerator()->create_user($userdata);
$managerrole = $DB->get_record('role', array('shortname' => 'manager'));
if (!empty($managerrole)) {
// By default the context of the system is assigned.
$idassignment = $this->getDataGenerator()->role_assign($managerrole->id, $user4->id);
$this->getDataGenerator()->role_assign($managerrole->id, $this->users[3]->id);
}
}
/**
* Setup group data for teamsubmission tests.
*/
private function setup_group_data(): void {
$this->resetAfterTest(false);
// Create a course, by default it is created with 5 sections.
$this->course = $this->getDataGenerator()->create_course();
// Split users into seaprate arrays for easier use here.
$teachers = [];
$students = [];
// Adding teachers to the course.
for ($i = 1; $i <= 2; $i++) {
$userdata = [];
$userdata['firstname'] = 'teacher' . $i;
$userdata['lasttname'] = 'lastname_teacher' . $i;
$teachers[$i] = $this->getDataGenerator()->create_user($userdata);
$this->getDataGenerator()->enrol_user($teachers[$i]->id, $this->course->id, 'teacher');
}
$oldusers = array($user1, $user2, $user4);
$newusers = array($user1, $user2);
// Adding students to the course.
for ($i = 1; $i <= 6; $i++) {
$userdata = [];
$userdata['firstname'] = 'student' . $i;
$userdata['lasttname'] = 'lastname_student' . $i;
$students[$i] = $this->getDataGenerator()->create_user($userdata);
$this->getDataGenerator()->enrol_user($students[$i]->id, $this->course->id, 'student');
}
// Adding students to groups.
$this->groups['A'] = $this->getDataGenerator()->create_group(['courseid' => $this->course->id, 'name' => 'A']);
$this->groups['B'] = $this->getDataGenerator()->create_group(['courseid' => $this->course->id, 'name' => 'B']);
foreach ($students as $studentnumber => $user) {
if ($studentnumber <= 3) {
groups_add_member($this->groups['A'], $user);
} else {
groups_add_member($this->groups['B'], $user);
}
}
$this->users = ['students' => $students, 'teachers' => $teachers];
}
/**
* Test marker allocation and marking with group submissions.
*
* @covers ::update_allocated_markers, ::save_grade
*/
public function test_allocated_markers_with_group_submissions(): void {
$this->setup_group_data();
$assignment = $this->create_assignment([
'teamsubmission' => 1,
]);
// To test the logic that a marker should not be able to update anyone not in their group
// we will use the "public" method `save_grade` instead of the internal `update_mark`.
// Firstly, allocate teacher1 to every student in group A.
foreach ($this->users['students'] as $studentnumber => $student) {
if ($studentnumber <= 3) {
$assignment->update_allocated_markers($student->id, [$this->users['teachers'][1]->id]);
}
}
// Allocate a mark to the first student in the group.
// This should spread out to the other students in the group as well.
$this->setUser($this->users['teachers'][1]);
// Before we save it, we need to create the submission record, which won't happen from just saving it.
// We are passing -1 as userid because it's a required argument, but if the groupid is present, then
// the `get_group_submission` function ignores it, so it just needs any value really.
$assignment->get_group_submission(-1, $this->groups['A']->id, true);
// Then save it.
$assignment->save_grade($this->users['students'][1]->id, (object)[
'mark' => 50,
'applytoall' => 1,
'attemptnumber' => -1,
]);
// All 3 students in the group should now have the same mark from this allocated marker.
foreach ($this->users['students'] as $studentnumber => $student) {
if ($studentnumber <= 3) {
$gradeobject = $assignment->get_user_grade($student->id, true);
$mark = $assignment->get_mark($gradeobject->id, $this->users['teachers'][1]->id);
$this->assertEquals(50, $mark->mark);
}
}
// Now allocate teacher2 to 2 out of 3 students in group B.
foreach ($this->users['students'] as $studentnumber => $student) {
if ($studentnumber > 3 && $studentnumber < 6) {
$assignment->update_allocated_markers($student->id, [$this->users['teachers'][2]->id]);
}
}
// Allocate a mark to the first student in the group.
$this->setUser($this->users['teachers'][2]);
$assignment->get_group_submission(-1, $this->groups['B']->id, true);
$assignment->save_grade($this->users['students'][4]->id, (object)[
'mark' => 99,
'applytoall' => 1,
'attemptnumber' => -1,
]);
// Only 2 out of 3 students should have the grade applied.
foreach ($this->users['students'] as $studentnumber => $student) {
if ($studentnumber > 3) {
$gradeobject = $assignment->get_user_grade($student->id, true);
$mark = $assignment->get_mark($gradeobject->id, $this->users['teachers'][2]->id);
if ($studentnumber < 6) {
$this->assertEquals(99, $mark->mark);
} else {
$this->assertFalse($mark);
}
}
}
}
/**
* Create all the needed elements to test the difference between both functions.
*
* @coversNothing
*/
public function test_markerusers(): void {
$this->setup_data();
$oldusers = [$this->users[0], $this->users[1], $this->users[3]];
$newusers = [$this->users[0], $this->users[1]];
list($sort, $params) = users_order_by_sql('u');
@@ -140,6 +291,329 @@ final class markerallocation_test extends \advanced_testcase {
}
$this->assertEquals(count($oldmarkers), 0);
}
/**
* Test functionality around having multiple allocated markers.
*
* @covers ::update_allocated_markers, ::update_mark
*/
public function test_multiple_marker_allocation(): void {
$this->setup_data();
$assignment = $this->create_assignment();
// To start with, confirm that no markers are allocated to the student submission.
$markers = $assignment->get_allocated_markers($this->users[2]->id);
$this->assertCount(0, $markers);
// Allocate both teachers to the student assignment.
$assignment->update_allocated_markers($this->users[2]->id, [
$this->users[0]->id,
$this->users[1]->id,
]);
$markers = $assignment->get_allocated_markers($this->users[2]->id);
$this->assertCount(2, $markers);
// Now test that we can add a mark to the submission.
// Firstly, there should be no mark currently for either marker.
$gradeobject = $assignment->get_user_grade($this->users[2]->id, true);
$mark = $assignment->get_mark($gradeobject->id, $this->users[0]->id);
$this->assertFalse($mark);
// Assign a mark as teacher1.
$gradeobject->grader = $this->users[0]->id;
$assignment->update_mark($gradeobject, 99);
// Now check that we can find the mark.
$mark = $assignment->get_mark($gradeobject->id, $this->users[0]->id);
$this->assertEquals("99.00000", $mark->mark);
// Assign a mark as teacher2.
$gradeobject->grader = $this->users[1]->id;
$assignment->update_mark($gradeobject, 11);
// Now check that we can find the mark.
$mark = $assignment->get_mark($gradeobject->id, $this->users[1]->id);
$this->assertEquals("11.00000", $mark->mark);
}
/**
* Test manual calculation of final grade.
*
* @covers ::update_mark
*/
public function test_calculated_marker_grade_manual(): void {
$this->setup_data();
$assignment = $this->create_assignment();
// Allocate both teachers to the student assignment.
$assignment->update_allocated_markers($this->users[2]->id, [
$this->users[0]->id,
$this->users[1]->id,
]);
$gradeobject = $assignment->get_user_grade($this->users[2]->id, true);
// Assign a mark as teacher1.
$gradeobject->grader = $this->users[0]->id;
$assignment->update_mark($gradeobject, 99);
// Assign a mark as teacher2.
$gradeobject->grader = $this->users[1]->id;
$assignment->update_mark($gradeobject, 11);
// With manual calculation, there should be no grade set yet.
$gradeobject = $assignment->get_user_grade($this->users[2]->id, false);
$this->assertEquals(-1, $gradeobject->grade);
}
/**
* Test "maximum" calculation of final grade when using scale grading.
*
* @covers ::update_mark
*/
public function test_calculated_marker_grade_maximum(): void {
$this->setup_data();
$assignment = $this->create_assignment([
'multimarkmethod' => ASSIGN_MULTIMARKING_METHOD_MAX,
]);
// Allocate both teachers to the student assignment.
$assignment->update_allocated_markers($this->users[2]->id, [
$this->users[0]->id,
$this->users[1]->id,
]);
$gradeobject = $assignment->get_user_grade($this->users[2]->id, true);
// Assign a mark as teacher1.
$gradeobject->grader = $this->users[0]->id;
$assignment->update_mark($gradeobject, 11);
// Assign a mark as teacher2.
$gradeobject->grader = $this->users[1]->id;
$assignment->update_mark($gradeobject, 99);
// With max calculation, the grade should be the highest one.
$gradeobject = $assignment->get_user_grade($this->users[2]->id, false);
$this->assertEquals(99, $gradeobject->grade);
}
/**
* Test "average" calculation of final grade when using rounding of "none".
*
* @covers ::update_mark
*/
public function test_calculated_marker_grade_average_round_none(): void {
$this->setup_data();
$assignment = $this->create_assignment([
'multimarkmethod' => ASSIGN_MULTIMARKING_METHOD_AVERAGE,
'multimarkrounding' => ASSIGN_MULTIMARKING_AVERAGE_ROUND_NONE,
]);
// Allocate both teachers to the student assignment.
$assignment->update_allocated_markers($this->users[2]->id, [
$this->users[0]->id,
$this->users[1]->id,
]);
$gradeobject = $assignment->get_user_grade($this->users[2]->id, true);
// Assign a mark as teacher1.
$gradeobject->grader = $this->users[0]->id;
$assignment->update_mark($gradeobject, 90);
// Assign a mark as teacher2.
$gradeobject->grader = $this->users[1]->id;
$assignment->update_mark($gradeobject, 25);
// With avg calculation and no rounding, the grade should be 57.5.
$gradeobject = $assignment->get_user_grade($this->users[2]->id, false);
$this->assertEquals(57.5, $gradeobject->grade);
}
/**
* Test "average" calculation of final grade when using rounding of "down".
*
* @covers ::update_mark
*/
public function test_calculated_marker_grade_average_rounding_down(): void {
$this->setup_data();
$assignment = $this->create_assignment([
'multimarkmethod' => ASSIGN_MULTIMARKING_METHOD_AVERAGE,
'multimarkrounding' => ASSIGN_MULTIMARKING_AVERAGE_ROUND_DOWN,
]);
// Allocate both teachers to the student assignment.
$assignment->update_allocated_markers($this->users[2]->id, [
$this->users[0]->id,
$this->users[1]->id,
]);
$gradeobject = $assignment->get_user_grade($this->users[2]->id, true);
// Assign a mark as teacher1.
$gradeobject->grader = $this->users[0]->id;
$assignment->update_mark($gradeobject, 90);
// Assign a mark as teacher2.
$gradeobject->grader = $this->users[1]->id;
$assignment->update_mark($gradeobject, 25);
// With avg calculation and down rounding, the grade should be 57.
$gradeobject = $assignment->get_user_grade($this->users[2]->id, false);
$this->assertEquals(57, $gradeobject->grade);
}
/**
* Test that the grade calculation from marks using method "average" with up rounding, sets the correct grade.
*
* @covers ::update_mark
*/
public function test_calculated_marker_grade_average_round_up(): void {
$this->setup_data();
$assignment = $this->create_assignment([
'multimarkmethod' => ASSIGN_MULTIMARKING_METHOD_AVERAGE,
'multimarkrounding' => ASSIGN_MULTIMARKING_AVERAGE_ROUND_UP,
]);
// Allocate both teachers to the student assignment.
$assignment->update_allocated_markers($this->users[2]->id, [
$this->users[0]->id,
$this->users[1]->id,
]);
$gradeobject = $assignment->get_user_grade($this->users[2]->id, true);
// Assign a mark as teacher1.
$gradeobject->grader = $this->users[0]->id;
$assignment->update_mark($gradeobject, 90);
// Assign a mark as teacher2.
$gradeobject->grader = $this->users[1]->id;
$assignment->update_mark($gradeobject, 25);
// With avg calculation and up rounding, the grade should be 58.
$gradeobject = $assignment->get_user_grade($this->users[2]->id, false);
$this->assertEquals(58, $gradeobject->grade);
}
/**
* Test that the grade calculation from marks using method "average" with natural rounding, sets the correct grade.
*
* @covers ::update_mark
*/
public function test_calculated_marker_grade_average_round_natural(): void {
$this->setup_data();
$assignment = $this->create_assignment([
'multimarkmethod' => ASSIGN_MULTIMARKING_METHOD_AVERAGE,
'multimarkrounding' => ASSIGN_MULTIMARKING_AVERAGE_ROUND_NATURAL,
]);
// Allocate both teachers to the student assignment.
$assignment->update_allocated_markers($this->users[2]->id, [
$this->users[0]->id,
$this->users[1]->id,
]);
$gradeobject = $assignment->get_user_grade($this->users[2]->id, true);
// Assign a mark as teacher1.
$gradeobject->grader = $this->users[0]->id;
$assignment->update_mark($gradeobject, 90);
// Assign a mark as teacher2.
$gradeobject->grader = $this->users[1]->id;
$assignment->update_mark($gradeobject, 25);
// With avg calculation and natural rounding, the grade should be 58.
$gradeobject = $assignment->get_user_grade($this->users[2]->id, false);
$this->assertEquals(58, $gradeobject->grade);
}
/**
* Test that the workflow state changes on the overall grade based on marker states.
*
* @covers ::update_mark, ::calculate_and_save_overall_workflow_state
*/
public function test_calculated_marker_workflow(): void {
$this->setup_data();
$assignment = $this->create_assignment();
// Allocate both teachers to the student assignment.
$assignment->update_allocated_markers($this->users[2]->id, [
$this->users[0]->id,
$this->users[1]->id,
]);
// First confirm that the overall grade workflow state is not set.
$flags = $assignment->get_user_flags($this->users[2]->id, true);
$this->assertEmpty($flags->workflowstate);
// One marker then sets their mark to be in the state "In Marking".
$gradeobject = $assignment->get_user_grade($this->users[2]->id, true);
$gradeobject->grader = $this->users[0]->id;
$assignment->update_mark($gradeobject, null, ASSIGN_MARKING_WORKFLOW_STATE_INMARKING);
$assignment->calculate_and_save_overall_workflow_state($gradeobject, $flags, $flags->workflowstate);
// Re-check the overall workflow. This should now be "In Marking" as well.
$flags = $assignment->get_user_flags($this->users[2]->id, true);
$this->assertEquals(ASSIGN_MARKING_WORKFLOW_STATE_INMARKING, $flags->workflowstate);
// Now this teacher marks theirs as "Marking Complete".
$gradeobject->grader = $this->users[0]->id;
$assignment->update_mark($gradeobject, 90, ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW);
$assignment->calculate_and_save_overall_workflow_state($gradeobject, $flags, $flags->workflowstate);
// Nothing should change on the overall state, that should still be In Marking.
$flags = $assignment->get_user_flags($this->users[2]->id, true);
$this->assertEquals(ASSIGN_MARKING_WORKFLOW_STATE_INMARKING, $flags->workflowstate);
// Now the second marker sets theirs as "Marking Complete".
$gradeobject->grader = $this->users[1]->id;
$assignment->update_mark($gradeobject, 70, ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW);
$assignment->calculate_and_save_overall_workflow_state($gradeobject, $flags, $flags->workflowstate);
// Now that both are complete, the overall state should be the same.
$flags = $assignment->get_user_flags($this->users[2]->id, true);
$this->assertEquals(ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW, $flags->workflowstate);
}
/**
* Test that when we remove a marker their marks are not counted towards anything.
*
* @covers ::update_mark
*/
public function test_unallocated_marker_not_included_in_mark_calculations(): void {
$this->setup_data();
$assignment = $this->create_assignment([
'multimarkmethod' => ASSIGN_MULTIMARKING_METHOD_AVERAGE,
'multimarkrounding' => ASSIGN_MULTIMARKING_AVERAGE_ROUND_NATURAL,
]);
// Allocate both teachers to the student assignment.
$assignment->update_allocated_markers($this->users[2]->id, [
$this->users[0]->id,
$this->users[1]->id,
]);
$gradeobject = $assignment->get_user_grade($this->users[2]->id, true);
// Assign a mark as teacher1.
$gradeobject->grader = $this->users[0]->id;
$assignment->update_mark($gradeobject, 90);
// Now we remove teacher1 and add manager instead. So we have manager and teacher2 as the markers.
$assignment->update_allocated_markers($this->users[2]->id, [$this->users[3]->id, $this->users[1]->id]);
// Now add a marker from teacher2.
$gradeobject->grader = $this->users[1]->id;
$assignment->update_mark($gradeobject, 10);
// At this point, though we've had 2 marks, only 1 of the allocated markers has marked.
// So the grade should not be set.
$gradeobject = $assignment->get_user_grade($this->users[2]->id, false);
$this->assertEquals(-1, $gradeobject->grade);
}
}
@@ -210,7 +210,10 @@ final class provider_test extends provider_testcase {
'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
'maxattempts' => 3,
'assignsubmission_onlinetext_enabled' => true,
'assignfeedback_comments_enabled' => true
'assignfeedback_comments_enabled' => true,
'markingworkflow' => 1,
'markingallocation' => 1,
'markercount' => 2,
]);
$context = $assign->get_context();
@@ -253,6 +256,14 @@ final class provider_test extends provider_testcase {
// Give the submission a grade.
$assign->save_grade($user->id, $data);
// Allocate the teacher as a marker on the assignment.
$assign->update_allocated_markers($user->id, [$teacher->id]);
// Give the submission a mark.
$gradeobject = $assign->get_user_grade($user->id, true);
$gradeobject->grader = $teacher->id;
$assign->update_mark($gradeobject, 99.9);
/** @var \core_privacy\tests\request\content_writer $writer */
$writer = writer::with_context($context);
$this->assertFalse($writer->has_any_data());
@@ -284,6 +295,12 @@ final class provider_test extends provider_testcase {
$overrideexport->cutoffdate);
$this->assertEquals(\core_privacy\local\request\transform::datetime($overridedata->allowsubmissionsfromdate),
$overrideexport->allowsubmissionsfromdate);
// Check allocated markers and marks are exported.
$this->assertEquals($teacher->id, $writer->get_data(['Marker allocations'])->data[0]['marker']);
$this->assertEquals($user->id, $writer->get_data(['Marker allocations'])->data[0]['student']);
$this->assertEquals($teacher->id, $writer->get_data(['Marks'])->data[0]['marker']);
$this->assertEquals(99.9, $writer->get_data(['Marks'])->data[0]['mark']);
}
/**
@@ -306,7 +323,10 @@ final class provider_test extends provider_testcase {
'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
'maxattempts' => 3,
'assignsubmission_onlinetext_enabled' => true,
'assignfeedback_comments_enabled' => true
'assignfeedback_comments_enabled' => true,
'markingworkflow' => 1,
'markingallocation' => 1,
'markercount' => 2,
]);
$context = $assign->get_context();
@@ -366,6 +386,18 @@ final class provider_test extends provider_testcase {
$flagdata->extensionduedate = $duedate;
$assign->update_user_flags($flagdata);
// Allocate the teacher as a marker on the assignment for both users.
$assign->update_allocated_markers($user1->id, [$teacher->id]);
$assign->update_allocated_markers($user2->id, [$teacher->id]);
// Give the submission a mark for both users.
$gradeobject = $assign->get_user_grade($user1->id, true);
$gradeobject->grader = $teacher->id;
$assign->update_mark($gradeobject, 11.1);
$gradeobject = $assign->get_user_grade($user2->id, true);
$gradeobject->grader = $teacher->id;
$assign->update_mark($gradeobject, 88.8);
/** @var \core_privacy\tests\request\content_writer $writer */
$writer = writer::with_context($context);
$this->assertFalse($writer->has_any_data());
@@ -393,6 +425,14 @@ final class provider_test extends provider_testcase {
'Feedback comments'])->commenttext);
$this->assertStringContainsString($teachercommenttext3, $writer->get_data(['studentsubmissions', $user2->id, 'attempt 2',
'Feedback comments'])->commenttext);
// Check for marker allocations as this teacher.
$this->assertEquals($teacher->id, $writer->get_data(['Marker allocations'])->data[0]['marker']);
$this->assertEquals($teacher->id, $writer->get_data(['Marker allocations'])->data[1]['marker']);
// And check for marks they have given.
$this->assertEquals($teacher->id, $writer->get_data(['Marks'])->data[0]['marker']);
$this->assertEquals($teacher->id, $writer->get_data(['Marks'])->data[1]['marker']);
}
/**
@@ -415,7 +455,10 @@ final class provider_test extends provider_testcase {
'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
'maxattempts' => 3,
'assignsubmission_onlinetext_enabled' => true,
'assignfeedback_comments_enabled' => true
'assignfeedback_comments_enabled' => true,
'markingworkflow' => 1,
'markingallocation' => 1,
'markercount' => 2,
]);
$context = $assign->get_context();
@@ -478,6 +521,18 @@ final class provider_test extends provider_testcase {
// Give the submission a grade.
$assign->save_grade($user2->id, $data);
// Allocate the teacher as a marker on the assignment for both users.
$assign->update_allocated_markers($user1->id, [$teacher->id]);
$assign->update_allocated_markers($user2->id, [$teacher->id]);
// Give the submission a mark for both users.
$gradeobject = $assign->get_user_grade($user1->id, true);
$gradeobject->grader = $teacher->id;
$assign->update_mark($gradeobject, 11.1);
$gradeobject = $assign->get_user_grade($user2->id, true);
$gradeobject->grader = $teacher->id;
$assign->update_mark($gradeobject, 88.8);
// Delete all user data for this assignment.
provider::delete_data_for_all_users_in_context($context);
@@ -490,6 +545,10 @@ final class provider_test extends provider_testcase {
$this->assertEmpty($records);
$records = $DB->get_records('assignfeedback_comments');
$this->assertEmpty($records);
$records = $DB->get_records('assign_mark');
$this->assertEmpty($records);
$records = $DB->get_records('assign_allocated_marker');
$this->assertEmpty($records);
// Check that overrides and the calendar events are deleted.
$records = $DB->get_records('event');
@@ -520,7 +579,10 @@ final class provider_test extends provider_testcase {
'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
'maxattempts' => 3,
'assignsubmission_onlinetext_enabled' => true,
'assignfeedback_comments_enabled' => true
'assignfeedback_comments_enabled' => true,
'markingworkflow' => 1,
'markingallocation' => 1,
'markercount' => 2,
]);
$context = $assign->get_context();
@@ -583,6 +645,18 @@ final class provider_test extends provider_testcase {
// Give the submission a grade.
$assign->save_grade($user2->id, $data);
// Allocate the teacher as a marker on the assignment for both users.
$assign->update_allocated_markers($user1->id, [$teacher->id]);
$assign->update_allocated_markers($user2->id, [$teacher->id]);
// Give the submission a mark for both users.
$gradeobject = $assign->get_user_grade($user1->id, true);
$gradeobject->grader = $teacher->id;
$assign->update_mark($gradeobject, 11.1);
$gradeobject = $assign->get_user_grade($user2->id, true);
$gradeobject->grader = $teacher->id;
$assign->update_mark($gradeobject, 88.8);
// Delete user 2's data.
$approvedlist = new approved_contextlist($user2, 'mod_assign', [$context->id, $coursecontext->id]);
provider::delete_data_for_user($approvedlist);
@@ -621,6 +695,12 @@ final class provider_test extends provider_testcase {
$record = array_shift($records);
// The remaining event should be for user 1.
$this->assertEquals($user1->id, $record->userid);
// Check marks and allocated markers only exist for user1 now.
$records = $DB->get_records('assign_allocated_marker');
$this->assertCount(1, $records);
$records = $DB->get_records('assign_mark');
$this->assertCount(1, $records);
}
/**
@@ -639,7 +719,7 @@ final class provider_test extends provider_testcase {
$user2 = $this->getDataGenerator()->create_user();
// User 3 made a submission.
$user3 = $this->getDataGenerator()->create_user();
// User 4 makes a submission and it is marked by the teacher.
// User 4 makes a submission and it is graded by the teacher.
$user4 = $this->getDataGenerator()->create_user();
// Grading and providing feedback as a teacher.
$user5 = $this->getDataGenerator()->create_user();
@@ -718,6 +798,25 @@ final class provider_test extends provider_testcase {
// Give the submission a grade.
$assign2->save_grade($user6->id, $data);
// Allocate the teacher as a marker on both assignments for some of the users.
$assign1->update_allocated_markers($user1->id, [$user5->id]);
$assign1->update_allocated_markers($user2->id, [$user5->id]);
$assign1->update_allocated_markers($user6->id, [$user5->id]);
$assign2->update_allocated_markers($user1->id, [$user5->id]);
$assign2->update_allocated_markers($user2->id, [$user5->id]);
$assign2->update_allocated_markers($user6->id, [$user5->id]);
// Give the submission a mark for some users on both assignments.
$gradeobject = $assign1->get_user_grade($user3->id, true);
$gradeobject->grader = $user5->id;
$assign1->update_mark($gradeobject, 10);
$gradeobject = $assign1->get_user_grade($user4->id, true);
$gradeobject->grader = $user5->id;
$assign1->update_mark($gradeobject, 100);
$gradeobject = $assign2->get_user_grade($user6->id, true);
$gradeobject->grader = $user5->id;
$assign2->update_mark($gradeobject, 59);
// Check data is in place.
$data = $DB->get_records('assign_submission');
// We should have one entry for user 3 and two entries each for user 4 and 6.
@@ -735,8 +834,8 @@ final class provider_test extends provider_testcase {
$this->assertEquals(2, $usercounts[$user6->id]);
$data = $DB->get_records('assign_grades');
// Two entries in assign_grades, one for each grade given.
$this->assertCount(2, $data);
// Three entries in assign_grades, for grades given and grade items created for setting marks.
$this->assertCount(3, $data);
$data = $DB->get_records('assign_overrides');
$this->assertCount(1, $data);
@@ -744,6 +843,12 @@ final class provider_test extends provider_testcase {
$data = $DB->get_records('comments');
$this->assertCount(1, $data);
$data = $DB->get_records('assign_allocated_marker');
$this->assertCount(6, $data);
$data = $DB->get_records('assign_mark');
$this->assertCount(3, $data);
$userlist = new \core_privacy\local\request\approved_userlist($context, 'assign', [$user1->id, $user2->id]);
provider::delete_data_for_users($userlist);
@@ -757,6 +862,14 @@ final class provider_test extends provider_testcase {
// No change here.
$this->assertCount(5, $data);
// Should be 4 records left - user6 on assign1, and everyone on assign2.
$data = $DB->get_records('assign_allocated_marker');
$this->assertCount(4, $data);
// Should be unchanged.
$data = $DB->get_records('assign_mark');
$this->assertCount(3, $data);
$userlist = new \core_privacy\local\request\approved_userlist($context, 'assign', [$user3->id, $user5->id]);
provider::delete_data_for_users($userlist);
@@ -767,5 +880,13 @@ final class provider_test extends provider_testcase {
$data = $DB->get_records('assign_grades');
// Grades should be unchanged.
$this->assertCount(2, $data);
// Should be 3 left, all for assign2.
$data = $DB->get_records('assign_allocated_marker');
$this->assertCount(3, $data);
// Should be 1 left, for assign2.
$data = $DB->get_records('assign_mark');
$this->assertCount(1, $data);
}
}