SMILEConsortium / node-smile-server

3 stars 4 forks source link

Several questions deleted in the same time (and even not the good one sometimes) #98

Closed chrqls closed 10 years ago

chrqls commented 10 years ago

@truedat101

At the beginning, when we call /smile/all, we have these questions: (ps: I remove the sequence "NAME":"JohnDoe","IP":"127.0.0.11", on each row)

{"O1":"a","O2":"aa","O3":"aaa","O4":"aaaa","TYPE":"QUESTION","Q":"a","A":"4"},
{"O1":"b","O2":"bb","O3":"bbb","O4":"bbbb","TYPE":"QUESTION","Q":"b","A":"4"},
{"O1":"c","O2":"cc","O3":"ccc","O4":"cccc","TYPE":"QUESTION","Q":"c","A":"4"},
{"O1":"d","O2":"dd","O3":"ddd","O4":"dddd","TYPE":"QUESTION","Q":"d","A":"4"},
{"O1":"e","O2":"ee","O3":"eee","O4":"eeee","TYPE":"QUESTION","Q":"e","A":"4"},
{"O1":"f","O2":"ff","O3":"fff","O4":"ffff","TYPE":"QUESTION","Q":"f","A":"4"},
{"O1":"g","O2":"gg","O3":"ggg","O4":"gggg","TYPE":"QUESTION","Q":"g","A":"4"},
{"O1":"h","O2":"hh","O3":"hhh","O4":"hhhh","TYPE":"QUESTION","Q":"h","A":"4"},
{"O1":"i","O2":"ii","O3":"iii","O4":"iiii","TYPE":"QUESTION","Q":"i","A":"4"},
{"O1":"j","O2":"jj","O3":"jjj","O4":"jjjj","TYPE":"QUESTION","Q":"j","A":"4"}]

First round

I made 3 tests:

Each time, I got the same result

{"O1":"b","O2":"bb","O3":"bbb","O4":"bbbb","TYPE":"QUESTION","Q":"b","A":"4"},
{"O1":"d","O2":"dd","O3":"ddd","O4":"dddd","TYPE":"QUESTION","Q":"d","A":"4"},
{"O1":"f","O2":"ff","O3":"fff","O4":"ffff","TYPE":"QUESTION","Q":"f","A":"4"},
{"O1":"h","O2":"hh","O3":"hhh","O4":"hhhh","TYPE":"QUESTION","Q":"h","A":"4"},
{"O1":"j","O2":"jj","O3":"jjj","O4":"jjjj","TYPE":"QUESTION","Q":"j","A":"4"}]

Second round

If I choose to delete question J, /smile/all returns:

{"O1":"b","O2":"bb","O3":"bbb","O4":"bbbb","TYPE":"QUESTION","Q":"b","A":"4"},
{"O1":"f","O2":"ff","O3":"fff","O4":"ffff","TYPE":"QUESTION","Q":"f","A":"4"},
{"O1":"j","O2":"jj","O3":"jjj","O4":"jjjj","TYPE":"QUESTION","Q":"j","A":"4"}]

but if I choose to delete question D, it will return:

{"O1":"d","O2":"dd","O3":"ddd","O4":"dddd","TYPE":"QUESTION","Q":"d","A":"4"},
{"O1":"h","O2":"hh","O3":"hhh","O4":"hhhh","TYPE":"QUESTION","Q":"h","A":"4"}]

I am going to see what is wrong with this problem.

truedat101 commented 10 years ago

Here's the problem. We don't have a Question.SessionID generated by the clients that submit questions. Each question has a unique SessionID. If you can clarify how we generate this SessionID, we can add this to the SMILE Student Web app.

truedat101 commented 10 years ago

I can work around this problem, but I'm not sure how to uniquely identify a question, for example, delete question 3 in your example, we have to traverse both the array of questions (listOfQuestions in the server), as well as currentQuestions (which is a JSON object ... all we have is a question number, no other identifying details of the question, that's why I opted to use the SessionID that is stored in each question.

truedat101 commented 10 years ago

note also, this only affects questions generated by the web client, and not by the preloaded questions. @chrqls can you propose a solution?

chrqls commented 10 years ago

I just noticed the same conclusion on my side.

chrqls commented 10 years ago

You are totally right about the fact that the preloaded questions have a SessionID key, but if you preload JAMsj Barracks Set 2013, you will see by deleting the third question that the first one is also deleted, so there is 2 problems here.

chrqls commented 10 years ago

If I want to create a new IQSet, here is the JSON sent by teacher app:

{"iqdata":[{"PICURL":"","NAME":"Testboy","Q":"A","A":4,"IP":"127.0.0.1","O4":"AAAAA","O3":"AAAA","O2":"AAA","O1":"AA","TYPE":"QUESTION"},
{"PICURL":"","NAME":"Testboy","Q":"B","A":4,"IP":"127.0.0.1","O4":"BBBBB","O3":"BBBB","O2":"BBB","O1":"BB","TYPE":"QUESTION"},
{"PICURL":"","NAME":"Testboy","Q":"C","A":4,"IP":"127.0.0.1","O4":"CCCCC","O3":"CCCC","O2":"CCC","O1":"CC","TYPE":"QUESTION"},
{"PICURL":"","NAME":"Testboy","Q":"E","A":4,"IP":"127.0.0.1","O4":"EEEEE","O3":"EEEE","O2":"EEE","O1":"EE","TYPE":"QUESTION"},
{"PICURL":"","NAME":"Testboy","Q":"F","A":4,"IP":"127.0.0.1","O4":"FFFFF","O3":"FFFF","O2":"FFF","O1":"FF","TYPE":"QUESTION"},
{"PICURL":"","NAME":"Testboy","Q":"G","A":4,"IP":"127.0.0.1","O4":"GGGGG","O3":"GGGG","O2":"GGG","O1":"GG","TYPE":"QUESTION"}],"title":"Alphabet"}

