@quesmed/types 2.6.209 → 2.6.210

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/cjs/models/Marksheet.d.ts +9 -1
  2. package/dist/cjs/models/Marksheet.js +7 -1
  3. package/dist/cjs/models/Question.d.ts +2 -2
  4. package/dist/cjs/resolvers/fragments/marksheet.js +4 -0
  5. package/dist/cjs/resolvers/fragments/record.d.ts +0 -1
  6. package/dist/cjs/resolvers/fragments/record.js +14 -19
  7. package/dist/cjs/resolvers/mutation/restricted/marksheet.d.ts +2 -0
  8. package/dist/cjs/resolvers/mutation/restricted/marksheet.js +10 -0
  9. package/dist/cjs/resolvers/mutation/restricted/mockTest.d.ts +3 -0
  10. package/dist/cjs/resolvers/query/admin/record.js +2 -2
  11. package/dist/cjs/resolvers/query/restricted/analytics.js +2 -0
  12. package/dist/cjs/resolvers/subscription/marksheet.d.ts +6 -4
  13. package/dist/cjs/resolvers/subscription/marksheet.js +2 -0
  14. package/dist/cjs/utils/evaluateMark.d.ts +14 -0
  15. package/dist/cjs/utils/evaluateMark.js +310 -0
  16. package/dist/cjs/utils/index.d.ts +1 -0
  17. package/dist/cjs/utils/index.js +1 -0
  18. package/dist/mjs/models/Marksheet.d.ts +9 -1
  19. package/dist/mjs/models/Marksheet.js +6 -0
  20. package/dist/mjs/models/Question.d.ts +2 -2
  21. package/dist/mjs/resolvers/fragments/marksheet.js +4 -0
  22. package/dist/mjs/resolvers/fragments/record.d.ts +0 -1
  23. package/dist/mjs/resolvers/fragments/record.js +13 -18
  24. package/dist/mjs/resolvers/mutation/restricted/marksheet.d.ts +2 -0
  25. package/dist/mjs/resolvers/mutation/restricted/marksheet.js +10 -0
  26. package/dist/mjs/resolvers/mutation/restricted/mockTest.d.ts +3 -0
  27. package/dist/mjs/resolvers/query/admin/record.js +2 -2
  28. package/dist/mjs/resolvers/query/restricted/analytics.js +2 -0
  29. package/dist/mjs/resolvers/subscription/marksheet.d.ts +6 -4
  30. package/dist/mjs/resolvers/subscription/marksheet.js +2 -0
  31. package/dist/mjs/utils/evaluateMark.d.ts +14 -0
  32. package/dist/mjs/utils/evaluateMark.js +306 -0
  33. package/dist/mjs/utils/index.d.ts +1 -0
  34. package/dist/mjs/utils/index.js +1 -0
  35. package/package.json +1 -1
