Skip to content

Commit 247feb0

Browse files
committed
Add AI scoring fields to Application and Review models, and update grading logic
1 parent 3c1bca8 commit 247feb0

3 files changed

Lines changed: 43 additions & 0 deletions

File tree

services/registration/src/models/application.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export interface Application extends mongoose.Document {
8888
createdAt: Date;
8989
updatedAt: Date;
9090
finalScore: number;
91+
finalAiScore?: number;
9192
}
9293

9394
const applicationSchema = new Schema<Application>(
@@ -309,6 +310,11 @@ const applicationSchema = new Schema<Application>(
309310
finalScore: {
310311
type: Number,
311312
},
313+
// Average of the graders' AI likelihood ratings across this application's reviews.
314+
// Recorded for data collection only; nothing currently acts on it.
315+
finalAiScore: {
316+
type: Number,
317+
},
312318
},
313319
{
314320
timestamps: true,

services/registration/src/models/review.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface Review extends mongoose.Document {
88
essayId: Types.ObjectId;
99
score: number;
1010
adjustedScore: number;
11+
aiScore?: number;
1112
timestamp: Date;
1213
}
1314

@@ -40,6 +41,13 @@ const reviewSchema = new Schema<Review>({
4041
type: Number,
4142
required: true,
4243
},
44+
// Grader's rating of how likely the essay was written by AI. Optional so that reviews
45+
// submitted before this field existed (and any client not yet sending it) remain valid.
46+
aiScore: {
47+
type: Number,
48+
min: 1,
49+
max: 3,
50+
},
4351
timestamp: {
4452
type: Date,
4553
required: true,

services/registration/src/routes/grading.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import { getCalibrationMapping } from "../common/adjustScores";
1515
const MAX_REVIEWS_PER_ESSAY = 2;
1616
// NOTE: No. of essays for each application. As such, will need to be updated whenever we add/remove essays.
1717
const ESSAY_COUNT = 4;
18+
// Graders rate how likely an essay was written by AI alongside the normal score, so that they can
19+
// grade the essay on its merits and record the suspicion separately.
20+
// 1 = no chance of AI, 2 = some hints and sounds kinda like AI, 3 = fairly confident this is AI.
21+
const AI_SCORE_MIN = 1;
22+
const AI_SCORE_MAX = 3;
1823

1924
type AggregatedEssay = {
2025
applicationId: string;
@@ -224,6 +229,18 @@ gradingRouter.route("/actions/submit-review").post(
224229
throw new BadRequestError("One or more of the method body arguments is missing.");
225230
}
226231

232+
// Optional so clients that haven't been updated yet can still submit reviews.
233+
const { aiScore } = req.body;
234+
if (
235+
aiScore !== undefined &&
236+
aiScore !== null &&
237+
(!Number.isInteger(aiScore) || aiScore < AI_SCORE_MIN || aiScore > AI_SCORE_MAX)
238+
) {
239+
throw new BadRequestError(
240+
`AI score must be an integer between ${AI_SCORE_MIN} and ${AI_SCORE_MAX}`
241+
);
242+
}
243+
227244
const gradingGroup = req.body.gradingGroup as GradingGroupType;
228245

229246
let criteriaScores = grader.calibrationScores.find(
@@ -294,6 +311,7 @@ gradingRouter.route("/actions/submit-review").post(
294311
score: req.body.score,
295312
timestamp: new Date(),
296313
adjustedScore,
314+
aiScore,
297315
});
298316

299317
const allEssayReviews = await ReviewModel.find({
@@ -305,6 +323,17 @@ gradingRouter.route("/actions/submit-review").post(
305323
application.gradingComplete = true;
306324
const sumScores = allEssayReviews.reduce((prev, review) => prev + review.adjustedScore, 0);
307325
application.finalScore = sumScores / allEssayReviews.length;
326+
327+
// Average only over the reviews that actually submitted an AI score, so reviews from
328+
// before this field existed don't pull the average toward zero.
329+
const aiScores = allEssayReviews
330+
.map(review => review.aiScore)
331+
.filter((score): score is number => score !== undefined && score !== null);
332+
if (aiScores.length > 0) {
333+
application.finalAiScore =
334+
aiScores.reduce((prev, score) => prev + score, 0) / aiScores.length;
335+
}
336+
308337
await application.save();
309338
}
310339

0 commit comments

Comments
 (0)