X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=models%2Fassessment.js;fp=models%2Fassessment.js;h=0000000000000000000000000000000000000000;hb=a3080c337cfaca9d600911396cae5a9233d43554;hp=de3d2d7bbecb96a64e1ebde50fcfa225fb464986;hpb=87d1063bdac629eca89c7ec0192fbc17e069a197;p=qomet.git diff --git a/models/assessment.js b/models/assessment.js deleted file mode 100644 index de3d2d7..0000000 --- a/models/assessment.js +++ /dev/null @@ -1,401 +0,0 @@ -const CourseModel = require("../models/course"); -const UserModel = require("../models/user"); -const ObjectId = require("bson-objectid"); -const TokenGen = require("../utils/tokenGenerator"); -const db = require("../utils/database"); - -const AssessmentModel = -{ - /* - * Structure: - * _id: BSON id - * cid: course ID - * name: varchar - * active: boolean - * mode: secure | watch | exam | open (decreasing security) - * fixed: bool (questions in fixed order; default: false) - * display: "one" or "all" (generally "all" for open questions, but...) - * time: 0, global (one vaue) or per question (array of integers) - * introduction: "", - * coefficient: number, default 1 - * questions: array of - * index: for paper test, like 2.1.a (?!); and quiz: 0, 1, 2, 3... - * wording: varchar (HTML) - * options: array of varchar --> if present, question type == quiz! - * fixed: bool, options in fixed order (default: false) - * answer: array of integers (for quiz) or html text (for paper); striped in exam mode - * active: boolean, is question in current assessment? - * points: points for this question (default 1) - * param: parameter (if applicable) - * papers : array of - * number: student number - * inputs: array of {index,answer[array of integers or html text],startTime} - * current: index of current question (if relevant: display="one") - * startTime - * discoTime, totalDisco: last disconnect timestamp (if relevant) + total time - * discoCount: number of disconnections - * password: random string identifying student for exam session TEMPORARY - */ - - ////////////////// - // BASIC FUNCTIONS - - getById: function(aid, callback) - { - db.assessments.findOne( - { _id: aid }, - callback - ); - }, - - getByPath: function(cid, name, callback) - { - db.assessments.findOne( - { - cid: cid, - name: name, - }, - callback - ); - }, - - insert: function(cid, name, callback) - { - db.assessments.insert( - { - name: name, - cid: cid, - active: false, - mode: "exam", - fixed: false, - display: "one", - time: 0, - introduction: "", - coefficient: 1, - questions: [ ], - papers: [ ], - }, - callback - ); - }, - - getByCourse: function(cid, callback) - { - db.assessments.find( - { cid: cid }, - callback - ); - }, - - // arg: full assessment without _id field - replace: function(aid, assessment, cb) - { - // Should be: (but unsupported by mongojs) -// db.assessments.replaceOne( -// { _id: aid }, -// assessment, -// cb -// ); - // Temporary workaround: - db.assessments.update( - { _id: aid }, - { $set: assessment }, - cb - ); - }, - - getQuestions: function(aid, callback) - { - db.assessments.findOne( - { - _id: aid, - display: "all", - }, - { questions: 1}, - (err,res) => { - callback(err, !!res ? res.questions : null); - } - ); - }, - - getQuestion: function(aid, index, callback) - { - db.assessments.findOne( - { - _id: aid, - display: "one", - }, - { questions: 1}, - (err,res) => { - if (!!err || !res) - return callback(err, res); - const qIdx = res.questions.findIndex( item => { return item.index == index; }); - if (qIdx === -1) - return callback({errmsg: "Question not found"}, null); - callback(null, res.questions[qIdx]); - } - ); - }, - - getPaperByNumber: function(aid, number, callback) - { - db.assessments.findOne( - { - _id: aid, - "papers.number": number, - }, - (err,a) => { - if (!!err || !a) - return callback(err,a); - for (let p of a.papers) - { - if (p.number == number) - return callback(null,p); //reached for sure - } - } - ); - }, - - // NOTE: no callbacks for 2 next functions, failures are not so important - // (because monitored: teachers can see what's going on) - - addDisco: function(aid, number, deltaTime) - { - db.assessments.update( - { - _id: aid, - "papers.number": number, - }, - { $inc: { - "papers.$.discoCount": 1, - "papers.$.totalDisco": deltaTime, - } }, - { $set: { "papers.$.discoTime": null } } - ); - }, - - setDiscoTime: function(aid, number) - { - db.assessments.update( - { - _id: aid, - "papers.number": number, - }, - { $set: { "papers.$.discoTime": Date.now() } } - ); - }, - - getDiscoTime: function(aid, number, cb) - { - db.assessments.findOne( - { _id: aid }, - (err,a) => { - if (!!err) - return cb(err, null); - const idx = a.papers.findIndex( item => { return item.number == number; }); - cb(null, a.papers[idx].discoTime); - } - ); - }, - - hasInput: function(aid, number, password, idx, cb) - { - db.assessments.findOne( - { - _id: aid, - "papers.number": number, - "papers.password": password, - }, - (err,a) => { - if (!!err || !a) - return cb(err,a); - let papIdx = a.papers.findIndex( item => { return item.number == number; }); - for (let i of a.papers[papIdx].inputs) - { - if (i.index == idx) - return cb(null,true); - } - cb(null,false); - } - ); - }, - - // https://stackoverflow.com/questions/27874469/mongodb-push-in-nested-array - setInput: function(aid, number, password, input, callback) //input: index + arrayOfInt (or txt) - { - db.assessments.update( - { - _id: aid, - "papers.number": number, - "papers.password": password, - }, - { $push: { "papers.$.inputs": input } }, - callback - ); - }, - - endAssessment: function(aid, number, password, callback) - { - db.assessments.update( - { - _id: aid, - "papers.number": number, - "papers.password": password, - }, - { $set: { - "papers.$.endTime": Date.now(), - "papers.$.password": "", - } }, - callback - ); - }, - - remove: function(aid, cb) - { - db.assessments.remove( - { _id: aid }, - cb - ); - }, - - removeGroup: function(cid, cb) - { - db.assessments.remove( - { cid: cid }, - cb - ); - }, - - ///////////////////// - // ADVANCED FUNCTIONS - - getByRefs: function(initials, code, name, cb) - { - UserModel.getByInitials(initials, (err,user) => { - if (!!err || !user) - return cb(err || {errmsg: "User not found"}); - CourseModel.getByPath(user._id, code, (err2,course) => { - if (!!err2 || !course) - return cb(err2 || {errmsg: "Course not found"}); - AssessmentModel.getByPath(course._id, name, (err3,assessment) => { - if (!!err3 || !assessment) - return cb(err3 || {errmsg: "Assessment not found"}); - cb(null,assessment); - }); - }); - }); - }, - - checkPassword: function(aid, number, password, cb) - { - AssessmentModel.getById(aid, (err,assessment) => { - if (!!err || !assessment) - return cb(err, assessment); - const paperIdx = assessment.papers.findIndex( item => { return item.number == number; }); - if (paperIdx === -1) - return cb({errmsg: "Paper not found"}, false); - cb(null, assessment.papers[paperIdx].password == password); - }); - }, - - add: function(uid, cid, name, cb) - { - // 1) Check that course is owned by user of ID uid - CourseModel.getById(cid, (err,course) => { - if (!!err || !course) - return cb({errmsg: "Course retrieval failure"}); - if (!course.uid.equals(uid)) - return cb({errmsg:"Not your course"},undefined); - // 2) Insert new blank assessment - AssessmentModel.insert(cid, name, cb); - }); - }, - - update: function(uid, assessment, cb) - { - const aid = ObjectId(assessment._id); - // 1) Check that assessment is owned by user of ID uid - AssessmentModel.getById(aid, (err,assessmentOld) => { - if (!!err || !assessmentOld) - return cb({errmsg: "Assessment retrieval failure"}); - CourseModel.getById(ObjectId(assessmentOld.cid), (err2,course) => { - if (!!err2 || !course) - return cb({errmsg: "Course retrieval failure"}); - if (!course.uid.equals(uid)) - return cb({errmsg:"Not your course"},undefined); - // 2) Replace assessment - delete assessment["_id"]; - assessment.cid = ObjectId(assessment.cid); - AssessmentModel.replace(aid, assessment, cb); - }); - }); - }, - - // Set password in responses collection - startSession: function(aid, number, password, cb) - { - AssessmentModel.getPaperByNumber(aid, number, (err,paper) => { - if (!!err) - return cb(err,null); - if (!paper && !!password) - return cb({errmsg: "Cannot start a new exam before finishing current"},null); - if (!!paper) - { - if (!password) - return cb({errmsg: "Missing password"}); - if (paper.password != password) - return cb({errmsg: "Wrong password"}); - } - AssessmentModel.getQuestions(aid, (err2,questions) => { - if (!!err2) - return cb(err2,null); - if (!!paper) - return cb(null,{paper:paper}); - const pwd = TokenGen.generate(12); //arbitrary number, 12 seems enough... - db.assessments.update( - { _id: aid }, - { $push: { papers: { - number: number, - startTime: Date.now(), - endTime: undefined, - password: password, - totalDisco: 0, - discoCount: 0, - inputs: [ ], //TODO: this is stage 1, stack indexed answers. - // then build JSON tree for easier access / correct - }}}, - (err3,ret) => { cb(err3,{password:password}); } - ); - }); - }); - }, - - newAnswer: function(aid, number, password, input, cb) - { - // Check that student hasn't already answered - AssessmentModel.hasInput(aid, number, password, input.index, (err,ret) => { - if (!!err) - return cb(err,null); - if (!!ret) - return cb({errmsg:"Question already answered"},null); - AssessmentModel.setInput(aid, number, password, input, (err2,ret2) => { - if (!!err2 || !ret2) - return cb(err2,ret2); - return cb(null,ret2); - }); - }); - }, - - // NOTE: no callbacks for next function, failures are not so important - // (because monitored: teachers can see what's going on) - newConnection: function(aid, number) - { - //increment discoCount, reset discoTime to NULL, update totalDisco - AssessmentModel.getDiscoTime(aid, number, (err,discoTime) => { - if (!!discoTime) - AssessmentModel.addDisco(aid, number, Date.now() - discoTime); - }); - }, -} - -module.exports = AssessmentModel;