Skip to content

Commit 998b82e

Browse files
authored
Merge pull request #252 from makeopensource/104-grading-opts
104 grading opts
2 parents dcb37f0 + 1c4c142 commit 998b82e

File tree

8 files changed

+140
-7
lines changed

8 files changed

+140
-7
lines changed

devU-api/src/entities/assignment/assignment.model.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111

1212
import CourseModel from '../course/course.model'
1313

14+
import { ScoringType } from 'devu-shared-modules'
15+
1416
@Entity('assignments')
1517
export default class AssignmentModel {
1618
/**
@@ -58,6 +60,11 @@ export default class AssignmentModel {
5860
* type: string
5961
* array: true
6062
* description: filenames of stored attachments, matches the index of the fileHashes, i.e. filename[i] is the name of hash[i]
63+
* scoringType:
64+
* type: string
65+
* enum: [highest-score, latest-submission, no-score]
66+
* default: highest-score
67+
* description: Determines how the final score is chosen for the assignment
6168
*/
6269

6370
@PrimaryGeneratedColumn()
@@ -109,4 +116,12 @@ export default class AssignmentModel {
109116

110117
@Column({ name: 'attachmentsFilenames', array: true, default: [], type: 'text', nullable: false })
111118
attachmentsFilenames: string[]
119+
120+
@Column({
121+
type: 'enum',
122+
enum: ScoringType,
123+
default: ScoringType.HIGHEST_SCORE,
124+
name: 'scoring_type'
125+
})
126+
scoringType: ScoringType
112127
}

devU-api/src/entities/assignment/assignment.router.ts

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,10 @@ Router.get('/:assignmentId', asInt('assignmentId'), isAuthorizedByAssignmentStat
123123
* tags:
124124
* - Assignments
125125
* responses:
126-
* '200':
127-
* description: OK
126+
* '201':
127+
* description: Created
128+
* '400':
129+
* description: Bad Request
128130
* parameters:
129131
* - name: courseId
130132
* in: path
@@ -136,7 +138,41 @@ Router.get('/:assignmentId', asInt('assignmentId'), isAuthorizedByAssignmentStat
136138
* content:
137139
* application/x-www-form-urlencoded:
138140
* schema:
139-
* $ref: '#/components/schemas/Assignment'
141+
* type: object
142+
* required: [courseId, name, categoryName, maxFileSize, disableHandins, startDate, dueDate, endDate]
143+
* properties:
144+
* courseId:
145+
* type: integer
146+
* name:
147+
* type: string
148+
* maxLength: 128
149+
* categoryName:
150+
* type: string
151+
* maxLength: 128
152+
* description:
153+
* type: string
154+
* nullable: true
155+
* maxFileSize:
156+
* type: integer
157+
* maxSubmissions:
158+
* type: integer
159+
* nullable: true
160+
* disableHandins:
161+
* type: boolean
162+
* startDate:
163+
* type: string
164+
* format: date-time
165+
* dueDate:
166+
* type: string
167+
* format: date-time
168+
* endDate:
169+
* type: string
170+
* format: date-time
171+
* scoringType:
172+
* type: string
173+
* enum: [highest-score, latest-submission, no-score]
174+
* default: highest-score
175+
* description: Determines how the final score is chosen for the assignment
140176
*/
141177

142178

@@ -151,7 +187,9 @@ Router.post('/', isAuthorized('assignmentEditAll'), upload.array('files', 5), va
151187
* - Assignments
152188
* responses:
153189
* '200':
154-
* description: OK
190+
* description: Updated
191+
* '404':
192+
* description: Not Found
155193
* parameters:
156194
* - name: courseId
157195
* in: path
@@ -168,7 +206,37 @@ Router.post('/', isAuthorized('assignmentEditAll'), upload.array('files', 5), va
168206
* content:
169207
* application/x-www-form-urlencoded:
170208
* schema:
171-
* $ref: '#/components/schemas/Assignment'
209+
* type: object
210+
* properties:
211+
* name:
212+
* type: string
213+
* maxLength: 128
214+
* categoryName:
215+
* type: string
216+
* maxLength: 128
217+
* description:
218+
* type: string
219+
* nullable: true
220+
* maxFileSize:
221+
* type: integer
222+
* maxSubmissions:
223+
* type: integer
224+
* nullable: true
225+
* disableHandins:
226+
* type: boolean
227+
* startDate:
228+
* type: string
229+
* format: date-time
230+
* dueDate:
231+
* type: string
232+
* format: date-time
233+
* endDate:
234+
* type: string
235+
* format: date-time
236+
* scoringType:
237+
* type: string
238+
* enum: [highest-score, latest-submission, no-score]
239+
* description: Determines how the final score is chosen for the assignment
172240
*/
173241
Router.put(
174242
'/:assignmentId',

devU-api/src/entities/assignment/assignment.serializer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ export function serialize(assignment: AssignmentModel): Assignment {
1717
disableHandins: assignment.disableHandins,
1818
createdAt: assignment.createdAt.toISOString(),
1919
updatedAt: assignment.updatedAt.toISOString(),
20+
scoringType: assignment.scoringType,
2021
attachmentsHashes: assignment.attachmentsHashes,
21-
attachmentsFilenames: assignment.attachmentsFilenames
22+
attachmentsFilenames: assignment.attachmentsFilenames,
2223
}
2324
}

devU-api/src/entities/assignment/assignment.service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export async function update(assignment: Assignment) {
2828
disableHandins,
2929
attachmentsHashes,
3030
attachmentsFilenames,
31+
scoringType,
3132
} = assignment
3233

3334
if (!id) throw new Error('Missing Id')
@@ -44,6 +45,7 @@ export async function update(assignment: Assignment) {
4445
disableHandins,
4546
attachmentsHashes,
4647
attachmentsFilenames,
48+
scoringType,
4749
})
4850
}
4951

devU-api/src/entities/assignment/assignment.validator.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { check } from 'express-validator'
22

33
import validate from '../../middleware/validator/generic.validator'
44
import { isBeforeParam, isAfterParam } from '../../middleware/validator/date.validator'
5+
import { ScoringType } from 'devu-shared-modules'
56

67
const courseId = check('courseId').isNumeric()
78
const name = check('name').isString().trim().isLength({ max: 128 })
@@ -10,6 +11,10 @@ const description = check('description').isString().trim()
1011
const maxFileSize = check('maxFileSize').isNumeric()
1112
const maxSubmissions = check('maxSubmissions').isNumeric().optional({ nullable: true })
1213
const disableHandins = check('disableHandins').isBoolean()
14+
const scoringType = check('scoringType')
15+
.optional()
16+
.isIn(Object.values(ScoringType))
17+
.withMessage(`scoringType must be one of: ${Object.values(ScoringType).join(', ')}`)
1318

1419
const startDate = check('startDate')
1520
.trim()
@@ -43,6 +48,7 @@ const validator = [
4348
maxFileSize,
4449
maxSubmissions,
4550
disableHandins,
51+
scoringType,
4652
validate,
4753
]
4854

devU-api/src/entities/grader/grader.service.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
AssignmentProblem,
1212
AssignmentScore,
1313
NonContainerAutoGrader,
14+
ScoringType,
1415
Submission,
1516
SubmissionProblemScore,
1617
SubmissionScore,
@@ -25,6 +26,7 @@ import { downloadFile, initializeMinio } from '../../fileStorage'
2526
import { createNewLab, sendSubmission, waitForJob } from '../../autograders/leviathan.service'
2627
import { DockerFile, LabData, LabFile, SubmissionFile } from 'leviathan-node-sdk'
2728
import path from 'path'
29+
import assignmentService from '../assignment/assignment.service'
2830

2931
async function grade(submissionId: number) {
3032
const submissionModel = await submissionService.retrieve(submissionId)
@@ -271,11 +273,27 @@ export async function callbackFailure(assignmentId: number, submissionId: number
271273
//Currently just sets assignmentscore to the latest submission. Pulled this function out for easy future modification.
272274
async function updateAssignmentScore(submission: Submission, score: number) {
273275
const assignmentScoreModel = await assignmentScoreService.retrieveByUser(submission.assignmentId, submission.userId)
276+
const assignment = await assignmentService.retrieve(submission.assignmentId, submission.courseId)
277+
274278
if (assignmentScoreModel) {
275279
//If assignmentScore already exists, update existing entity
276280
const assignmentScore = serializeAssignmentScore(assignmentScoreModel)
277281
assignmentScore.score = score
278-
assignmentScoreService.update(assignmentScore)
282+
283+
// todo use scoring type in assignment entity, leaving this alone for now,
284+
// grader endpoint needs to be refactored
285+
switch (assignment!.scoringType) {
286+
case ScoringType.HIGHEST_SCORE:
287+
break
288+
case ScoringType.LATEST_SUBMISSION:
289+
break
290+
case ScoringType.NO_SCORE:
291+
break
292+
default:
293+
break
294+
}
295+
296+
await assignmentScoreService.update(assignmentScore)
279297
} else {
280298
//Otherwise create a new one
281299
const assignmentScore: AssignmentScore = {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { MigrationInterface, QueryRunner } from "typeorm";
2+
3+
export class AddScoringType1744570382000 implements MigrationInterface {
4+
name = 'AddScoringType1744570382000'
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(`CREATE TYPE "public"."assignments_scoring_type_enum" AS ENUM('highest-score', 'latest-submission', 'no-score')`);
8+
await queryRunner.query(`ALTER TABLE "assignments" ADD "scoring_type" "public"."assignments_scoring_type_enum" NOT NULL DEFAULT 'highest-score'`);
9+
}
10+
11+
public async down(queryRunner: QueryRunner): Promise<void> {
12+
await queryRunner.query(`ALTER TABLE "assignments" DROP COLUMN "scoring_type"`);
13+
await queryRunner.query(`DROP TYPE "public"."assignments_scoring_type_enum"`);
14+
}
15+
16+
}

devU-shared/src/types/assignment.types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
export enum ScoringType {
2+
HIGHEST_SCORE = 'highest-score',
3+
LATEST_SUBMISSION = 'latest-submission',
4+
NO_SCORE = 'no-score'
5+
}
6+
17
export type Assignment = {
28
id?: number
39
courseId: number
@@ -14,5 +20,6 @@ export type Assignment = {
1420
updatedAt?: string
1521
attachmentsHashes ?: string[]
1622
attachmentsFilenames ?: string[]
23+
scoringType?: ScoringType
1724
}
1825

0 commit comments

Comments
 (0)