diff --git a/Changelog.md b/Changelog.md
index 73b8de355d..96e0ea6b02 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -8,6 +8,7 @@
- Improve textviewer rendering speed (#7211)
- Add periodic roster syncing via LTI (#7178)
+- Allow instructors to assign graders by section (#7179)
### 🐛 Bug fixes
- Fix incorrect calculation of token penalties when submissions are on time (#7216)
diff --git a/app/assets/javascripts/Components/Modals/section_distribution_modal.js b/app/assets/javascripts/Components/Modals/section_distribution_modal.js
new file mode 100644
index 0000000000..3ea46e9ec0
--- /dev/null
+++ b/app/assets/javascripts/Components/Modals/section_distribution_modal.js
@@ -0,0 +1,80 @@
+import React from "react";
+import Modal from "react-modal";
+import PropTypes from "prop-types";
+
+export class SectionDistributionModal extends React.Component {
+ static defaultProps = {
+ override: false,
+ };
+
+ constructor(props) {
+ super(props);
+ this.input = React.createRef();
+ this.sectionsArray = Object.values(this.props.sections).sort();
+ this.graderMap = this.props.graders.reduce((map, grader) => {
+ map[grader.user_name] = grader._id;
+ return map;
+ }, {});
+ }
+
+ componentDidMount() {
+ Modal.setAppElement("body");
+ }
+
+ onSubmit = event => {
+ event.preventDefault();
+ const form = new FormData(this.input.current);
+ const assignments = {};
+ form.forEach((value, key) => {
+ assignments[key] = this.graderMap[value];
+ });
+ this.props.onSubmit(assignments);
+ };
+
+ renderSectionRow = section => {
+ const {graders} = this.props;
+ return (
+
+
+
+
+ );
+ };
+
+ render() {
+ return (
+
+
+
+ );
+ }
+}
+
+SectionDistributionModal.propTypes = {
+ graders: PropTypes.arrayOf(PropTypes.object).isRequired,
+ isOpen: PropTypes.bool.isRequired,
+ onSubmit: PropTypes.func.isRequired,
+ sections: PropTypes.objectOf(PropTypes.string).isRequired,
+};
diff --git a/app/assets/javascripts/Components/graders_manager.jsx b/app/assets/javascripts/Components/graders_manager.jsx
index cdc70c2f58..8ad927a2ac 100644
--- a/app/assets/javascripts/Components/graders_manager.jsx
+++ b/app/assets/javascripts/Components/graders_manager.jsx
@@ -6,6 +6,7 @@ import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {withSelection, CheckboxTable} from "./markus_with_selection_hoc";
import {selectFilter} from "./Helpers/table_helpers";
import {GraderDistributionModal} from "./Modals/graders_distribution_modal";
+import {SectionDistributionModal} from "./Modals/section_distribution_modal";
class GradersManager extends React.Component {
constructor(props) {
@@ -22,6 +23,7 @@ class GradersManager extends React.Component {
hide_unassigned_criteria: false,
sections: {},
isGraderDistributionModalOpen: false,
+ isSectionDistributionModalOpen: false,
show_hidden: false,
show_hidden_groups: false,
hidden_graders_count: 0,
@@ -51,6 +53,11 @@ class GradersManager extends React.Component {
isGraderDistributionModalOpen: true,
});
};
+ openSectionDistributionModal = () => {
+ this.setState({
+ isSectionDistributionModalOpen: true,
+ });
+ };
fetchData = () => {
fetch(Routes.course_assignment_graders_path(this.props.course_id, this.props.assignment_id), {
@@ -87,6 +94,7 @@ class GradersManager extends React.Component {
anonymize_groups: res.anonymize_groups,
hide_unassigned_criteria: res.hide_unassigned_criteria,
isGraderDistributionModalOpen: false,
+ isSectionDistributionModalOpen: false,
hidden_graders_count: res.graders.filter(grader => grader.hidden).length,
inactive_groups_count: inactive_groups_count,
});
@@ -124,6 +132,25 @@ class GradersManager extends React.Component {
}).then(this.fetchData);
};
+ assignSections = assignments => {
+ let sections = Object.keys(assignments);
+ let graders = Object.values(assignments);
+ $.post({
+ url: Routes.global_actions_course_assignment_graders_path(
+ this.props.course_id,
+ this.props.assignment_id
+ ),
+ data: {
+ global_actions: "assign_sections",
+ current_table: this.state.tableName,
+ skip_empty_submissions: this.state.skip_empty_submissions,
+ assignments: assignments,
+ sections: sections,
+ graders: graders,
+ },
+ }).then(this.fetchData);
+ };
+
assignRandomly = weightings => {
let groups = this.groupsTable ? this.groupsTable.state.selection : [];
let criteria = this.criteriaTable ? this.criteriaTable.state.selection : [];
@@ -305,6 +332,7 @@ class GradersManager extends React.Component {
)}
+ {this.state.isSectionDistributionModalOpen && (
+ this.setState({isSectionDistributionModalOpen: false})}
+ onSubmit={this.assignSections}
+ graders={this.state.graders}
+ sections={this.state.sections}
+ />
+ )}
);
}
@@ -778,6 +815,10 @@ class GradersActionBox extends React.Component {
{I18n.t("graders.actions.randomly_assign_graders")}
+