[virtualcommons-svn] commit/vcweb: alllee: adding a basic lock check to the experimenter monitor in
Status: Beta
Brought to you by:
alllee
From: <com...@bi...> - 2013-07-22 23:37:51
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/bb7b9525bde2/ Changeset: bb7b9525bde2 User: alllee Date: 2013-07-23 01:37:46 Summary: adding a basic lock check to the experimenter monitor interface to try to prevent double advance to next rounds Affected #: 4 files diff -r 94a0b76f6cbc871b70de376d6b6238ee32168c5c -r bb7b9525bde2db7f923da2f6bacb0a1e200b14a1 vcweb/core/__init__.py --- a/vcweb/core/__init__.py +++ b/vcweb/core/__init__.py @@ -2,9 +2,13 @@ from django.core.serializers.json import DjangoJSONEncoder from django.db.models import Model from django.db.models.query import QuerySet +from django.utils.functional import curry from functools import partial +import fcntl import json -from django.utils.functional import curry +import logging + +logger = logging.getLogger(__name__) class VcwebJSONEncoder(DjangoJSONEncoder): def default(self, obj): @@ -36,3 +40,17 @@ def enum(*sequential, **named): enums = dict(zip(sequential, range(len(sequential))), **named) return type('Enum', (), enums) + +class ModelLock(object): + def __init__(self, model): + self.filename = "/tmp/django-%s.%d.lock" % (model._meta.object_name, model.pk) + self.handle = open(self.filename, 'w') + + def acquire(self): + return fcntl.flock(self.handle, fcntl.LOCK_EX) + + def release(self): + return fcntl.flock(self.handle, fcntl.LOCK_UN) + + def __del__(self): + self.handle.close() diff -r 94a0b76f6cbc871b70de376d6b6238ee32168c5c -r bb7b9525bde2db7f923da2f6bacb0a1e200b14a1 vcweb/core/ajax.py --- a/vcweb/core/ajax.py +++ b/vcweb/core/ajax.py @@ -170,13 +170,17 @@ def experiment_controller(request, pk, action=None): experimenter = request.user.experimenter experiment = _get_experiment(request, pk) + logger.debug("experimenter %s invoking %s on %s", experimenter, action, experiment) try: response_tuples = experiment.invoke(action, experimenter) - logger.debug("invoking action %s results: %s", action, str(response_tuples)) - return experiment.to_json() + logger.debug("invoking action %s: %s", action, str(response_tuples)) + return JsonResponse(dumps({ + 'success': True, + 'experiment': experiment.to_dict() + })) except AttributeError as e: logger.warning("no attribute %s on experiment %s (%s)", action, experiment.status_line, e) - return dumps({ + return JsonResponse(dumps({ 'success': False, 'message': 'Invalid experiment action %s' % action - }) + })) diff -r 94a0b76f6cbc871b70de376d6b6238ee32168c5c -r bb7b9525bde2db7f923da2f6bacb0a1e200b14a1 vcweb/core/models.py --- a/vcweb/core/models.py +++ b/vcweb/core/models.py @@ -1115,9 +1115,9 @@ # FIXME: refactor this into a single data structure # maps round type name to (description, default_template_filename) ROUND_TYPES_DICT = dict( - WELCOME=(_('Initial welcome page / waiting room'), 'welcome.html'), - GENERAL_INSTRUCTIONS=(_('Introduction to the experiment / general instructions page'), 'general-instructions.html'), - REGULAR=('Regular / playable experiment round', 'participate.html'), + WELCOME=(_('Initial welcome page waiting room'), 'welcome.html'), + GENERAL_INSTRUCTIONS=(_('General instructions and introduction to the experiment'), 'general-instructions.html'), + REGULAR=('Regular experiment round', 'participate.html'), CHAT=('Chat round', 'chat.html'), DEBRIEFING=('Debriefing round', 'debriefing.html'), INSTRUCTIONS=('Instructions round', 'instructions.html'), diff -r 94a0b76f6cbc871b70de376d6b6238ee32168c5c -r bb7b9525bde2db7f923da2f6bacb0a1e200b14a1 vcweb/core/templates/experimenter/monitor.html --- a/vcweb/core/templates/experimenter/monitor.html +++ b/vcweb/core/templates/experimenter/monitor.html @@ -230,7 +230,6 @@ } } model.saveExperimenterNotes = function(localModel, evt) { - var notes = $('#experimenterNotesText').val(); $('#submitExperimenterNotesButton').html("Submitting.."); Dajaxice.vcweb.core.save_experimenter_notes(function(data) { @@ -280,12 +279,17 @@ }; model.advanceToNextRound = function() { $('#progress-modal').modal('show'); - Dajaxice.vcweb.core.experiment_controller(function(data) { - ko.mapping.fromJS(data, model); + Dajaxice.vcweb.core.experiment_controller(function(response) { + if (response.success) { + ko.mapping.fromJS(response.experiment, model); model.startTimer(); sendUpdateEvent(); $('#progress-modal').modal('hide'); - }, {pk: {{experiment.pk}}, 'action':"advance_to_next_round"}); + } + else { + console.debug("Unable to advance to next round: " + response.message); + } + }, {pk: {{experiment.pk}}, 'action':"advance_to_next_round"}); } model.confirmExperimentControllerAction = function(shouldUpdateParticipants, localModel, evt) { element = evt.target; @@ -295,13 +299,18 @@ } confirmAction(element, function(confirmed, action) { $('#progress-modal').modal('show'); - Dajaxice.vcweb.core.experiment_controller(function(data) { - ko.mapping.fromJS(data, model); - model.startTimer(); - if (shouldUpdateParticipants) { - sendUpdateEvent(); + Dajaxice.vcweb.core.experiment_controller(function(response) { + if (response.success) { + ko.mapping.fromJS(response.experiment, model); + model.startTimer(); + if (shouldUpdateParticipants) { + sendUpdateEvent(); + } + $('#progress-modal').modal('hide'); } - $('#progress-modal').modal('hide'); + else { + console.debug("Unable to perform experiment controller action: " + response.message); + } }, {pk: {{experiment.pk}}, 'action':action}); }); } @@ -311,14 +320,24 @@ model.startOrStopExperimentActionText = ko.computed(function() { return model.isRoundInProgress() ? "stop" : "start"; }); + // FIXME: poor man's mutex, hacky and unconvinced that it'll provide true thread safety. Revisit. + model.isCheckingParticipants = ko.observable(false); model.checkAllParticipantsReady = function() { + if (model.isCheckingParticipants()) { + console.debug("already checking participants, aborting"); + return; + } + model.isCheckingParticipants(true); console.debug("checking if all participants are ready"); $.get('/experiment/{{experiment.pk}}/check-ready-participants', function(response) { if (response.all_participants_ready) { - model.addMessage("All participants are ready, advancing to next round"); - model.advanceToNextRound(); + model.addMessage("All participants are ready, please advance to the next round"); + // FIXME: need to ensure that this can only get called once, otherwise we run the risk of + // invoking advance to next round twice + model.advanceToNextRound() } }); + model.isCheckingParticipants(false); } model.updateParticipants = function(m, evt) { confirmAction(evt.target, function(confirmed, action) { Repository URL: https://bitbucket.org/virtualcommons/vcweb/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. |