fd0133c6ae310642c716d761df8e9ec91d3fb1f8
1 const CourseModel
= require("../models/course");
2 const UserModel
= require("../models/user");
3 const ObjectId
= require("bson-objectid");
4 const TokenGen
= require("../utils/tokenGenerator");
5 const db
= require("../utils/database");
7 const AssessmentModel
=
15 * mode: secure | watch | exam | open (decreasing security)
16 * fixed: bool (questions in fixed order; default: false)
17 * display: "one" or "all" (generally "all" for open questions, but...)
18 * time: 0, global (one vaue) or per question (array of integers)
20 * coefficient: number, default 1
22 * index: for paper test, like 2.1.a (?!); and quiz: 0, 1, 2, 3...
23 * wording: varchar (HTML)
24 * options: array of varchar --> if present, question type == quiz!
25 * fixed: bool, options in fixed order (default: false)
26 * answer: array of integers (for quiz) or html text (for paper); striped in exam mode
27 * active: boolean, is question in current assessment?
28 * points: points for this question (default 1)
30 * number: student number
31 * inputs: array of {index,answer[array of integers or html text],startTime}
32 * current: index of current question (if relevant: display="one")
34 * discoTime, totalDisco: last disconnect timestamp (if relevant) + total time
35 * discoCount: number of disconnections
36 * password: random string identifying student for exam session TEMPORARY
42 getById: function(aid
, callback
)
44 db
.assessments
.findOne(
50 getByPath: function(cid
, name
, callback
)
52 db
.assessments
.findOne(
61 insert: function(cid
, name
, callback
)
63 db
.assessments
.insert(
81 getByCourse: function(cid
, callback
)
89 // arg: full assessment without _id field
90 replace: function(aid
, assessment
, cb
)
92 // Should be: (but unsupported by mongojs)
93 // db.assessments.replaceOne(
98 // Temporary workaround:
99 db
.assessments
.update(
101 { $set: assessment
},
106 getQuestions: function(aid
, callback
)
108 db
.assessments
.findOne(
115 callback(err
, !!res
? res
.questions : null);
120 getQuestion: function(aid
, index
, callback
)
122 db
.assessments
.findOne(
130 return callback(err
, res
);
131 const qIdx
= res
.questions
.findIndex( item
=> { return item
.index
== index
; });
133 return callback({errmsg: "Question not found"}, null);
134 callback(null, res
.questions
[qIdx
]);
139 getPaperByNumber: function(aid
, number
, callback
)
141 db
.assessments
.findOne(
144 "papers.number": number
,
148 return callback(err
,a
);
149 for (let p
of a
.papers
)
151 if (p
.number
== number
)
152 return callback(null,p
); //reached for sure
158 // NOTE: no callbacks for 2 next functions, failures are not so important
159 // (because monitored: teachers can see what's going on)
161 addDisco: function(aid
, number
, deltaTime
)
163 db
.assessments
.update(
166 "papers.number": number
,
169 "papers.$.discoCount": 1,
170 "papers.$.totalDisco": deltaTime
,
172 { $set: { "papers.$.discoTime": null } }
176 setDiscoTime: function(aid
, number
)
178 db
.assessments
.update(
181 "papers.number": number
,
183 { $set: { "papers.$.discoTime": Date
.now() } }
187 getDiscoTime: function(aid
, number
, cb
)
189 db
.assessments
.findOne(
193 return cb(err
, null);
194 const idx
= a
.papers
.findIndex( item
=> { return item
.number
== number
; });
195 cb(null, a
.papers
[idx
].discoTime
);
200 hasInput: function(aid
, number
, password
, idx
, cb
)
202 db
.assessments
.findOne(
205 "papers.number": number
,
206 "papers.password": password
,
211 let papIdx
= a
.papers
.findIndex( item
=> { return item
.number
== number
; });
212 for (let i
of a
.papers
[papIdx
].inputs
)
215 return cb(null,true);
222 // https://stackoverflow.com/questions/27874469/mongodb-push-in-nested-array
223 setInput: function(aid
, number
, password
, input
, callback
) //input: index + arrayOfInt (or txt)
225 db
.assessments
.update(
228 "papers.number": number
,
229 "papers.password": password
,
231 { $push: { "papers.$.inputs": input
} },
236 endAssessment: function(aid
, number
, password
, callback
)
238 db
.assessments
.update(
241 "papers.number": number
,
242 "papers.password": password
,
245 "papers.$.endTime": Date
.now(),
246 "papers.$.password": "",
252 remove: function(aid
, cb
)
254 db
.assessments
.remove(
260 removeGroup: function(cid
, cb
)
262 db
.assessments
.remove(
268 /////////////////////
269 // ADVANCED FUNCTIONS
271 getByRefs: function(initials
, code
, name
, cb
)
273 UserModel
.getByInitials(initials
, (err
,user
) => {
275 return cb(err
|| {errmsg: "User not found"});
276 CourseModel
.getByPath(user
._id
, code
, (err2
,course
) => {
277 if (!!err2
|| !course
)
278 return cb(err2
|| {errmsg: "Course not found"});
279 AssessmentModel
.getByPath(course
._id
, name
, (err3
,assessment
) => {
280 if (!!err3
|| !assessment
)
281 return cb(err3
|| {errmsg: "Assessment not found"});
288 checkPassword: function(aid
, number
, password
, cb
)
290 AssessmentModel
.getById(aid
, (err
,assessment
) => {
291 if (!!err
|| !assessment
)
292 return cb(err
, assessment
);
293 const paperIdx
= assessment
.papers
.findIndex( item
=> { return item
.number
== number
; });
295 return cb({errmsg: "Paper not found"}, false);
296 cb(null, assessment
.papers
[paperIdx
].password
== password
);
300 add: function(uid
, cid
, name
, cb
)
302 // 1) Check that course is owned by user of ID uid
303 CourseModel
.getById(cid
, (err
,course
) => {
304 if (!!err
|| !course
)
305 return cb({errmsg: "Course retrieval failure"});
306 if (!course
.uid
.equals(uid
))
307 return cb({errmsg:"Not your course"},undefined);
308 // 2) Insert new blank assessment
309 AssessmentModel
.insert(cid
, name
, cb
);
313 update: function(uid
, assessment
, cb
)
315 const aid
= ObjectId(assessment
._id
);
316 // 1) Check that assessment is owned by user of ID uid
317 AssessmentModel
.getById(aid
, (err
,assessmentOld
) => {
318 if (!!err
|| !assessmentOld
)
319 return cb({errmsg: "Assessment retrieval failure"});
320 CourseModel
.getById(ObjectId(assessmentOld
.cid
), (err2
,course
) => {
321 if (!!err2
|| !course
)
322 return cb({errmsg: "Course retrieval failure"});
323 if (!course
.uid
.equals(uid
))
324 return cb({errmsg:"Not your course"},undefined);
325 // 2) Replace assessment
326 delete assessment
["_id"];
327 assessment
.cid
= ObjectId(assessment
.cid
);
328 AssessmentModel
.replace(aid
, assessment
, cb
);
333 // Set password in responses collection
334 startSession: function(aid
, number
, password
, cb
)
336 AssessmentModel
.getPaperByNumber(aid
, number
, (err
,paper
) => {
339 if (!paper
&& !!password
)
340 return cb({errmsg: "Cannot start a new exam before finishing current"},null);
344 return cb({errmsg: "Missing password"});
345 if (paper
.password
!= password
)
346 return cb({errmsg: "Wrong password"});
348 AssessmentModel
.getQuestions(aid
, (err2
,questions
) => {
350 return cb(err2
,null);
352 return cb(null,{paper:paper
});
353 const pwd
= TokenGen
.generate(12); //arbitrary number, 12 seems enough...
354 db
.assessments
.update(
358 startTime: Date
.now(),
363 inputs: [ ], //TODO: this is stage 1, stack indexed answers.
364 // then build JSON tree for easier access / correct
366 (err3
,ret
) => { cb(err3
,{password:password
}); }
372 newAnswer: function(aid
, number
, password
, input
, cb
)
374 // Check that student hasn't already answered
375 AssessmentModel
.hasInput(aid
, number
, password
, input
.index
, (err
,ret
) => {
379 return cb({errmsg:"Question already answered"},null);
380 AssessmentModel
.setInput(aid
, number
, password
, input
, (err2
,ret2
) => {
382 return cb(err2
,ret2
);
383 return cb(null,ret2
);
388 // NOTE: no callbacks for next function, failures are not so important
389 // (because monitored: teachers can see what's going on)
390 newConnection: function(aid
, number
)
392 //increment discoCount, reset discoTime to NULL, update totalDisco
393 AssessmentModel
.getDiscoTime(aid
, number
, (err
,discoTime
) => {
395 AssessmentModel
.addDisco(aid
, number
, Date
.now() - discoTime
);
400 module
.exports
= AssessmentModel
;