@@ -76,6 +76,7 @@ export interface IMarksheet {
76
76
  isTestMarksheet: boolean;
77
77
  correct?: number;
78
78
  incorrect?: number;
79
+ partial?: number;
79
80
  totalQuestions?: number;
80
81
  sessionId: string;
81
82
  passingMark: number;
@@ -93,7 +94,12 @@ export interface IMarksheet {
93
94
  builderConfig?: IBuildConfigData;
94
95
  entitlementId: EProductType | EEntitlementType;
95
96
  }
96
- export type IMarksheetMarkJSONB = string | [string] | [IQuestionQAAnswer] | [string[], string[]] | [IPrescribeMark] | null;
97
+ export type IMarksheetMarkJSONB = string | [string] | string[] | [IQuestionQAAnswer] | [string[], string[]] | [IPrescribeMark] | [string, string, string] | [string, string] | [string, string][] | null;
98
+ export declare enum EMarkResult {
99
+ Incorrect = 0,
100
+ Correct = 1,
101
+ Partial = 2
102
+ }
97
103
  export interface IMarksheetMark {
98
104
  id: Id;
99
105
  createdAt: number | Date;
@@ -110,6 +116,8 @@ export interface IMarksheetMark {
110
116
  marksheet?: IMarksheet;
111
117
  isAnswered: boolean;
112
118
  striked: number[];
119
+ score: number;
120
+ result: EMarkResult;
113
121
  }
114
122
  export interface IPreBuildMarksheetRef {
115
123
  createdAt: number | Date;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EMarksheetAction = exports.EMarksheetType = exports.EStudyAction = exports.MARKSHEET_SOURCE = exports.EMarksheetState = void 0;
3
+ exports.EMarkResult = exports.EMarksheetAction = exports.EMarksheetType = exports.EStudyAction = exports.MARKSHEET_SOURCE = exports.EMarksheetState = void 0;
4
4
  var EMarksheetState;
5
5
  (function (EMarksheetState) {
6
6
  EMarksheetState[EMarksheetState["LOBBY"] = 0] = "LOBBY";
@@ -50,3 +50,9 @@ var EMarksheetAction;
50
50
  EMarksheetAction[EMarksheetAction["ANSWER_SELECTED"] = 15] = "ANSWER_SELECTED";
51
51
  EMarksheetAction[EMarksheetAction["MARK_CHECK_UNCHECK"] = 16] = "MARK_CHECK_UNCHECK";
52
52
  })(EMarksheetAction = exports.EMarksheetAction || (exports.EMarksheetAction = {}));
53
+ var EMarkResult;
54
+ (function (EMarkResult) {
55
+ EMarkResult[EMarkResult["Incorrect"] = 0] = "Incorrect";
56
+ EMarkResult[EMarkResult["Correct"] = 1] = "Correct";
57
+ EMarkResult[EMarkResult["Partial"] = 2] = "Partial";
58
+ })(EMarkResult = exports.EMarkResult || (exports.EMarkResult = {}));
@@ -77,7 +77,7 @@ export interface IQuestionCommentLike {
77
77
  comment: IQuestionComment;
78
78
  likeTrueDislikeFalse: boolean;
79
79
  }
80
- export type IQuestionAnswer = string | [string] | [IQuestionQAAnswer] | [string[], string[]] | IPrescribeMark[] | [string, string][] | [string, string, string] | string[] | [string, string];
80
+ export type IQuestionAnswer = string | [string] | [IQuestionQAAnswer] | [string[], string[]] | IPrescribeAnswer[] | [string, string][] | [string, string, string] | string[] | [string, string];
81
81
  export type IQuestionAll = IQuestion | IQuestionSBA | IQuestionQA | IQuestionMultiQ | IQuestionPrescribe | IQuestionEMQ | IQuestionSelect3 | IQuestionRanking | IQuestionSjtTwoAnswer;
82
82
  export interface IQuestion {
83
83
  id: Id;
@@ -207,7 +207,7 @@ export interface IQuestionSjtTwoAnswer extends IQuestion {
207
207
  sjtTwoAnswer: [string, string];
208
208
  }
209
209
  export interface IQuestionPrescribe extends IQuestion {
210
- answer: IPrescribeMark[];
210
+ answer: IPrescribeAnswer[];
211
211
  prescribeAnswer: IPrescribeAnswer[];
212
212
  }
213
213
  export type IPrescribeAnswerData = {
@@ -15,6 +15,8 @@ exports.MARKSHEET_MARK_FIELDS = (0, client_1.gql) `
15
15
  isAnswered
16
16
  striked
17
17
  mark
18
+ result
19
+ score
18
20
  questionId
19
21
  question {
20
22
  ...QuestionFields
@@ -144,5 +146,7 @@ exports.NEW_MARK_FIELDS = (0, client_1.gql) `
144
146
  questionChoiceId
145
147
  mark
146
148
  isAnswered
149
+ result
150
+ score
147
151
  }
148
152
  `;
@@ -3,7 +3,6 @@ export declare const JOB_REMARK_FRAGMENT: import("@apollo/client").DocumentNode;
3
3
  export declare const JOB_ASSET_FRAGMENT: import("@apollo/client").DocumentNode;
4
4
  export declare const JOB_RECORD_METRICS_SUMMARY_FRAGMENT: import("@apollo/client").DocumentNode;
5
5
  export declare const JOB_RECORD_CHOICE_SUMMARY_FRAGMENT: import("@apollo/client").DocumentNode;
6
- export declare const JOB_RECORD_STATISTICS_FRAGMENT: import("@apollo/client").DocumentNode;
7
6
  export declare const JOB_RECORD_QUESTION_FRAGMENT: import("@apollo/client").DocumentNode;
8
7
  export declare const JOB_RECORD_CHAPTER_FRAGMENT: import("@apollo/client").DocumentNode;
9
8
  export declare const JOB_RECORD_STATION_FRAGMENT: import("@apollo/client").DocumentNode;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JOB_RECORD_FRAGMENT = exports.JOB_RECORD_STATUS_FRAGMENT = exports.JOB_RECORD_MOCK_TEST_FRAGMENT = exports.JOB_RECORD_STATION_FRAGMENT = exports.JOB_RECORD_CHAPTER_FRAGMENT = exports.JOB_RECORD_QUESTION_FRAGMENT = exports.JOB_RECORD_STATISTICS_FRAGMENT = exports.JOB_RECORD_CHOICE_SUMMARY_FRAGMENT = exports.JOB_RECORD_METRICS_SUMMARY_FRAGMENT = exports.JOB_ASSET_FRAGMENT = exports.JOB_REMARK_FRAGMENT = exports.JOB_USER_FRAGMENT = void 0;
3
+ exports.JOB_RECORD_FRAGMENT = exports.JOB_RECORD_STATUS_FRAGMENT = exports.JOB_RECORD_MOCK_TEST_FRAGMENT = exports.JOB_RECORD_STATION_FRAGMENT = exports.JOB_RECORD_CHAPTER_FRAGMENT = exports.JOB_RECORD_QUESTION_FRAGMENT = exports.JOB_RECORD_CHOICE_SUMMARY_FRAGMENT = exports.JOB_RECORD_METRICS_SUMMARY_FRAGMENT = exports.JOB_ASSET_FRAGMENT = exports.JOB_REMARK_FRAGMENT = exports.JOB_USER_FRAGMENT = void 0;
4
4
  const client_1 = require("@apollo/client");
5
5
  exports.JOB_USER_FRAGMENT = (0, client_1.gql) `
6
6
  fragment JobUser on User {
@@ -63,10 +63,6 @@ exports.JOB_RECORD_METRICS_SUMMARY_FRAGMENT = (0, client_1.gql) `
63
63
  correct
64
64
  incorrect
65
65
  percentage
66
- commentCount
67
- likes
68
- dislikes
69
- public
70
66
  }
71
67
  `;
72
68
  exports.JOB_RECORD_CHOICE_SUMMARY_FRAGMENT = (0, client_1.gql) `
@@ -78,18 +74,18 @@ exports.JOB_RECORD_CHOICE_SUMMARY_FRAGMENT = (0, client_1.gql) `
78
74
  questionId
79
75
  }
80
76
  `;
81
- exports.JOB_RECORD_STATISTICS_FRAGMENT = (0, client_1.gql) `
82
- ${exports.JOB_RECORD_METRICS_SUMMARY_FRAGMENT}
83
- ${exports.JOB_RECORD_CHOICE_SUMMARY_FRAGMENT}
84
- fragment JobRecordStatistics on JobRecord {
85
- metricsSummary {
86
- ...JobRecordMetricsSummary
87
- }
88
- choiceSummary {
89
- ...JobRecordChoiceSummary
90
- }
91
- }
92
- `;
77
+ // export const JOB_RECORD_STATISTICS_FRAGMENT = gql`
78
+ // ${JOB_RECORD_METRICS_SUMMARY_FRAGMENT}
79
+ // ${JOB_RECORD_CHOICE_SUMMARY_FRAGMENT}
80
+ // fragment JobRecordStatistics on JobRecord {
81
+ // metricsSummary {
82
+ // ...JobRecordMetricsSummary
83
+ // }
84
+ // choiceSummary {
85
+ // ...JobRecordChoiceSummary
86
+ // }
87
+ // }
88
+ // `;
93
89
  exports.JOB_RECORD_QUESTION_FRAGMENT = (0, client_1.gql) `
94
90
  fragment JobRecordQuestion on JobRecordQuestion {
95
91
  question
@@ -170,7 +166,6 @@ exports.JOB_RECORD_FRAGMENT = (0, client_1.gql) `
170
166
  ${exports.JOB_RECORD_CHAPTER_FRAGMENT}
171
167
  ${exports.JOB_RECORD_STATION_FRAGMENT}
172
168
  ${exports.JOB_RECORD_MOCK_TEST_FRAGMENT}
173
- ${exports.JOB_RECORD_STATISTICS_FRAGMENT}
174
169
  ${exports.JOB_ASSET_FRAGMENT}
175
170
  fragment JobRecord on JobRecord {
176
171
  id
@@ -208,6 +203,6 @@ exports.JOB_RECORD_FRAGMENT = (0, client_1.gql) `
208
203
  updatedAt
209
204
  publishedAt
210
205
  ...JobRecordStatus
211
- ...JobRecordStatistics
206
+ # ...JobRecordStatistics
212
207
  }
213
208
  `;
@@ -190,6 +190,7 @@ export declare const optimisticModifyMarksheetBuilderConfig: (marksheetId: numbe
190
190
  isTestMarksheet: boolean;
191
191
  correct?: number | undefined;
192
192
  incorrect?: number | undefined;
193
+ partial?: number | undefined;
193
194
  totalQuestions?: number | undefined;
194
195
  sessionId: string;
195
196
  passingMark: number;
@@ -283,6 +284,7 @@ export declare const optimisticEndMarksheet: (marksheet: IMarksheet) => {
283
284
  isTestMarksheet: boolean;
284
285
  correct?: number | undefined;
285
286
  incorrect?: number | undefined;
287
+ partial?: number | undefined;
286
288
  totalQuestions?: number | undefined;
287
289
  sessionId: string;
288
290
  passingMark: number;
@@ -70,6 +70,8 @@ exports.SAVE_MARKSHEET = (0, client_1.gql) `
70
70
  state
71
71
  marks {
72
72
  isAnswered
73
+ result
74
+ score
73
75
  id
74
76
  marksheetId
75
77
  questionChoiceId
@@ -103,6 +105,8 @@ const updateMarksheets = (cache, result, options) => {
103
105
  mark: marksheet.mark,
104
106
  questionChoiceId: marksheet.choiceId,
105
107
  isAnswered: true,
108
+ result: 0,
109
+ score: 0,
106
110
  },
107
111
  fragment: marksheet_1.NEW_MARK_FIELDS,
108
112
  });
@@ -123,6 +127,8 @@ const optimisticSaveMarksheets = (marksheet, marksheetInput, questionIndex) => {
123
127
  questionChoiceId: choiceId ?? null,
124
128
  isAnswered: true,
125
129
  mark: mark || null,
130
+ result: 0,
131
+ score: 0,
126
132
  };
127
133
  const updatedMarks = [
128
134
  ...marks.slice(0, questionIndex),
@@ -345,6 +351,8 @@ exports.MODIFY_MARKSHEET_MARK = (0, client_1.gql) `
345
351
  questionChoiceId
346
352
  timeTaken
347
353
  mark
354
+ result
355
+ score
348
356
  isAnswered
349
357
  striked
350
358
  }
@@ -453,6 +461,8 @@ exports.TOGGLE_STRIKE_OPTIONS = (0, client_1.gql) `
453
461
  index
454
462
  isAnswered
455
463
  mark
464
+ result
465
+ score
456
466
  marksheetId
457
467
  question {
458
468
  id
@@ -29,6 +29,8 @@ export declare const optimisticToggleFlaggedQuestion: (mark: IMarksheetMark, fla
29
29
  marksheet?: IMarksheet | undefined;
30
30
  isAnswered: boolean;
31
31
  striked: number[];
32
+ score: number;
33
+ result: import("../../../models").EMarkResult;
32
34
  };
33
35
  };
34
36
  };
@@ -64,6 +66,7 @@ export declare const optimisticEndMockTest: (marksheet: IMarksheet) => {
64
66
  isTestMarksheet: boolean;
65
67
  correct?: number | undefined;
66
68
  incorrect?: number | undefined;
69
+ partial?: number | undefined;
67
70
  totalQuestions?: number | undefined;
68
71
  sessionId: string;
69
72
  passingMark: number;
@@ -5,9 +5,9 @@ const client_1 = require("@apollo/client");
5
5
  const fragments_1 = require("../../fragments");
6
6
  exports.GET_JOB_RECORDS = (0, client_1.gql) `
7
7
  ${fragments_1.JOB_RECORD_FRAGMENT}
8
- query GetJobRecords($jobId: ID!, $filter: JobRecordsFilterInput!) {
8
+ query GetJobRecords($jobId: ID!) {
9
9
  admin {
10
- getJobRecords(jobId: $jobId, filter: $filter) {
10
+ getJobRecords(jobId: $jobId) {
11
11
  ...JobRecord
12
12
  }
13
13
  }
@@ -79,6 +79,8 @@ exports.UCAT_THEME_ANALYTICS = (0, client_1.gql) `
79
79
  correct
80
80
  incorrect
81
81
  marks {
82
+ id
83
+ index
82
84
  questionId
83
85
  updatedAt
84
86
  mark
@@ -1,4 +1,4 @@
1
- import { EMarksheetAction, EMarksheetState, Id, IMarksheetMarkJSONB, IUser } from '../../models';
1
+ import { EMarksheetAction, EMarksheetState, Id, IMarksheetMark, IUser } from '../../models';
2
2
  import { IBuildConfigData, IPreBuildMarksheet } from '../mutation/restricted';
3
3
  import { RootData } from '../types';
4
4
  export interface IMarksheetGroupMember {
@@ -58,12 +58,14 @@ export interface IMarksheetMarkData {
58
58
  newMarkId?: Id;
59
59
  markIndex?: number;
60
60
  questionChoiceId?: Id;
61
- mark?: IMarksheetMarkJSONB;
61
+ mark?: IMarksheetMark['mark'];
62
62
  isAnswered?: boolean;
63
- timeTaken?: number;
63
+ score?: IMarksheetMark['score'];
64
+ result?: IMarksheetMark['result'];
65
+ timeTaken?: IMarksheetMark['timeTaken'];
64
66
  updatedAt?: Date;
65
67
  user: IUser;
66
- striked?: number[];
68
+ striked?: IMarksheetMark['striked'];
67
69
  }
68
70
  export type IOnMarksheetMarkChangeData = RootData<IMarksheetMarkData, 'onMarksheetMarkChange'>;
69
71
  export declare const ON_MARKSHEET_MARK_CHANGE_KEY = "ON_MARKSHEET_CHANGE_KEY";
@@ -79,6 +79,8 @@ exports.ON_MARKSHEET_MARK_CHANGE = (0, client_1.gql) `
79
79
  displayName
80
80
  }
81
81
  striked
82
+ score
83
+ result
82
84
  }
83
85
  }
84
86
  `;
@@ -0,0 +1,14 @@
1
+ import { EMarkResult, EPsaSectionType, EQuestionType, IMarksheetMarkJSONB, IQuestionAnswer } from '../models';
2
+ export interface IEvaluateMarkData {
3
+ score: number;
4
+ result: EMarkResult;
5
+ }
6
+ export declare function evaluateMark({ mark, answer, questionTypeId, psaSectionId, choices, }: {
7
+ mark: IMarksheetMarkJSONB;
8
+ answer: IQuestionAnswer;
9
+ questionTypeId: EQuestionType;
10
+ psaSectionId: EPsaSectionType | null;
11
+ choices?: Array<{
12
+ label: string;
13
+ }>;
14
+ }): IEvaluateMarkData;
@@ -0,0 +1,310 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.evaluateMark = void 0;
4
+ const models_1 = require("../models");
5
+ const object_1 = require("./object");
6
+ function isPrescribeAnswer(data) {
7
+ return Object(data).hasOwnProperty('dose');
8
+ }
9
+ function mapPrescribeMarkToAnswer(obj) {
10
+ if (isPrescribeAnswer(obj)) {
11
+ return {
12
+ drugId: obj.drug.value,
13
+ doseId: obj.dose.value,
14
+ durationId: obj.duration.value,
15
+ frequencyId: obj.frequency.value,
16
+ routeId: obj.route.value,
17
+ };
18
+ }
19
+ return {
20
+ drugId: obj.drugId,
21
+ doseId: obj.doseId,
22
+ durationId: obj.durationId,
23
+ frequencyId: obj.frequencyId,
24
+ routeId: obj.routeId,
25
+ };
26
+ }
27
+ function createHashMap(arr) {
28
+ const map = new Map();
29
+ for (const innerArr of arr) {
30
+ const sortedInnerArr = innerArr.slice().sort();
31
+ const key = sortedInnerArr.join('|');
32
+ map.set(key, (map.get(key) || 0) + 1);
33
+ }
34
+ return map;
35
+ }
36
+ function deepParse(data) {
37
+ let parsed = data;
38
+ while (typeof parsed === 'string') {
39
+ try {
40
+ parsed = JSON.parse(parsed);
41
+ }
42
+ catch (e) {
43
+ break;
44
+ }
45
+ }
46
+ return parsed;
47
+ }
48
+ function evaluateMark({ mark, answer, questionTypeId, psaSectionId, choices, }) {
49
+ const data = {
50
+ score: 0,
51
+ result: models_1.EMarkResult.Incorrect,
52
+ };
53
+ if (!mark || !answer) {
54
+ return data;
55
+ }
56
+ try {
57
+ const flatAnswer = answer[0];
58
+ const jsonAnswer = deepParse(mark);
59
+ const flatAttempt = jsonAnswer[0];
60
+ switch (questionTypeId) {
61
+ case models_1.EQuestionType.SINGLE_BEST_ANSWER:
62
+ case models_1.EQuestionType.SBA_WITH_MULTIPLE_CHOICES:
63
+ case models_1.EQuestionType.VERBAL_VERACITY:
64
+ case models_1.EQuestionType.VERBAL_READING_COMPREHENSION:
65
+ case models_1.EQuestionType.DECISION_MAKING_SBA:
66
+ case models_1.EQuestionType.QUANTITATIVE_REASONING_SBA: {
67
+ if (flatAnswer === flatAttempt) {
68
+ data.score = psaSectionId ? 2 : 1;
69
+ data.result = models_1.EMarkResult.Correct;
70
+ }
71
+ else {
72
+ data.score = 0;
73
+ data.result = models_1.EMarkResult.Incorrect;
74
+ }
75
+ break;
76
+ }
77
+ case models_1.EQuestionType.QUESTION_ANSWER: {
78
+ const normalizedAnswer = flatAnswer.dose
79
+ .toLowerCase()
80
+ .replace(/\s/g, '');
81
+ const normalizedAttempt = flatAttempt
82
+ ? flatAttempt.toLowerCase().replace(/\s/g, '')
83
+ : '';
84
+ if (normalizedAnswer === normalizedAttempt) {
85
+ // for psa section, we give 2 marks for correct answer
86
+ data.score = psaSectionId ? 2 : 1;
87
+ data.result = models_1.EMarkResult.Correct;
88
+ }
89
+ else {
90
+ data.score = 0;
91
+ data.result = models_1.EMarkResult.Incorrect;
92
+ }
93
+ break;
94
+ }
95
+ case models_1.EQuestionType.MULTIPLE_ANSWERS: {
96
+ const [answerA, answerB] = answer.map((x) => x.sort());
97
+ let [attemptA, attemptB] = [[], []];
98
+ if (Array.isArray(jsonAnswer) &&
99
+ jsonAnswer.length === 2 &&
100
+ Array.isArray(jsonAnswer[0]) &&
101
+ Array.isArray(jsonAnswer[1])) {
102
+ [attemptA, attemptB] = jsonAnswer.map((x) => x.sort());
103
+ }
104
+ const isAMatch = answerA.length === attemptA.length &&
105
+ attemptA.every((x, i) => x === answerA[i]);
106
+ const isBMatch = answerB.length === attemptB.length &&
107
+ attemptB.every((x, i) => x === answerB[i]);
108
+ if (isAMatch && isBMatch) {
109
+ data.score = 4;
110
+ data.result = models_1.EMarkResult.Correct;
111
+ }
112
+ else if (isAMatch || isBMatch) {
113
+ data.score = 2;
114
+ data.result = models_1.EMarkResult.Partial;
115
+ }
116
+ else {
117
+ data.score = 0;
118
+ data.result = models_1.EMarkResult.Incorrect;
119
+ }
120
+ break;
121
+ }
122
+ case models_1.EQuestionType.DECISION_MAKING_YES_NO: {
123
+ const [answerA, answerB] = answer.map((x) => [...x].sort());
124
+ let [attemptA, attemptB] = [[], []];
125
+ if (Array.isArray(jsonAnswer) &&
126
+ jsonAnswer.length === 2 &&
127
+ Array.isArray(jsonAnswer[0]) &&
128
+ Array.isArray(jsonAnswer[1])) {
129
+ [attemptA, attemptB] = jsonAnswer.map((x) => [...x].sort());
130
+ }
131
+ // Count correct matches
132
+ let correctCount = 0;
133
+ // Count matches in A
134
+ correctCount += attemptA.filter((x) => answerA.includes(x)).length;
135
+ // Count matches in B
136
+ correctCount += attemptB.filter((x) => answerB.includes(x)).length;
137
+ const totalStatements = answerA.length + answerB.length;
138
+ if (correctCount === totalStatements) {
139
+ // 5 / 5
140
+ data.score = 2;
141
+ data.result = models_1.EMarkResult.Correct;
142
+ }
143
+ else if (correctCount === totalStatements - 1) {
144
+ // 4 / 5
145
+ data.score = 1;
146
+ data.result = models_1.EMarkResult.Partial;
147
+ }
148
+ else {
149
+ // 3 or fewer
150
+ data.score = 0;
151
+ data.result = models_1.EMarkResult.Incorrect;
152
+ }
153
+ break;
154
+ }
155
+ case models_1.EQuestionType.PRESCRIPTION_ANSWER: {
156
+ let foundCorrect = false;
157
+ if (typeof flatAttempt === 'object' &&
158
+ Object.keys(flatAttempt).length > 0) {
159
+ const answers = answer.map(mapPrescribeMarkToAnswer);
160
+ const attempt = mapPrescribeMarkToAnswer(flatAttempt);
161
+ foundCorrect = answers.some((ans) => (0, object_1.compareObjects)(ans, attempt));
162
+ }
163
+ if (foundCorrect) {
164
+ data.score = 1;
165
+ data.result = models_1.EMarkResult.Correct;
166
+ }
167
+ else {
168
+ data.score = 0;
169
+ data.result = models_1.EMarkResult.Incorrect;
170
+ }
171
+ break;
172
+ }
173
+ case models_1.EQuestionType.EXTENDED_MATCHING_ANSWER: {
174
+ const extendedAnswer = answer;
175
+ const attempt = jsonAnswer;
176
+ if (extendedAnswer.length !== attempt.length) {
177
+ data.score = 0;
178
+ data.result = models_1.EMarkResult.Incorrect;
179
+ }
180
+ else {
181
+ const map1 = createHashMap(extendedAnswer);
182
+ const map2 = createHashMap(attempt);
183
+ let isCorrect = true;
184
+ for (const [key, value] of map1) {
185
+ if (map2.get(key) !== value) {
186
+ isCorrect = false;
187
+ break;
188
+ }
189
+ }
190
+ if (isCorrect) {
191
+ data.score = 1;
192
+ data.result = models_1.EMarkResult.Correct;
193
+ }
194
+ else {
195
+ data.score = 0;
196
+ data.result = models_1.EMarkResult.Incorrect;
197
+ }
198
+ }
199
+ break;
200
+ }
201
+ case models_1.EQuestionType.SELECT_THREE_ANSWER: {
202
+ const threeAnswer = answer;
203
+ const attempt = jsonAnswer;
204
+ if (threeAnswer.length !== 3) {
205
+ data.score = 0;
206
+ data.result = models_1.EMarkResult.Incorrect;
207
+ }
208
+ else {
209
+ const answerSet = new Set(threeAnswer);
210
+ let isCorrect = true;
211
+ for (const attemptLabel of attempt) {
212
+ if (!answerSet.has(attemptLabel)) {
213
+ isCorrect = false;
214
+ break;
215
+ }
216
+ }
217
+ if (isCorrect) {
218
+ data.score = 1;
219
+ data.result = models_1.EMarkResult.Correct;
220
+ }
221
+ else {
222
+ data.score = 0;
223
+ data.result = models_1.EMarkResult.Incorrect;
224
+ }
225
+ }
226
+ break;
227
+ }
228
+ case models_1.EQuestionType.RANKING_ANSWER: {
229
+ // Duplicate logic to Extended Matching, but they have different scoring systems
230
+ // which we may add at a later point
231
+ const rankAnswer = answer;
232
+ const attempt = jsonAnswer;
233
+ if (rankAnswer.length !== attempt.length) {
234
+ data.score = 0;
235
+ data.result = models_1.EMarkResult.Incorrect;
236
+ }
237
+ else {
238
+ let isCorrect = true;
239
+ for (let i = 0; i < rankAnswer.length; i++) {
240
+ if (rankAnswer[i] !== attempt[i]) {
241
+ isCorrect = false;
242
+ break;
243
+ }
244
+ }
245
+ if (isCorrect) {
246
+ data.score = 1;
247
+ data.result = models_1.EMarkResult.Correct;
248
+ }
249
+ else {
250
+ data.score = 0;
251
+ data.result = models_1.EMarkResult.Incorrect;
252
+ }
253
+ }
254
+ break;
255
+ }
256
+ case models_1.EQuestionType.SJT_TWO_ANSWERS: {
257
+ const twoAnswer = answer;
258
+ const attempt = jsonAnswer;
259
+ if (twoAnswer.length === 2 &&
260
+ attempt.length === 2 &&
261
+ twoAnswer.join(',') === attempt.join(',')) {
262
+ data.score = 1;
263
+ data.result = models_1.EMarkResult.Correct;
264
+ }
265
+ else {
266
+ data.score = 0;
267
+ data.result = models_1.EMarkResult.Incorrect;
268
+ }
269
+ break;
270
+ }
271
+ case models_1.EQuestionType.SJT_SINGLE_CHOICE: {
272
+ const answer = flatAnswer;
273
+ const attempt = flatAttempt;
274
+ if (!choices) {
275
+ data.score = 0;
276
+ data.result = models_1.EMarkResult.Incorrect;
277
+ break;
278
+ }
279
+ const options = choices.map((o) => o.label);
280
+ const answerIndex = options.indexOf(answer);
281
+ const attemptIndex = options.indexOf(attempt);
282
+ if (answerIndex === -1 || attemptIndex === -1) {
283
+ data.score = 0;
284
+ data.result = models_1.EMarkResult.Incorrect;
285
+ }
286
+ else {
287
+ const distance = Math.abs(answerIndex - attemptIndex);
288
+ if (distance === 0) {
289
+ data.score = 1;
290
+ data.result = models_1.EMarkResult.Correct;
291
+ }
292
+ else if (distance === 1) {
293
+ data.score = 0.5;
294
+ data.result = models_1.EMarkResult.Partial;
295
+ }
296
+ else {
297
+ data.score = 0;
298
+ data.result = models_1.EMarkResult.Incorrect;
299
+ }
300
+ }
301
+ break;
302
+ }
303
+ }
304
+ }
305
+ catch (e) {
306
+ console.error(e);
307
+ }
308
+ return data;
309
+ }
310
+ exports.evaluateMark = evaluateMark;
@@ -1,4 +1,5 @@
1
1
  export * from './commonFunctions';
2
+ export * from './evaluateMark';
2
3
  export * from './lightgallery';
3
4
  export * from './object';
4
5
  export * from './offlineLink';
@@ -19,6 +19,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
20
  exports.printDuration = exports.Uuid4 = void 0;
21
21
  __exportStar(require("./commonFunctions"), exports);
22
+ __exportStar(require("./evaluateMark"), exports);
22
23
  __exportStar(require("./lightgallery"), exports);
23
24
  __exportStar(require("./object"), exports);
24
25
  __exportStar(require("./offlineLink"), exports);
@@ -76,6 +76,7 @@ export interface IMarksheet {
76
76
  isTestMarksheet: boolean;
77
77
  correct?: number;
78
78
  incorrect?: number;
79
+ partial?: number;
79
80
  totalQuestions?: number;
80
81
  sessionId: string;
81
82
  passingMark: number;
@@ -93,7 +94,12 @@ export interface IMarksheet {
93
94
  builderConfig?: IBuildConfigData;
94
95
  entitlementId: EProductType | EEntitlementType;
95
96
  }
96
- export type IMarksheetMarkJSONB = string | [string] | [IQuestionQAAnswer] | [string[], string[]] | [IPrescribeMark] | null;
97
+ export type IMarksheetMarkJSONB = string | [string] | string[] | [IQuestionQAAnswer] | [string[], string[]] | [IPrescribeMark] | [string, string, string] | [string, string] | [string, string][] | null;
98
+ export declare enum EMarkResult {
99
+ Incorrect = 0,
100
+ Correct = 1,
101
+ Partial = 2
102
+ }
97
103
  export interface IMarksheetMark {
98
104
  id: Id;
99
105
  createdAt: number | Date;
@@ -110,6 +116,8 @@ export interface IMarksheetMark {
110
116
  marksheet?: IMarksheet;
111
117
  isAnswered: boolean;
112
118
  striked: number[];
119
+ score: number;
120
+ result: EMarkResult;
113
121
  }
114
122
  export interface IPreBuildMarksheetRef {
115
123
  createdAt: number | Date;
@@ -47,3 +47,9 @@ export var EMarksheetAction;
47
47
  EMarksheetAction[EMarksheetAction["ANSWER_SELECTED"] = 15] = "ANSWER_SELECTED";
48
48
  EMarksheetAction[EMarksheetAction["MARK_CHECK_UNCHECK"] = 16] = "MARK_CHECK_UNCHECK";
49
49
  })(EMarksheetAction || (EMarksheetAction = {}));
50
+ export var EMarkResult;
51
+ (function (EMarkResult) {
52
+ EMarkResult[EMarkResult["Incorrect"] = 0] = "Incorrect";
53
+ EMarkResult[EMarkResult["Correct"] = 1] = "Correct";
54
+ EMarkResult[EMarkResult["Partial"] = 2] = "Partial";
55
+ })(EMarkResult || (EMarkResult = {}));
@@ -77,7 +77,7 @@ export interface IQuestionCommentLike {
77
77
  comment: IQuestionComment;
78
78
  likeTrueDislikeFalse: boolean;
79
79
  }
80
- export type IQuestionAnswer = string | [string] | [IQuestionQAAnswer] | [string[], string[]] | IPrescribeMark[] | [string, string][] | [string, string, string] | string[] | [string, string];
80
+ export type IQuestionAnswer = string | [string] | [IQuestionQAAnswer] | [string[], string[]] | IPrescribeAnswer[] | [string, string][] | [string, string, string] | string[] | [string, string];
81
81
  export type IQuestionAll = IQuestion | IQuestionSBA | IQuestionQA | IQuestionMultiQ | IQuestionPrescribe | IQuestionEMQ | IQuestionSelect3 | IQuestionRanking | IQuestionSjtTwoAnswer;
82
82
  export interface IQuestion {
83
83
  id: Id;
@@ -207,7 +207,7 @@ export interface IQuestionSjtTwoAnswer extends IQuestion {
207
207
  sjtTwoAnswer: [string, string];
208
208
  }
209
209
  export interface IQuestionPrescribe extends IQuestion {
210
- answer: IPrescribeMark[];
210
+ answer: IPrescribeAnswer[];
211
211
  prescribeAnswer: IPrescribeAnswer[];
212
212
  }
213
213
  export type IPrescribeAnswerData = {
@@ -12,6 +12,8 @@ export const MARKSHEET_MARK_FIELDS = gql `
12
12
  isAnswered
13
13
  striked
14
14
  mark
15
+ result
16
+ score
15
17
  questionId
16
18
  question {
17
19
  ...QuestionFields
@@ -141,5 +143,7 @@ export const NEW_MARK_FIELDS = gql `
141
143
  questionChoiceId
142
144
  mark
143
145
  isAnswered
146
+ result
147
+ score
144
148
  }
145
149
  `;
@@ -3,7 +3,6 @@ export declare const JOB_REMARK_FRAGMENT: import("@apollo/client").DocumentNode;
3
3
  export declare const JOB_ASSET_FRAGMENT: import("@apollo/client").DocumentNode;
4
4
  export declare const JOB_RECORD_METRICS_SUMMARY_FRAGMENT: import("@apollo/client").DocumentNode;
5
5
  export declare const JOB_RECORD_CHOICE_SUMMARY_FRAGMENT: import("@apollo/client").DocumentNode;
6
- export declare const JOB_RECORD_STATISTICS_FRAGMENT: import("@apollo/client").DocumentNode;
7
6
  export declare const JOB_RECORD_QUESTION_FRAGMENT: import("@apollo/client").DocumentNode;
8
7
  export declare const JOB_RECORD_CHAPTER_FRAGMENT: import("@apollo/client").DocumentNode;
9
8
  export declare const JOB_RECORD_STATION_FRAGMENT: import("@apollo/client").DocumentNode;
@@ -60,10 +60,6 @@ export const JOB_RECORD_METRICS_SUMMARY_FRAGMENT = gql `
60
60
  correct
61
61
  incorrect
62
62
  percentage
63
- commentCount
64
- likes
65
- dislikes
66
- public
67
63
  }
68
64
  `;
69
65
  export const JOB_RECORD_CHOICE_SUMMARY_FRAGMENT = gql `
@@ -75,18 +71,18 @@ export const JOB_RECORD_CHOICE_SUMMARY_FRAGMENT = gql `
75
71
  questionId
76
72
  }
77
73
  `;
78
- export const JOB_RECORD_STATISTICS_FRAGMENT = gql `
79
- ${JOB_RECORD_METRICS_SUMMARY_FRAGMENT}
80
- ${JOB_RECORD_CHOICE_SUMMARY_FRAGMENT}
81
- fragment JobRecordStatistics on JobRecord {
82
- metricsSummary {
83
- ...JobRecordMetricsSummary
84
- }
85
- choiceSummary {
86
- ...JobRecordChoiceSummary
87
- }
88
- }
89
- `;
74
+ // export const JOB_RECORD_STATISTICS_FRAGMENT = gql`
75
+ // ${JOB_RECORD_METRICS_SUMMARY_FRAGMENT}
76
+ // ${JOB_RECORD_CHOICE_SUMMARY_FRAGMENT}
77
+ // fragment JobRecordStatistics on JobRecord {
78
+ // metricsSummary {
79
+ // ...JobRecordMetricsSummary
80
+ // }
81
+ // choiceSummary {
82
+ // ...JobRecordChoiceSummary
83
+ // }
84
+ // }
85
+ // `;
90
86
  export const JOB_RECORD_QUESTION_FRAGMENT = gql `
91
87
  fragment JobRecordQuestion on JobRecordQuestion {
92
88
  question
@@ -167,7 +163,6 @@ export const JOB_RECORD_FRAGMENT = gql `
167
163
  ${JOB_RECORD_CHAPTER_FRAGMENT}
168
164
  ${JOB_RECORD_STATION_FRAGMENT}
169
165
  ${JOB_RECORD_MOCK_TEST_FRAGMENT}
170
- ${JOB_RECORD_STATISTICS_FRAGMENT}
171
166
  ${JOB_ASSET_FRAGMENT}
172
167
  fragment JobRecord on JobRecord {
173
168
  id
@@ -205,6 +200,6 @@ export const JOB_RECORD_FRAGMENT = gql `
205
200
  updatedAt
206
201
  publishedAt
207
202
  ...JobRecordStatus
208
- ...JobRecordStatistics
203
+ # ...JobRecordStatistics
209
204
  }
210
205
  `;
@@ -190,6 +190,7 @@ export declare const optimisticModifyMarksheetBuilderConfig: (marksheetId: numbe
190
190
  isTestMarksheet: boolean;
191
191
  correct?: number | undefined;
192
192
  incorrect?: number | undefined;
193
+ partial?: number | undefined;
193
194
  totalQuestions?: number | undefined;
194
195
  sessionId: string;
195
196
  passingMark: number;
@@ -283,6 +284,7 @@ export declare const optimisticEndMarksheet: (marksheet: IMarksheet) => {
283
284
  isTestMarksheet: boolean;
284
285
  correct?: number | undefined;
285
286
  incorrect?: number | undefined;
287
+ partial?: number | undefined;
286
288
  totalQuestions?: number | undefined;
287
289
  sessionId: string;
288
290
  passingMark: number;
@@ -67,6 +67,8 @@ export const SAVE_MARKSHEET = gql `
67
67
  state
68
68
  marks {
69
69
  isAnswered
70
+ result
71
+ score
70
72
  id
71
73
  marksheetId
72
74
  questionChoiceId
@@ -100,6 +102,8 @@ export const updateMarksheets = (cache, result, options) => {
100
102
  mark: marksheet.mark,
101
103
  questionChoiceId: marksheet.choiceId,
102
104
  isAnswered: true,
105
+ result: 0,
106
+ score: 0,
103
107
  },
104
108
  fragment: NEW_MARK_FIELDS,
105
109
  });
@@ -119,6 +123,8 @@ export const optimisticSaveMarksheets = (marksheet, marksheetInput, questionInde
119
123
  questionChoiceId: choiceId ?? null,
120
124
  isAnswered: true,
121
125
  mark: mark || null,
126
+ result: 0,
127
+ score: 0,
122
128
  };
123
129
  const updatedMarks = [
124
130
  ...marks.slice(0, questionIndex),
@@ -335,6 +341,8 @@ export const MODIFY_MARKSHEET_MARK = gql `
335
341
  questionChoiceId
336
342
  timeTaken
337
343
  mark
344
+ result
345
+ score
338
346
  isAnswered
339
347
  striked
340
348
  }
@@ -441,6 +449,8 @@ export const TOGGLE_STRIKE_OPTIONS = gql `
441
449
  index
442
450
  isAnswered
443
451
  mark
452
+ result
453
+ score
444
454
  marksheetId
445
455
  question {
446
456
  id
@@ -29,6 +29,8 @@ export declare const optimisticToggleFlaggedQuestion: (mark: IMarksheetMark, fla
29
29
  marksheet?: IMarksheet | undefined;
30
30
  isAnswered: boolean;
31
31
  striked: number[];
32
+ score: number;
33
+ result: import("../../../models").EMarkResult;
32
34
  };
33
35
  };
34
36
  };
@@ -64,6 +66,7 @@ export declare const optimisticEndMockTest: (marksheet: IMarksheet) => {
64
66
  isTestMarksheet: boolean;
65
67
  correct?: number | undefined;
66
68
  incorrect?: number | undefined;
69
+ partial?: number | undefined;
67
70
  totalQuestions?: number | undefined;
68
71
  sessionId: string;
69
72
  passingMark: number;
@@ -2,9 +2,9 @@ import { gql } from '@apollo/client';
2
2
  import { JOB_RECORD_FRAGMENT } from '../../fragments';
3
3
  export const GET_JOB_RECORDS = gql `
4
4
  ${JOB_RECORD_FRAGMENT}
5
- query GetJobRecords($jobId: ID!, $filter: JobRecordsFilterInput!) {
5
+ query GetJobRecords($jobId: ID!) {
6
6
  admin {
7
- getJobRecords(jobId: $jobId, filter: $filter) {
7
+ getJobRecords(jobId: $jobId) {
8
8
  ...JobRecord
9
9
  }
10
10
  }
@@ -76,6 +76,8 @@ export const UCAT_THEME_ANALYTICS = gql `
76
76
  correct
77
77
  incorrect
78
78
  marks {
79
+ id
80
+ index
79
81
  questionId
80
82
  updatedAt
81
83
  mark
@@ -1,4 +1,4 @@
1
- import { EMarksheetAction, EMarksheetState, Id, IMarksheetMarkJSONB, IUser } from '../../models';
1
+ import { EMarksheetAction, EMarksheetState, Id, IMarksheetMark, IUser } from '../../models';
2
2
  import { IBuildConfigData, IPreBuildMarksheet } from '../mutation/restricted';
3
3
  import { RootData } from '../types';
4
4
  export interface IMarksheetGroupMember {
@@ -58,12 +58,14 @@ export interface IMarksheetMarkData {
58
58
  newMarkId?: Id;
59
59
  markIndex?: number;
60
60
  questionChoiceId?: Id;
61
- mark?: IMarksheetMarkJSONB;
61
+ mark?: IMarksheetMark['mark'];
62
62
  isAnswered?: boolean;
63
- timeTaken?: number;
63
+ score?: IMarksheetMark['score'];
64
+ result?: IMarksheetMark['result'];
65
+ timeTaken?: IMarksheetMark['timeTaken'];
64
66
  updatedAt?: Date;
65
67
  user: IUser;
66
- striked?: number[];
68
+ striked?: IMarksheetMark['striked'];
67
69
  }
68
70
  export type IOnMarksheetMarkChangeData = RootData<IMarksheetMarkData, 'onMarksheetMarkChange'>;
69
71
  export declare const ON_MARKSHEET_MARK_CHANGE_KEY = "ON_MARKSHEET_CHANGE_KEY";
@@ -76,6 +76,8 @@ export const ON_MARKSHEET_MARK_CHANGE = gql `
76
76
  displayName
77
77
  }
78
78
  striked
79
+ score
80
+ result
79
81
  }
80
82
  }
81
83
  `;
@@ -0,0 +1,14 @@
1
+ import { EMarkResult, EPsaSectionType, EQuestionType, IMarksheetMarkJSONB, IQuestionAnswer } from '../models';
2
+ export interface IEvaluateMarkData {
3
+ score: number;
4
+ result: EMarkResult;
5
+ }
6
+ export declare function evaluateMark({ mark, answer, questionTypeId, psaSectionId, choices, }: {
7
+ mark: IMarksheetMarkJSONB;
8
+ answer: IQuestionAnswer;
9
+ questionTypeId: EQuestionType;
10
+ psaSectionId: EPsaSectionType | null;
11
+ choices?: Array<{
12
+ label: string;
13
+ }>;
14
+ }): IEvaluateMarkData;
@@ -0,0 +1,306 @@
1
+ import { EMarkResult, EQuestionType, } from '../models';
2
+ import { compareObjects } from './object';
3
+ function isPrescribeAnswer(data) {
4
+ return Object(data).hasOwnProperty('dose');
5
+ }
6
+ function mapPrescribeMarkToAnswer(obj) {
7
+ if (isPrescribeAnswer(obj)) {
8
+ return {
9
+ drugId: obj.drug.value,
10
+ doseId: obj.dose.value,
11
+ durationId: obj.duration.value,
12
+ frequencyId: obj.frequency.value,
13
+ routeId: obj.route.value,
14
+ };
15
+ }
16
+ return {
17
+ drugId: obj.drugId,
18
+ doseId: obj.doseId,
19
+ durationId: obj.durationId,
20
+ frequencyId: obj.frequencyId,
21
+ routeId: obj.routeId,
22
+ };
23
+ }
24
+ function createHashMap(arr) {
25
+ const map = new Map();
26
+ for (const innerArr of arr) {
27
+ const sortedInnerArr = innerArr.slice().sort();
28
+ const key = sortedInnerArr.join('|');
29
+ map.set(key, (map.get(key) || 0) + 1);
30
+ }
31
+ return map;
32
+ }
33
+ function deepParse(data) {
34
+ let parsed = data;
35
+ while (typeof parsed === 'string') {
36
+ try {
37
+ parsed = JSON.parse(parsed);
38
+ }
39
+ catch (e) {
40
+ break;
41
+ }
42
+ }
43
+ return parsed;
44
+ }
45
+ export function evaluateMark({ mark, answer, questionTypeId, psaSectionId, choices, }) {
46
+ const data = {
47
+ score: 0,
48
+ result: EMarkResult.Incorrect,
49
+ };
50
+ if (!mark || !answer) {
51
+ return data;
52
+ }
53
+ try {
54
+ const flatAnswer = answer[0];
55
+ const jsonAnswer = deepParse(mark);
56
+ const flatAttempt = jsonAnswer[0];
57
+ switch (questionTypeId) {
58
+ case EQuestionType.SINGLE_BEST_ANSWER:
59
+ case EQuestionType.SBA_WITH_MULTIPLE_CHOICES:
60
+ case EQuestionType.VERBAL_VERACITY:
61
+ case EQuestionType.VERBAL_READING_COMPREHENSION:
62
+ case EQuestionType.DECISION_MAKING_SBA:
63
+ case EQuestionType.QUANTITATIVE_REASONING_SBA: {
64
+ if (flatAnswer === flatAttempt) {
65
+ data.score = psaSectionId ? 2 : 1;
66
+ data.result = EMarkResult.Correct;
67
+ }
68
+ else {
69
+ data.score = 0;
70
+ data.result = EMarkResult.Incorrect;
71
+ }
72
+ break;
73
+ }
74
+ case EQuestionType.QUESTION_ANSWER: {
75
+ const normalizedAnswer = flatAnswer.dose
76
+ .toLowerCase()
77
+ .replace(/\s/g, '');
78
+ const normalizedAttempt = flatAttempt
79
+ ? flatAttempt.toLowerCase().replace(/\s/g, '')
80
+ : '';
81
+ if (normalizedAnswer === normalizedAttempt) {
82
+ // for psa section, we give 2 marks for correct answer
83
+ data.score = psaSectionId ? 2 : 1;
84
+ data.result = EMarkResult.Correct;
85
+ }
86
+ else {
87
+ data.score = 0;
88
+ data.result = EMarkResult.Incorrect;
89
+ }
90
+ break;
91
+ }
92
+ case EQuestionType.MULTIPLE_ANSWERS: {
93
+ const [answerA, answerB] = answer.map((x) => x.sort());
94
+ let [attemptA, attemptB] = [[], []];
95
+ if (Array.isArray(jsonAnswer) &&
96
+ jsonAnswer.length === 2 &&
97
+ Array.isArray(jsonAnswer[0]) &&
98
+ Array.isArray(jsonAnswer[1])) {
99
+ [attemptA, attemptB] = jsonAnswer.map((x) => x.sort());
100
+ }
101
+ const isAMatch = answerA.length === attemptA.length &&
102
+ attemptA.every((x, i) => x === answerA[i]);
103
+ const isBMatch = answerB.length === attemptB.length &&
104
+ attemptB.every((x, i) => x === answerB[i]);
105
+ if (isAMatch && isBMatch) {
106
+ data.score = 4;
107
+ data.result = EMarkResult.Correct;
108
+ }
109
+ else if (isAMatch || isBMatch) {
110
+ data.score = 2;
111
+ data.result = EMarkResult.Partial;
112
+ }
113
+ else {
114
+ data.score = 0;
115
+ data.result = EMarkResult.Incorrect;
116
+ }
117
+ break;
118
+ }
119
+ case EQuestionType.DECISION_MAKING_YES_NO: {
120
+ const [answerA, answerB] = answer.map((x) => [...x].sort());
121
+ let [attemptA, attemptB] = [[], []];
122
+ if (Array.isArray(jsonAnswer) &&
123
+ jsonAnswer.length === 2 &&
124
+ Array.isArray(jsonAnswer[0]) &&
125
+ Array.isArray(jsonAnswer[1])) {
126
+ [attemptA, attemptB] = jsonAnswer.map((x) => [...x].sort());
127
+ }
128
+ // Count correct matches
129
+ let correctCount = 0;
130
+ // Count matches in A
131
+ correctCount += attemptA.filter((x) => answerA.includes(x)).length;
132
+ // Count matches in B
133
+ correctCount += attemptB.filter((x) => answerB.includes(x)).length;
134
+ const totalStatements = answerA.length + answerB.length;
135
+ if (correctCount === totalStatements) {
136
+ // 5 / 5
137
+ data.score = 2;
138
+ data.result = EMarkResult.Correct;
139
+ }
140
+ else if (correctCount === totalStatements - 1) {
141
+ // 4 / 5
142
+ data.score = 1;
143
+ data.result = EMarkResult.Partial;
144
+ }
145
+ else {
146
+ // 3 or fewer
147
+ data.score = 0;
148
+ data.result = EMarkResult.Incorrect;
149
+ }
150
+ break;
151
+ }
152
+ case EQuestionType.PRESCRIPTION_ANSWER: {
153
+ let foundCorrect = false;
154
+ if (typeof flatAttempt === 'object' &&
155
+ Object.keys(flatAttempt).length > 0) {
156
+ const answers = answer.map(mapPrescribeMarkToAnswer);
157
+ const attempt = mapPrescribeMarkToAnswer(flatAttempt);
158
+ foundCorrect = answers.some((ans) => compareObjects(ans, attempt));
159
+ }
160
+ if (foundCorrect) {
161
+ data.score = 1;
162
+ data.result = EMarkResult.Correct;
163
+ }
164
+ else {
165
+ data.score = 0;
166
+ data.result = EMarkResult.Incorrect;
167
+ }
168
+ break;
169
+ }
170
+ case EQuestionType.EXTENDED_MATCHING_ANSWER: {
171
+ const extendedAnswer = answer;
172
+ const attempt = jsonAnswer;
173
+ if (extendedAnswer.length !== attempt.length) {
174
+ data.score = 0;
175
+ data.result = EMarkResult.Incorrect;
176
+ }
177
+ else {
178
+ const map1 = createHashMap(extendedAnswer);
179
+ const map2 = createHashMap(attempt);
180
+ let isCorrect = true;
181
+ for (const [key, value] of map1) {
182
+ if (map2.get(key) !== value) {
183
+ isCorrect = false;
184
+ break;
185
+ }
186
+ }
187
+ if (isCorrect) {
188
+ data.score = 1;
189
+ data.result = EMarkResult.Correct;
190
+ }
191
+ else {
192
+ data.score = 0;
193
+ data.result = EMarkResult.Incorrect;
194
+ }
195
+ }
196
+ break;
197
+ }
198
+ case EQuestionType.SELECT_THREE_ANSWER: {
199
+ const threeAnswer = answer;
200
+ const attempt = jsonAnswer;
201
+ if (threeAnswer.length !== 3) {
202
+ data.score = 0;
203
+ data.result = EMarkResult.Incorrect;
204
+ }
205
+ else {
206
+ const answerSet = new Set(threeAnswer);
207
+ let isCorrect = true;
208
+ for (const attemptLabel of attempt) {
209
+ if (!answerSet.has(attemptLabel)) {
210
+ isCorrect = false;
211
+ break;
212
+ }
213
+ }
214
+ if (isCorrect) {
215
+ data.score = 1;
216
+ data.result = EMarkResult.Correct;
217
+ }
218
+ else {
219
+ data.score = 0;
220
+ data.result = EMarkResult.Incorrect;
221
+ }
222
+ }
223
+ break;
224
+ }
225
+ case EQuestionType.RANKING_ANSWER: {
226
+ // Duplicate logic to Extended Matching, but they have different scoring systems
227
+ // which we may add at a later point
228
+ const rankAnswer = answer;
229
+ const attempt = jsonAnswer;
230
+ if (rankAnswer.length !== attempt.length) {
231
+ data.score = 0;
232
+ data.result = EMarkResult.Incorrect;
233
+ }
234
+ else {
235
+ let isCorrect = true;
236
+ for (let i = 0; i < rankAnswer.length; i++) {
237
+ if (rankAnswer[i] !== attempt[i]) {
238
+ isCorrect = false;
239
+ break;
240
+ }
241
+ }
242
+ if (isCorrect) {
243
+ data.score = 1;
244
+ data.result = EMarkResult.Correct;
245
+ }
246
+ else {
247
+ data.score = 0;
248
+ data.result = EMarkResult.Incorrect;
249
+ }
250
+ }
251
+ break;
252
+ }
253
+ case EQuestionType.SJT_TWO_ANSWERS: {
254
+ const twoAnswer = answer;
255
+ const attempt = jsonAnswer;
256
+ if (twoAnswer.length === 2 &&
257
+ attempt.length === 2 &&
258
+ twoAnswer.join(',') === attempt.join(',')) {
259
+ data.score = 1;
260
+ data.result = EMarkResult.Correct;
261
+ }
262
+ else {
263
+ data.score = 0;
264
+ data.result = EMarkResult.Incorrect;
265
+ }
266
+ break;
267
+ }
268
+ case EQuestionType.SJT_SINGLE_CHOICE: {
269
+ const answer = flatAnswer;
270
+ const attempt = flatAttempt;
271
+ if (!choices) {
272
+ data.score = 0;
273
+ data.result = EMarkResult.Incorrect;
274
+ break;
275
+ }
276
+ const options = choices.map((o) => o.label);
277
+ const answerIndex = options.indexOf(answer);
278
+ const attemptIndex = options.indexOf(attempt);
279
+ if (answerIndex === -1 || attemptIndex === -1) {
280
+ data.score = 0;
281
+ data.result = EMarkResult.Incorrect;
282
+ }
283
+ else {
284
+ const distance = Math.abs(answerIndex - attemptIndex);
285
+ if (distance === 0) {
286
+ data.score = 1;
287
+ data.result = EMarkResult.Correct;
288
+ }
289
+ else if (distance === 1) {
290
+ data.score = 0.5;
291
+ data.result = EMarkResult.Partial;
292
+ }
293
+ else {
294
+ data.score = 0;
295
+ data.result = EMarkResult.Incorrect;
296
+ }
297
+ }
298
+ break;
299
+ }
300
+ }
301
+ }
302
+ catch (e) {
303
+ console.error(e);
304
+ }
305
+ return data;
306
+ }
@@ -1,4 +1,5 @@
1
1
  export * from './commonFunctions';
2
+ export * from './evaluateMark';
2
3
  export * from './lightgallery';
3
4
  export * from './object';
4
5
  export * from './offlineLink';
@@ -1,4 +1,5 @@
1
1
  export * from './commonFunctions';
2
+ export * from './evaluateMark';
2
3
  export * from './lightgallery';
3
4
  export * from './object';
4
5
  export * from './offlineLink';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quesmed/types",
3
- "version": "2.6.209",
3
+ "version": "2.6.210",
4
4
  "description": "Typescript types for Quesmed",
5
5
  "keywords": [
6
6
  "quesmed",