As you can see, there is not a SessionID key Then, if you load this new IQSet, and then delete the last question, it calls this method on node server:

exports.handleQuestionJSONDelete = function(req, res) {

    var questionNumber = parseInt(req.id, 10);
    var question = game.questions.getList()[questionNumber];

    // Is the Question ID existing, Is it Make Questions State?
    // ...

    // Handle deletion
    var status = game.questions.deleteQuestion(questionNumber);

    console.log(status); // shows the good question "deleted" in JSON

    // Verify we get back an mSessionID of the deleted question
    if (status !== 0) {
        var msgs = game.messages.past;
        for (var i = 0; i < msgs.length; i++) {
            if (msgs[i].mSessionID === status.mSessionID) {
                console.log("Deleting message with mSessionID = " + status.mSessionID);
                game.messages.past.splice(i, 1); // Delete the question from the ghost of messages past
            }
        }
    }
    res.sendJSON(HTTP_STATUS_OK, {'status': status});
};

you can see in the var msgs = game.messages.past; that the SessionID does exist on node server:

[{"TYPE":"QUESTION","IP":"127.0.0.1","NAME":"Testboy","O1":"AA","O2":"AAA","O3":"AAAA","O4":"AAAAA","Q":"A","SessionID":"1387707995134","A":4},
{"TYPE":"QUESTION","IP":"127.0.0.1","NAME":"Testboy","O1":"BB","O2":"BBB","O3":"BBBB","O4":"BBBBB","Q":"B","SessionID":"1387707995235","A":4},
{"TYPE":"QUESTION","IP":"127.0.0.1","NAME":"Testboy","O1":"CC","O2":"CCC","O3":"CCCC","O4":"CCCCC","Q":"C","SessionID":"1387707995284","A":4},
{"TYPE":"QUESTION","IP":"127.0.0.1","NAME":"Testboy","O1":"EE","O2":"EEE","O3":"EEEE","O4":"EEEEE","Q":"E","SessionID":"1387707995334","A":4},
{"TYPE":"QUESTION","IP":"127.0.0.1","NAME":"Testboy","O1":"FF","O2":"FFF","O3":"FFFF","O4":"FFFFF","Q":"F","SessionID":"1387707995382","A":4},
{"TYPE":"QUESTION","IP":"127.0.0.1","NAME":"Testboy","O1":"GG","O2":"GGG","O3":"GGGG","O4":"GGGGG","Q":"G","SessionID":"1387707995434","A":4},{"TYPE":"START_MAKE"}]

So, in my opinion, this SessionID is generated on the node server.

chrqls commented 10 years ago

When the node server new the new IQSet to save, if everything is ok, it calls in index.js (line 178) this method:

console.log('IQSet is valid, commit');
    pdb.putIQSet(iqset, function(err, result) {

        if (!err) {
            iqset.success = true;
            return res.sendJSON(HTTP_STATUS_OK, iqset);
        } else {
            return res.sendJSON(HTTP_STATUS_OK, {
            'error': 'Unable to persist IQSet data',
            'success': false
        });
    }
});

And the method putIQSet does not contains something about the SessionID. So, I don't understand how the SessionID appears for now...

Persisteus.prototype.putIQSet = function putIQSet(iqdoc, cb) {

    if (!iqdoc._id)         { iqdoc._id = pouchdb.uuid(); }
    if (!iqdoc.ducktype)    { iqdoc.ducktype = "iqsetdoc"; }
    if (!iqdoc.date)        { iqdoc.date = new Date().toISOString(); }
    if (!iqdoc.title)       { iqdoc.title = iqdoc.date + "-IQSet"; }
    if (!iqdoc.teachername) { iqdoc.teachername = "Teacher"; }
    if (!iqdoc.groupname)   { iqdoc.groupname = "General"; }

    // 'iqdata' - We should check for tis in the payload
    this.DB.put(iqdoc, cb);
};
truedat101 commented 10 years ago

But if our main concern is the SessionID existing (you added that concept?), we should just think of it as being part of a running session, not of the IQSet itself, at least for the time being. I do think each question needs a GUID. But at the moment we don't manage questions individually in the database.

So what is the mechanism we use to generate the Question.SessionID?

truedat101 commented 10 years ago

Looks like in SMILE Teacher, ./main/java/org/smilec/smile/bu/SmilePlugServerManager.java: questionWrapper.setSessionID(Long.toString(System.currentTimeMillis())); // XXX This really should be the sessionID from the server (via couchDB _ID)

sets the sessionID

It is based on current time in millis. We can do the same in SMILE student, or, if the sessionID is not set on the question, we can set it on the server.

truedat101 commented 10 years ago

Ok, we can fix this in the server, before we complete Question.addQuestion() double check existence of SessionID , and use the questionKey that we generate as the id.

truedat101 commented 10 years ago

Closing, fixed.