Properly fix duplicate course instances

* Add script to merge existing course instances
* Have client only send one course instance creation
* Have server return existing course instance if one for the given classroom/course exists
* Remove bandaid code from ClassroomView
This commit is contained in:
Scott Erickson 2016-02-08 12:48:15 -08:00
parent 3acb2a4c7a
commit bb38c58903
4 changed files with 83 additions and 7 deletions

View file

@ -53,11 +53,6 @@ module.exports = class ClassroomView extends RootView
@sessions = new CocoCollection([], { model: LevelSession })
onCourseInstancesSync: ->
# clear duplicates with lodash magic
groups = _.groupBy @courseInstances.models, (ci) -> ci.get('courseID')
@courseInstances.reset(_.map(_.values(groups), _.first))
# TODO: Make having multiple course instances for a classroom/course pair impossible in the db
@sessions = new CocoCollection([], { model: LevelSession })
for courseInstance in @courseInstances.models
sessions = new CocoCollection([], { url: "/db/course_instance/#{courseInstance.id}/level_sessions", model: LevelSession })
@ -205,7 +200,9 @@ module.exports = class ClassroomView extends RootView
if courseInstance.isNew()
# adding the first student to this course, so generate the course instance for it
courseInstance.save(null, {validate: false})
if not courseInstance.saving
courseInstance.save(null, {validate: false})
courseInstance.saving = true
courseInstance.once 'sync', onCourseInstanceCreated
else
onCourseInstanceCreated()

View file

@ -0,0 +1,53 @@
var currentClassroom = null;
var courseInstances = {};
var count = 0;
load('node_modules/lodash/lodash.js');
function toStrings(array) {
return _.sortBy(_.map(array, function(item) { return item.valueOf(); }));
}
function mergeMembers(members1, members2) {
var members1 = members1 || [];
var members2 = members2 || [];
var members = members1.concat(members2);
if(!members.length)
return [];
print('concat:');
print('\t', members1.length, JSON.stringify(toStrings(members1)));
print('\t', members2.length, JSON.stringify(toStrings(members2)));
print('\t', members.length, JSON.stringify(toStrings(members)));
members = _.uniq(members, false, function(member) { return member.valueOf() });
print('\t', members.length, JSON.stringify(toStrings(members)));
return members;
}
var count = 0;
db.course.instances.find({classroomID: {$exists: true}}).sort({classroomID: 1}).forEach(function(courseInstance) {
count += 1;
if(count % 100 === 0) { print('count', count); }
if (!currentClassroom || !courseInstance.classroomID.equals(currentClassroom)) {
currentClassroom = courseInstance.classroomID;
courseInstances = {};
}
if (courseInstances[courseInstance.courseID]) {
var keeper = courseInstances[courseInstance.courseID];
if (!keeper.classroomID.equals(courseInstance.classroomID)) {
throw new Error('This should not happen.');
return;
}
print('duplicate...', count, 'in classroom', courseInstance.classroomID, keeper.members.length, courseInstance.members.length);
print(JSON.stringify(courseInstance, null, '\t'));
print(JSON.stringify(keeper, null, '\t'));
keeper.members = mergeMembers(keeper.members, courseInstance.members);
print('new members', keeper.members.length);
print('save', db.course.instances.save(keeper));
print('remove', db.course.instances.remove({_id:courseInstance._id}), true);
print('keeper id', keeper._id)
}
else {
courseInstances[courseInstance.courseID] = courseInstance;
}
});

View file

@ -133,7 +133,14 @@ CourseInstanceHandler = class CourseInstanceHandler extends Handler
Course.findById req.body.courseID, (err, course) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res, 'Course not found') unless course
super(req, res)
q = {
courseID: mongoose.Types.ObjectId(req.body.courseID)
classroomID: mongoose.Types.ObjectId(req.body.classroomID)
}
CourseInstance.findOne(q).exec (err, doc) =>
return @sendDatabaseError(res, err) if err
return @sendSuccess(res, @formatEntity(req, doc)) if doc
super(req, res)
makeNewInstance: (req) ->
doc = new CourseInstance({

View file

@ -23,6 +23,25 @@ describe 'POST /db/course_instance', ->
expect(res.statusCode).toBe(200)
expect(body.classroomID).toBeDefined()
done()
it 'returns the same CourseInstance if you POST twice', (done) ->
test = @
url = getURL('/db/course_instance')
data = {
name: 'Some Name'
courseID: test.course.id
classroomID: test.classroom.id
}
request.post {uri: url, json: data}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.classroomID).toBeDefined()
firstID = body._id
request.post {uri: url, json: data}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.classroomID).toBeDefined()
secondID = body._id
expect(firstID).toBe(secondID)
done()
it 'returns 404 if the Course does not exist', (done) ->
test = @