Merge forename + name into [identifying] 'name' (more general)
[qomet.git] / entities / assessment.js
1 const db = require("../utils/database");
2
3 const AssessmentEntity =
4 {
5 /*
6 * Structure:
7 * _id: BSON id
8 * cid: course ID
9 * name: varchar
10 * active: boolean true/false
11 * mode: secure | exam | open (decreasing security)
12 * fixed: bool (questions in fixed order; default: false)
13 * display: "one" or "all" (generally "all" for open questions, but...)
14 * time: 0, //<=0 means "untimed"; otherwise, time in seconds
15 * introduction: "",
16 * conclusion: "https://www.youtube.com/watch?v=6-9AjToJYuw",
17 * coefficient: number, default 1
18 * questions: array of
19 * index: for paper test, like 2.1.a (?!); and quiz: 0, 1, 2, 3...
20 * wording: varchar (HTML)
21 * options: array of varchar --> if present, question type == quiz!
22 * fixed: bool, options in fixed order (default: false)
23 * answer: array of integers (for quiz) or html text (for paper); striped in exam mode
24 * active: boolean, is question in current assessment? --> striped if inactive!
25 * points: points for this question (default 1)
26 * papers : array of
27 * number: student number
28 * inputs: array of indexed arrays of integers (or html text if not quiz)
29 * startTime, endTime
30 * discoTime, totalDisco: last disconnect timestamp (if relevant) + total
31 * discoCount: total disconnections
32 * password: random string identifying student for exam session TEMPORARY
33 */
34
35 getById: function(aid, callback)
36 {
37 db.assessments.findOne(
38 { _id: aid },
39 callback
40 );
41 },
42
43 getByPath: function(cid, name, callback)
44 {
45 db.assessments.findOne(
46 {
47 cid: cid,
48 name: name,
49 },
50 callback
51 );
52 },
53
54 insert: function(cid, name, callback)
55 {
56 db.assessments.insert(
57 {
58 name: name,
59 cid: cid,
60 active: false,
61 mode: "exam",
62 fixed: false,
63 display: "one",
64 time: 0,
65 introduction: "",
66 conclusion: "",
67 coefficient: 1,
68 questions: [ ],
69 papers: [ ],
70 },
71 callback
72 );
73 },
74
75 getByCourse: function(cid, callback)
76 {
77 db.assessments.find(
78 { cid: cid },
79 callback
80 );
81 },
82
83 // arg: full assessment without _id field
84 replace: function(aid, assessment, cb)
85 {
86 // Should be: (but unsupported by mongojs)
87 // db.assessments.replaceOne(
88 // { _id: aid },
89 // assessment,
90 // cb
91 // );
92 // Temporary workaround:
93 db.assessments.update(
94 { _id: aid },
95 { $set: assessment },
96 cb
97 );
98 },
99
100 getQuestions: function(aid, callback)
101 {
102 db.assessments.findOne(
103 { _id: aid },
104 { questions: 1},
105 (err,res) => {
106 callback(err, !!res ? res.questions : null);
107 }
108 );
109 },
110
111 getPaperByNumber: function(aid, number, callback)
112 {
113 db.assessments.findOne(
114 {
115 _id: aid,
116 "papers.number": number,
117 },
118 (err,a) => {
119 if (!!err || !a)
120 return callback(err,a);
121 for (let p of a.papers)
122 {
123 if (p.number == number)
124 return callback(null,p); //reached for sure
125 }
126 }
127 );
128 },
129
130 startSession: function(aid, number, password, callback)
131 {
132 db.assessments.update(
133 { _id: aid },
134 { $push: { papers: {
135 number: number,
136 startTime: Date.now(),
137 endTime: undefined,
138 password: password,
139 totalDisco: 0,
140 discoCount: 0,
141 inputs: [ ], //TODO: this is stage 1, stack indexed answers.
142 // then build JSON tree for easier access / correct
143 }}},
144 callback
145 );
146 },
147
148 // NOTE: no callbacks for 2 next functions, failures are not so important
149 // (because monitored: teachers can see what's going on)
150
151 addDisco: function(aid, number, deltaTime)
152 {
153 db.assessments.update(
154 {
155 _id: aid,
156 "papers.number": number,
157 },
158 { $inc: {
159 "papers.$.discoCount": 1,
160 "papers.$.totalDisco": deltaTime,
161 } },
162 { $set: { "papers.$.discoTime": null } }
163 );
164 },
165
166 setDiscoTime: function(aid, number)
167 {
168 db.assessments.update(
169 {
170 _id: aid,
171 "papers.number": number,
172 },
173 { $set: { "papers.$.discoTime": Date.now() } }
174 );
175 },
176
177 getDiscoTime: function(aid, number, cb)
178 {
179 db.assessments.findOne(
180 { _id: aid },
181 (err,a) => {
182 if (!!err)
183 return cb(err, null);
184 const idx = a.papers.findIndex( item => { return item.number == number; });
185 cb(null, a.papers[idx].discoTime);
186 }
187 );
188 },
189
190 hasInput: function(aid, number, password, idx, cb)
191 {
192 db.assessments.findOne(
193 {
194 _id: aid,
195 "papers.number": number,
196 "papers.password": password,
197 },
198 (err,a) => {
199 if (!!err || !a)
200 return cb(err,a);
201 let papIdx = a.papers.findIndex( item => { return item.number == number; });
202 for (let i of a.papers[papIdx].inputs)
203 {
204 if (i.index == idx)
205 return cb(null,true);
206 }
207 cb(null,false);
208 }
209 );
210 },
211
212 // https://stackoverflow.com/questions/27874469/mongodb-push-in-nested-array
213 setInput: function(aid, number, password, input, callback) //input: index + arrayOfInt (or txt)
214 {
215 db.assessments.update(
216 {
217 _id: aid,
218 "papers.number": number,
219 "papers.password": password,
220 },
221 { $push: { "papers.$.inputs": input } },
222 callback
223 );
224 },
225
226 endAssessment: function(aid, number, password, callback)
227 {
228 db.assessments.update(
229 {
230 _id: aid,
231 "papers.number": number,
232 "papers.password": password,
233 },
234 { $set: {
235 "papers.$.endTime": Date.now(),
236 "papers.$.password": "",
237 } },
238 callback
239 );
240 },
241
242 getConclusion: function(aid, callback)
243 {
244 db.assessments.findOne(
245 { _id: aid },
246 { conclusion: 1},
247 (err,res) => {
248 callback(err, !!res ? res.conclusion : null);
249 }
250 );
251 },
252
253 remove: function(aid, cb)
254 {
255 db.assessments.remove(
256 { _id: aid },
257 cb
258 );
259 },
260
261 removeGroup: function(cid, cb)
262 {
263 db.assessments.remove(
264 { cid: cid },
265 cb
266 );
267 },
268 }
269
270 module.exports = AssessmentEntity;