Skip to content

Commit

Permalink
Merge pull request #7230 from pretendWhale/v2.5.2
Browse files Browse the repository at this point in the history
V2.5.2
  • Loading branch information
donny-wong authored Sep 27, 2024
2 parents dc710a4 + 70c4a65 commit bf88ab7
Show file tree
Hide file tree
Showing 33 changed files with 520 additions and 156 deletions.
10 changes: 6 additions & 4 deletions .github/workflows/test_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,13 @@ jobs:
- name: Install chromedriver
uses: nanasess/setup-chromedriver@v2
- name: Run chromedriver
run: chromedriver --whitelisted-ips &
run: chromedriver --port=9515 --whitelisted-ips &
- name: Run rspec tests
run: |
bundle exec rspec
bundle exec rspec spec/system
run: bundle exec rspec
env:
MARKUS__PYTHON: ./venv/bin/python3
- name: Run rspec system tests
run: bundle exec rspec spec/system
env:
MARKUS__PYTHON: ./venv/bin/python3
- name: Coveralls Parallel (rspec)
Expand Down
17 changes: 17 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## [v2.5.2]

### ✨ New features and improvements

- Improve textviewer rendering speed (#7211)
- Add periodic roster syncing via LTI (#7178)
- Allow instructors to assign graders by section (#7179)

### 🐛 Bug fixes

- Fix JSON/CSV summary of test results to always be inline with latest test run (#7214)
- Allow annotations to be added to results with released peer reviews (#7222)

### 🔧 Internal changes

- Manually specify chromedriver port number in Github actions (#7209)

## [v2.5.1]

### 🐛 Bug fixes
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ GEM
psych (5.1.2)
stringio
public_suffix (5.0.5)
puma (6.4.2)
puma (6.4.3)
nio4r (~> 2.0)
raabro (1.4.0)
racc (1.8.1)
Expand Down
2 changes: 1 addition & 1 deletion app/MARKUS_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION=v2.5.1,PATCH_LEVEL=DEV
VERSION=v2.5.2,PATCH_LEVEL=DEV
16 changes: 15 additions & 1 deletion app/assets/javascripts/Components/Modals/roster_sync_modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class LtiRosterModal extends React.Component {
include_tas: true,
include_students: true,
include_instructors: true,
automatic_sync: true,
};
}

Expand All @@ -36,6 +37,7 @@ class LtiRosterModal extends React.Component {
include_students: this.state.include_students,
include_tas: this.state.include_tas,
include_instructors: this.state.include_instructors,
automatic_sync: this.state.automatic_sync,
lti_deployment_id: this.props.roster_deployment_id,
},
});
Expand Down Expand Up @@ -82,13 +84,25 @@ class LtiRosterModal extends React.Component {
<input
type="checkbox"
name="include_instructors"
key="1"
key="3"
defaultChecked="true"
onChange={this.handleChange}
/>
{I18n.t("lti.sync_instructors")}
</label>
</p>
<p>
<label>
<input
type="checkbox"
name="automatic_sync"
key="4"
defaultChecked="true"
onChange={this.handleChange}
/>
{I18n.t("lti.sync_automatically")}
</label>
</p>

<section className={"modal-container dialog-actions"}>
<input type="submit" value={I18n.t("lti.roster_sync")} />
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<div className="flex-row-expand" key={section}>
<label htmlFor={`input-${section}`} className="modal-inline-label">
{section}
</label>
<select className={`input-${section}`} name={section}>
<option value=""></option>
{graders.map(grader => (
<option key={grader.user_name} value={grader.user_name}>
{grader.user_name}
</option>
))}
</select>
</div>
);
};

render() {
return (
<Modal
className="react-modal dialog"
isOpen={this.props.isOpen}
onRequestClose={this.props.onRequestClose}
>
<form onSubmit={this.onSubmit} ref={this.input}>
<div className={"modal-container-vertical"}>
<h2>{I18n.t("graders.assign_by_section_modal_title")}</h2>
<p style={{"max-width": "300px"}}>{I18n.t("graders.assign_by_section_instruction")}</p>
{this.sectionsArray.map(section => this.renderSectionRow(section))}
</div>
<div className={"modal-container"}>
<input type="submit" value={I18n.t("graders.actions.assign_by_section")} />
</div>
</form>
</Modal>
);
}
}

SectionDistributionModal.propTypes = {
graders: PropTypes.arrayOf(PropTypes.object).isRequired,
isOpen: PropTypes.bool.isRequired,
onSubmit: PropTypes.func.isRequired,
sections: PropTypes.objectOf(PropTypes.string).isRequired,
};
73 changes: 23 additions & 50 deletions app/assets/javascripts/Components/Result/text_viewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,62 +68,35 @@ export class TextViewer extends React.PureComponent {
let nodeLines = [];
let currLine = document.createElement("span");
currLine.classList.add("source-line");
let currChildren = [];
for (let node of this.raw_content.current.childNodes) {
if (node.nodeType === Node.TEXT_NODE) {
// SourceCodeLine.glow assumes text nodes are wrapped in <span> elements
let textContainer = document.createElement("span");
let textNode = null;
if (node.textContent.includes("\n")) {
const splits = node.textContent.split("\n");
for (let i = 0; i < splits.length - 1; i++) {
textNode = document.createTextNode(splits[i] + "\n");
textContainer.appendChild(textNode);
currLine.appendChild(textContainer);
nodeLines.push(currLine);
currLine = document.createElement("span");
currLine.classList.add("source-line");
textContainer = document.createElement("span");
}
textNode = document.createTextNode(splits[splits.length - 1]);
} else {
textNode = node.cloneNode(true);
}
textContainer.appendChild(textNode);
currLine.appendChild(textContainer);
} else {
if (node.textContent.includes("\n")) {
const splits = node.textContent.split("\n");
let textContainer = document.createElement("span");
textContainer.className = node.className;
let textNode = null;
for (let i = 0; i < splits.length - 1; i++) {
textNode = document.createTextNode(splits[i] + "\n");
textContainer.appendChild(textNode);
currLine.appendChild(textContainer);
nodeLines.push(currLine);
currLine = document.createElement("span");
currLine.classList.add("source-line");
textContainer = document.createElement("span");
textContainer.className = node.className;
}
textNode = document.createTextNode(splits[splits.length - 1]);
textContainer.appendChild(textNode);
currLine.appendChild(textContainer);
} else {
currLine.appendChild(node.cloneNode(true));
}
// Note: SourceCodeLine.glow assumes text nodes are wrapped in <span> elements
let textContainer = document.createElement("span");
let className = node.nodeType === Node.TEXT_NODE ? "" : node.className;
textContainer.className = className;

const splits = node.textContent.split("\n");
for (let i = 0; i < splits.length - 1; i++) {
textContainer.textContent = splits[i] + "\n";
currLine.append(...currChildren, textContainer);
nodeLines.push(currLine);
currLine = document.createElement("span");
currLine.classList.add("source-line");
currChildren = [];
textContainer = document.createElement("span");
textContainer.className = className;
}

textContainer.textContent = splits[splits.length - 1];
currLine.append(...currChildren, textContainer);
}
if (currLine.textContent.length > 0) {
nodeLines.push(currLine);
}
nodeLines.push(this.raw_content.current.lastChild.cloneNode(true));
while (this.raw_content.current.firstChild) {
this.raw_content.current.removeChild(this.raw_content.current.lastChild);
}
for (let n of nodeLines) {
this.raw_content.current.appendChild(n);
}
this.raw_content.current.replaceChildren(
...nodeLines,
this.raw_content.current.lastChild.cloneNode(true)
);
};

change_font_size = delta => {
Expand Down
41 changes: 41 additions & 0 deletions app/assets/javascripts/Components/graders_manager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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,
Expand Down Expand Up @@ -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), {
Expand Down Expand Up @@ -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,
});
Expand Down Expand Up @@ -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 : [];
Expand Down Expand Up @@ -305,6 +332,7 @@ class GradersManager extends React.Component {
<GradersActionBox
assignAll={this.assignAll}
openGraderDistributionModal={this.openGraderDistributionModal}
openSectionDistributionModal={this.openSectionDistributionModal}
unassignAll={this.unassignAll}
showHidden={this.state.show_hidden}
showHiddenGroups={this.state.show_hidden_groups}
Expand Down Expand Up @@ -406,6 +434,15 @@ class GradersManager extends React.Component {
onSubmit={this.assignRandomly}
/>
)}
{this.state.isSectionDistributionModalOpen && (
<SectionDistributionModal
isOpen={this.state.isSectionDistributionModalOpen}
onRequestClose={() => this.setState({isSectionDistributionModalOpen: false})}
onSubmit={this.assignSections}
graders={this.state.graders}
sections={this.state.sections}
/>
)}
</div>
);
}
Expand Down Expand Up @@ -778,6 +815,10 @@ class GradersActionBox extends React.Component {
<FontAwesomeIcon icon="fa-solid fa-dice" />
{I18n.t("graders.actions.randomly_assign_graders")}
</button>
<button onClick={this.props.openSectionDistributionModal}>
<FontAwesomeIcon icon="fa-solid fa-list" />
{I18n.t("graders.actions.assign_by_section")}
</button>
<button onClick={this.props.unassignAll}>
<FontAwesomeIcon icon="fa-solid fa-user-minus" />
{I18n.t("graders.actions.unassign_grader")}
Expand Down
2 changes: 2 additions & 0 deletions app/assets/javascripts/fontawesome_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
faGripVertical,
faInfo,
faLink,
faList,
faMinus,
faPen,
faPeopleGroup,
Expand Down Expand Up @@ -87,6 +88,7 @@ library.add(
faGripVertical,
faInfo,
faLink,
faList,
faMinus,
faPen,
faPeopleGroup,
Expand Down
Loading

0 comments on commit bf88ab7

Please sign in to comment.