diff --git a/src/main/java/edu/ksu/canvas/impl/SubmissionImpl.java b/src/main/java/edu/ksu/canvas/impl/SubmissionImpl.java index 6d074173..d38d36de 100644 --- a/src/main/java/edu/ksu/canvas/impl/SubmissionImpl.java +++ b/src/main/java/edu/ksu/canvas/impl/SubmissionImpl.java @@ -1,9 +1,19 @@ package edu.ksu.canvas.impl; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; + import edu.ksu.canvas.interfaces.SubmissionReader; import edu.ksu.canvas.interfaces.SubmissionWriter; import edu.ksu.canvas.model.Progress; @@ -11,16 +21,9 @@ import edu.ksu.canvas.net.Response; import edu.ksu.canvas.net.RestClient; import edu.ksu.canvas.oauth.OauthToken; +import edu.ksu.canvas.requestOptions.GetMultipleSubmissionsOptions; import edu.ksu.canvas.requestOptions.GetSubmissionsOptions; import edu.ksu.canvas.requestOptions.MultipleSubmissionsOptions; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.List; -import java.util.Optional; public class SubmissionImpl extends BaseImpl implements SubmissionReader, SubmissionWriter{ private static final Logger LOG = LoggerFactory.getLogger(SubmissionImpl.class); @@ -62,6 +65,16 @@ public List getSectionSubmissions(final GetSubmissionsOptions option final String url = buildCanvasUrl(String.format("sections/%s/assignments/%d/submissions", options.getCanvasId(), options.getAssignmentId()), options.getOptionsMap()); return getListFromCanvas(url); } + + @Override + public List getCourseSubmissionMultipleAssignments(final GetMultipleSubmissionsOptions options) throws IOException { + if(StringUtils.isBlank(options.getCanvasId())) { + throw new IllegalArgumentException("Course ID is required for this API call"); + } + LOG.debug(String.format("Listing multiple assignment submissions for course %s", options.getCanvasId())); + final String url = buildCanvasUrl(String.format("courses/%s/students/submissions", options.getCanvasId()), options.getOptionsMap()); + return getListFromCanvas(url); + } @Override public Optional getSingleCourseSubmission(final GetSubmissionsOptions options) throws IOException { diff --git a/src/main/java/edu/ksu/canvas/interfaces/SubmissionReader.java b/src/main/java/edu/ksu/canvas/interfaces/SubmissionReader.java index 01f45fb5..de631314 100644 --- a/src/main/java/edu/ksu/canvas/interfaces/SubmissionReader.java +++ b/src/main/java/edu/ksu/canvas/interfaces/SubmissionReader.java @@ -1,12 +1,13 @@ package edu.ksu.canvas.interfaces; -import edu.ksu.canvas.model.assignment.Submission; -import edu.ksu.canvas.requestOptions.GetSubmissionsOptions; - import java.io.IOException; import java.util.List; import java.util.Optional; +import edu.ksu.canvas.model.assignment.Submission; +import edu.ksu.canvas.requestOptions.GetMultipleSubmissionsOptions; +import edu.ksu.canvas.requestOptions.GetSubmissionsOptions; + public interface SubmissionReader extends CanvasReader { /** * Retrieve a list of assignment submissions from a course @@ -25,6 +26,15 @@ public interface SubmissionReader extends CanvasReader getSectionSubmissions(GetSubmissionsOptions options) throws IOException; + + /** + * Retrieve a list of multiple assignment submissions from a course + * + * @param options Options class containing required and optional parameters for this API call + * @return List of assignment submissions in the course with the course ID + * @throws IOException When there is an error communicating with Canvas + */ + List getCourseSubmissionMultipleAssignments(GetMultipleSubmissionsOptions options) throws IOException; /** * Retrieve a single assignment submission from a course diff --git a/src/main/java/edu/ksu/canvas/model/assignment/Submission.java b/src/main/java/edu/ksu/canvas/model/assignment/Submission.java index 47d450e0..3d5de709 100644 --- a/src/main/java/edu/ksu/canvas/model/assignment/Submission.java +++ b/src/main/java/edu/ksu/canvas/model/assignment/Submission.java @@ -32,7 +32,7 @@ public class Submission extends BaseCanvasModel implements Serializable { private Instant postedAt; private String url; private Integer userId; - private Integer gradeId; + private Integer graderId; private User user; private Boolean late; private Boolean assigmentVisible; @@ -40,6 +40,7 @@ public class Submission extends BaseCanvasModel implements Serializable { private Boolean missing; private String workflowState; private List submissionHistory; + private Instant gradedAt; public Integer getId() { return id; @@ -179,12 +180,12 @@ public void setUserId(Integer userId) { this.userId = userId; } - public Integer getGradeId() { - return gradeId; + public Integer getGraderId() { + return graderId; } - public void setGradeId(Integer gradeId) { - this.gradeId = gradeId; + public void setGraderId(Integer graderId) { + this.graderId = graderId; } public User getUser() { @@ -243,4 +244,11 @@ public void setSubmissionHistory(List submissionHistory) { this.submissionHistory = submissionHistory; } + public Instant getGradedAt() { + return gradedAt; + } + + public void setGradedAt(Instant gradedAt) { + this.gradedAt = gradedAt; + } } diff --git a/src/main/java/edu/ksu/canvas/requestOptions/GetMultipleSubmissionsOptions.java b/src/main/java/edu/ksu/canvas/requestOptions/GetMultipleSubmissionsOptions.java new file mode 100644 index 00000000..df2dc61c --- /dev/null +++ b/src/main/java/edu/ksu/canvas/requestOptions/GetMultipleSubmissionsOptions.java @@ -0,0 +1,177 @@ +package edu.ksu.canvas.requestOptions; + +import java.util.Date; +import java.util.List; + +import edu.ksu.canvas.requestOptions.BaseOptions; +import edu.ksu.canvas.requestOptions.GetSubmissionsOptions.Include; + +public class GetMultipleSubmissionsOptions extends BaseOptions { + + private String canvasId; + + /** + * Construct options class with required parameters to retrieve a list of multiple Submissions from courses or sections + * @param canvasId The Course or Section ID, depending on which API is being targeted. + */ + public GetMultipleSubmissionsOptions(final String canvasId) { + this.canvasId = canvasId; + } + + + /** + * Filter the list of submissions by the given student ids.. + * @param ids List of ids to filter submissions by + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions studentIds(final List ids) { + optionsMap.put("student_ids[]", ids); + return this; + } + + /** + * Optionally include more information with the returned assignment Submission objects. + * @param includes List of optional includes + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions includes(final List includes) { + addEnumList("include[]", includes); + return this; + } + + /** + * List of assignments to return submissions for. + * If none are given, submissions for all assignments are returned. + * @param ids List of ids to filter submissions by + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions assignmentIds(final List ids) { + optionsMap.put("assignment_ids[]", ids); + return this; + } + + /** + * When set to true, response will be grouped by student groups. + * Only valid for Submission lists, not individual submission queries. + * @param grouped Whether to group submissions by student group + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions grouped(Boolean grouped) { + addSingleItem("grouped", Boolean.toString(grouped)); + return this; + } + + /** + * If this argument is set to true, the response will only include submissions for assignments + * that have the post_to_sis flag set to true and user enrollments that were added through sis. + * @param postToSis Whether to include submissions that have the flag active + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions postToSis(Boolean postToSis) { + addSingleItem("post_to_sis", Boolean.toString(postToSis)); + return this; + } + + /** + * If this argument is set, the response will only include submissions that were submitted + * after the specified date_time. + * This will exclude submissions that do not have a submitted_at which will exclude unsubmitted submissions + * @param submittedSince date to get submissions after + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions submittedSince(Date submittedSince) { + addSingleItem("submitted_since", submittedSince.toString()); + return this; + } + + /** + * If this argument is set, the response will only include submissions that were graded + * after the specified date_time. + * This will exclude submissions that have not been graded. + * @param gradedSince date to get submissions graded after + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions gradedSince(Date gradedSince) { + addSingleItem("graded_since", gradedSince.toString()); + return this; + } + + /** + * The id of the grading period in which submissions are being requested + * (Requires grading periods to exist on the account) + * @param gradingPeriodId the grading period for the submissions + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions gradingPeriodId(Integer gradingPeriodId) { + addSingleItem("grading_period_id", gradingPeriodId.toString()); + return this; + } + + /** + * + * The current status of the submission + * @param workflowState the status to get the submissions + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions workflowState(String workflowState) { + addSingleItem("workflow_state", workflowState); + return this; + } + + /** + * + * The current state of the enrollments. + * If omitted will include all enrollments that are not deleted. + * @param enrollmentState the state of the enrollment to get submissions + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions enrollmentState(String enrollmentState) { + addSingleItem("enrollment_state", enrollmentState); + return this; + } + + /** + * If omitted it is set to true. When set to false it will ignore the effective state of the student enrollments + * and use the workflow_state for the enrollments. + * The argument is ignored unless enrollment_state argument is also passed. + * @param stateBasedOnDate the state of the enrollment to get submissions + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions stateBasedOnDate(Boolean stateBasedOnDate) { + addSingleItem("state_based_on_date", stateBasedOnDate.toString()); + return this; + } + + /** + * The order submissions will be returned in. + * Defaults to “id”. Doesn't affect results for “grouped” mode. + * @param order the order indicated + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions order(String order) { + addSingleItem("order", order); + return this; + } + + /** + * Determines whether ordered results are returned in ascending or descending order. + * Defaults to “ascending”. + * Doesn't affect results for “grouped” mode. + * @param orderDirection "ascending" or "descending" + * @return This object to allow adding more options + */ + public GetMultipleSubmissionsOptions orderDirection(String orderDirection) { + addSingleItem("order_direction", orderDirection); + return this; + } + + + public String getCanvasId() { + return canvasId; + } + + + public void setCanvasId(String canvasId) { + this.canvasId = canvasId; + } +} diff --git a/src/test/java/edu/ksu/canvas/tests/submission/SubmissionUTest.java b/src/test/java/edu/ksu/canvas/tests/submission/SubmissionUTest.java new file mode 100644 index 00000000..2407f76a --- /dev/null +++ b/src/test/java/edu/ksu/canvas/tests/submission/SubmissionUTest.java @@ -0,0 +1,43 @@ +package edu.ksu.canvas.tests.submission; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import edu.ksu.canvas.CanvasTestBase; +import edu.ksu.canvas.impl.SubmissionImpl; +import edu.ksu.canvas.interfaces.SubmissionReader; +import edu.ksu.canvas.model.assignment.Submission; +import edu.ksu.canvas.net.FakeRestClient; +import edu.ksu.canvas.net.Response; +import edu.ksu.canvas.requestOptions.GetMultipleSubmissionsOptions; + +public class SubmissionUTest extends CanvasTestBase { + @Autowired + private FakeRestClient fakeRestClient; + private SubmissionReader submissionReader; + + @Before + public void setupData() { + submissionReader = new SubmissionImpl(baseUrl, apiVersion, SOME_OAUTH_TOKEN, fakeRestClient, SOME_CONNECT_TIMEOUT, + SOME_READ_TIMEOUT, DEFAULT_PAGINATION_PAGE_SIZE, false); + } + + @Test + public void testGetCourseSubmissionMultipleAssignments() throws Exception { + String someCourseId = "123456"; + Response notErroredResponse = new Response(); + notErroredResponse.setErrorHappened(false); + notErroredResponse.setResponseCode(200); + String url = baseUrl + "/api/v1/courses/" + someCourseId + "/students/submissions"; + fakeRestClient.addSuccessResponse(url, "SampleJson/submission/SubmissionList.json"); + + List submissions = submissionReader.getCourseSubmissionMultipleAssignments(new GetMultipleSubmissionsOptions(someCourseId)); + Assert.assertEquals(2, submissions.size()); + Assert.assertTrue(submissions.stream().map(Submission::getGrade).filter("A-"::equals).findFirst().isPresent()); + Assert.assertTrue(submissions.stream().map(Submission::getGrade).filter("B-"::equals).findFirst().isPresent()); + } +} diff --git a/src/test/resources/SampleJson/submission/SubmissionList.json b/src/test/resources/SampleJson/submission/SubmissionList.json new file mode 100644 index 00000000..49086412 --- /dev/null +++ b/src/test/resources/SampleJson/submission/SubmissionList.json @@ -0,0 +1,66 @@ +[ + { + "assignment_id": 1, + "assignment": null, + "course": null, + "attempt": 1, + "body": "There are three factors too...", + "grade": "A-", + "grade_matches_current_submission": true, + "html_url": "http://canvas.example.com/courses/255/assignments/543/submissions/134", + "preview_url": "http://canvas.example.com/courses/255/assignments/543/submissions/134?preview=1", + "score": 13.5, + "submission_comments": null, + "submission_type": "online_text_entry", + "submitted_at": "2012-01-01T01:00:00Z", + "url": null, + "user_id": 134, + "grader_id": 86, + "graded_at": "2012-01-02T03:05:34Z", + "user": null, + "late": false, + "assignment_visible": true, + "excused": true, + "missing": true, + "late_policy_status": "missing", + "points_deducted": 12.3, + "seconds_late": 300, + "workflow_state": "submitted", + "extra_attempts": 10, + "anonymous_id": "acJ4Q", + "posted_at": "2020-01-02T11:10:30Z", + "read_status": "read" + }, + { + "assignment_id": 2, + "assignment": null, + "course": null, + "attempt": 1, + "body": "There are three factors too...", + "grade": "B-", + "grade_matches_current_submission": true, + "html_url": "http://canvas.example.com/courses/255/assignments/543/submissions/134", + "preview_url": "http://canvas.example.com/courses/255/assignments/543/submissions/134?preview=1", + "score": 14.5, + "submission_comments": null, + "submission_type": "online_text_entry", + "submitted_at": "2012-01-01T01:00:00Z", + "url": null, + "user_id": 134, + "grader_id": 86, + "graded_at": "2012-01-02T03:05:34Z", + "user": null, + "late": false, + "assignment_visible": true, + "excused": true, + "missing": true, + "late_policy_status": "missing", + "points_deducted": 12.3, + "seconds_late": 300, + "workflow_state": "submitted", + "extra_attempts": 10, + "anonymous_id": "acJ4Q", + "posted_at": "2020-01-02T11:10:30Z", + "read_status": "read" + } +] \ No newline at end of file