virtualcommons-svn Mailing List for Virtual Commons Experiment Software (Page 3)
Status: Beta
Brought to you by:
alllee
You can subscribe to this list here.
2008 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(1) |
Jul
(21) |
Aug
(31) |
Sep
(6) |
Oct
(15) |
Nov
(2) |
Dec
(9) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2009 |
Jan
(4) |
Feb
(6) |
Mar
(12) |
Apr
(52) |
May
(14) |
Jun
(19) |
Jul
(81) |
Aug
(115) |
Sep
(36) |
Oct
(88) |
Nov
(46) |
Dec
(58) |
2010 |
Jan
(52) |
Feb
(55) |
Mar
(48) |
Apr
(15) |
May
(5) |
Jun
(38) |
Jul
(27) |
Aug
(24) |
Sep
(28) |
Oct
(1) |
Nov
(2) |
Dec
(29) |
2011 |
Jan
(87) |
Feb
(39) |
Mar
(63) |
Apr
(42) |
May
(26) |
Jun
(53) |
Jul
(23) |
Aug
(43) |
Sep
(37) |
Oct
(25) |
Nov
(4) |
Dec
(7) |
2012 |
Jan
(73) |
Feb
(79) |
Mar
(62) |
Apr
(28) |
May
(12) |
Jun
(2) |
Jul
(9) |
Aug
(1) |
Sep
(8) |
Oct
|
Nov
(3) |
Dec
(3) |
2013 |
Jan
(8) |
Feb
(16) |
Mar
(38) |
Apr
(74) |
May
(62) |
Jun
(15) |
Jul
(49) |
Aug
(19) |
Sep
(9) |
Oct
|
Nov
|
Dec
|
2014 |
Jan
|
Feb
|
Mar
|
Apr
(2) |
May
(25) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <com...@bi...> - 2013-08-12 22:19:07
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/34ee1f421763/ Changeset: 34ee1f421763 User: alllee Date: 2013-08-13 00:18:55 Summary: re-enabling tour and leaving chat enabled so long as it's set up on the round and the participant is alive Affected #: 2 files diff -r 1ed54364197f5e5abeb9f911c6903dc181ee5404 -r 34ee1f421763cde41e10e8de43fb70dd7451ba3d vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -728,7 +728,7 @@ if (model.templateId() != "REGULAR") { return; } - // model.setupTour(); + model.setupTour(); if (! model.alive()) { model.secondsLeft(0); model.clearCurrentInterval(); @@ -822,7 +822,7 @@ model.initializeChat = function() { // FIXME: chat is disabled in practice rounds, when it's explicitly disabled, or if the participant is // no longer alive - var chatDisabled = model.isPracticeRound() || ! model.chatEnabled() || ! model.alive() || model.submitted(); + var chatDisabled = ! model.chatEnabled() || ! model.alive(); model.setFormDisabled("#chat-form", chatDisabled); model.chatEnabled(! chatDisabled); } diff -r 1ed54364197f5e5abeb9f911c6903dc181ee5404 -r 34ee1f421763cde41e10e8de43fb70dd7451ba3d vcweb/bound/views.py --- a/vcweb/bound/views.py +++ b/vcweb/bound/views.py @@ -1,17 +1,16 @@ from collections import Counter from operator import itemgetter from django.http import Http404 -from django.shortcuts import render, redirect, get_object_or_404 +from django.shortcuts import render, get_object_or_404 from vcweb.core import dumps from vcweb.core.decorators import participant_required from vcweb.core.http import JsonResponse -from vcweb.core.models import (Experiment, ParticipantGroupRelationship, ChatMessage, GroupRelationship) +from vcweb.core.models import (Experiment, ParticipantGroupRelationship, ChatMessage) from vcweb.bound.forms import SingleIntegerDecisionForm from vcweb.bound.models import (get_experiment_metadata, get_regrowth_rate, get_max_allowed_harvest_decision, - get_cost_of_living, get_resource_level, get_initial_resource_level, get_total_storage, get_storage, - get_all_session_storages, get_harvest_decision_dv, get_harvest_decision_parameter, - set_harvest_decision, can_observe_other_group, get_average_harvest, get_average_storage, - get_total_harvest, get_number_alive, get_player_data) + get_cost_of_living, get_resource_level, get_initial_resource_level, get_all_session_storages, + get_harvest_decision_dv, set_harvest_decision, can_observe_other_group, get_average_harvest, + get_average_storage, get_total_harvest, get_number_alive, get_player_data) from urllib import urlencode import logging @@ -114,8 +113,9 @@ experiment_model_dict['maxHarvestDecision'] = get_max_allowed_harvest_decision(participant_group_relationship, current_round_data, ec) experiment_model_dict['templateName'] = current_round.template_name experiment_model_dict['isPracticeRound'] = current_round.is_practice_round - # FIXME: only show the tour on the first practice round.. this is a bit brittle, maybe simply tie it to round #2? - # experiment_model_dict['showTour'] = current_round.is_practice_round and not previous_round.is_practice_round + # FIXME: only show the tour on the first practice round, this is a bit brittle. better setup might be to have a + # dedicated boolean flag on RoundConfiguration? + experiment_model_dict['showTour'] = current_round.is_practice_round and not previous_round.is_practice_round # instructions round parameters if current_round.is_instructions_round: experiment_model_dict['isInstructionsRound'] = True 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. |
From: <com...@bi...> - 2013-08-10 00:29:18
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/1ed54364197f/ Changeset: 1ed54364197f User: alllee Date: 2013-08-10 02:29:04 Summary: fixing typos in instructions Affected #: 1 file diff -r 0b6215b6b9c87fb970f4f1904406c6d9bdf6ed47 -r 1ed54364197f5e5abeb9f911c6903dc181ee5404 vcweb/lighterprints/templates/lighterprints/participate.html --- a/vcweb/lighterprints/templates/lighterprints/participate.html +++ b/vcweb/lighterprints/templates/lighterprints/participate.html @@ -38,7 +38,7 @@ <p><i class='lighterprints-green icon-leaf'></i> Earn <span class='badge badge-lighterprints-green'>green points</span> by completing challenges. Rank up by <b>increasing your group's average score, shown in the - Today"s Progress Chart.</b>. + Today's Progress Chart.</b></p><p><i class='icon-star text-gold'></i> Some challenges are unlocked all day, and others are only 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. |
From: A L. <iss...@bi...> - 2013-08-07 22:58:41
|
New issue 116: improve general data output format https://bitbucket.org/virtualcommons/vcweb/issue/116/improve-general-data-output-format A Lee: Create long tuples of individual observations with: 1. participant id 2. participant number 3. group id 4. round number 5. timestamp 6. data parameter type 7. data parameter value - still need to figure out how to translate complex values like the activity related to a lighter footprints activity performed data value, either list all of the attributes out or create a lookup table 8. all group data values associated with that round data. Responsible: alllee |
From: <com...@bi...> - 2013-08-07 00:13:19
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/0b6215b6b9c8/ Changeset: 0b6215b6b9c8 User: alllee Date: 2013-08-07 02:13:05 Summary: preliminary support for daily rounds in ticket #115 - adding ExperimentConfiguration.has_daily_rounds to signify that each RoundConfiguration in the experiment should begin automatically at midnight - adding Experiment.start_date to store when a daily experiment should begin. - adding a signal handler at midnight to update all daily experiments (inactive -> activate, activate -> advance to next round) Affected #: 2 files diff -r 30a09795c0e26fda7149c2e9def3fd8d45d29d95 -r 0b6215b6b9c87fb970f4f1904406c6d9bdf6ed47 vcweb/core/migrations/0011_auto__add_field_experiment_start_date__add_field_experimentconfigurati.py --- /dev/null +++ b/vcweb/core/migrations/0011_auto__add_field_experiment_start_date__add_field_experimentconfigurati.py @@ -0,0 +1,420 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Experiment.start_date' + db.add_column(u'core_experiment', 'start_date', + self.gf('django.db.models.fields.DateField')(null=True, blank=True), + keep_default=False) + + # Adding field 'ExperimentConfiguration.has_daily_rounds' + db.add_column(u'core_experimentconfiguration', 'has_daily_rounds', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Experiment.start_date' + db.delete_column(u'core_experiment', 'start_date') + + # Deleting field 'ExperimentConfiguration.has_daily_rounds' + db.delete_column(u'core_experimentconfiguration', 'has_daily_rounds') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'core.activitylog': { + 'Meta': {'object_name': 'ActivityLog'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'log_message': ('django.db.models.fields.TextField', [], {}) + }, + u'core.address': { + 'Meta': {'object_name': 'Address'}, + 'city': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'street1': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'street2': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'zipcode': ('django.db.models.fields.CharField', [], {'max_length': '8', 'blank': 'True'}) + }, + u'core.bookmarkedexperimentmetadata': { + 'Meta': {'ordering': "['experimenter', 'experiment_metadata']", 'unique_together': "(('experimenter', 'experiment_metadata'),)", 'object_name': 'BookmarkedExperimentMetadata'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'experiment_metadata': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bookmarked_experiment_metadata_set'", 'to': u"orm['core.ExperimentMetadata']"}), + 'experimenter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bookmarked_experiment_metadata_set'", 'to': u"orm['core.Experimenter']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'core.chatmessage': { + 'Meta': {'ordering': "['-date_created']", 'object_name': 'ChatMessage', '_ormbases': [u'core.ParticipantRoundDataValue']}, + u'participantrounddatavalue_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['core.ParticipantRoundDataValue']", 'unique': 'True', 'primary_key': 'True'}), + 'target_participant': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'target_participant_chat_message_set'", 'null': 'True', 'to': u"orm['core.ParticipantGroupRelationship']"}) + }, + u'core.comment': { + 'Meta': {'ordering': "['-date_created']", 'object_name': 'Comment', '_ormbases': [u'core.ParticipantRoundDataValue']}, + u'participantrounddatavalue_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['core.ParticipantRoundDataValue']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'core.experiment': { + 'Meta': {'ordering': "['date_created', 'status']", 'object_name': 'Experiment'}, + 'amqp_exchange_name': ('django.db.models.fields.CharField', [], {'default': "'vcweb.default.exchange'", 'max_length': '64'}), + 'authentication_code': ('django.db.models.fields.CharField', [], {'default': "'vcweb.auth.code'", 'max_length': '32'}), + 'current_repeated_round_sequence_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'current_round_sequence_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'current_round_start_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_activated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'duration': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'experiment_configuration': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.ExperimentConfiguration']"}), + 'experiment_metadata': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.ExperimentMetadata']"}), + 'experimenter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Experimenter']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'INACTIVE'", 'max_length': '32'}), + 'tick_duration': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}) + }, + u'core.experimentactivitylog': { + 'Meta': {'object_name': 'ExperimentActivityLog', '_ormbases': [u'core.ActivityLog']}, + u'activitylog_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['core.ActivityLog']", 'unique': 'True', 'primary_key': 'True'}), + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_log_set'", 'to': u"orm['core.Experiment']"}), + 'round_configuration': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.RoundConfiguration']"}) + }, + u'core.experimentconfiguration': { + 'Meta': {'ordering': "['experiment_metadata', 'creator', 'date_created']", 'object_name': 'ExperimentConfiguration'}, + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'experiment_configuration_set'", 'to': u"orm['core.Experimenter']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'exchange_rate': ('django.db.models.fields.DecimalField', [], {'default': '0.2', 'null': 'True', 'max_digits': '6', 'decimal_places': '2', 'blank': 'True'}), + 'experiment_metadata': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'experiment_configuration_set'", 'to': u"orm['core.ExperimentMetadata']"}), + 'has_daily_rounds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invitation_subject': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'invitation_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_experimenter_driven': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'max_group_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '5'}), + 'max_number_of_participants': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'treatment_id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}) + }, + u'core.experimenter': { + 'Meta': {'ordering': "['user']", 'object_name': 'Experimenter'}, + 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'authentication_token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'failed_password_attempts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'institution': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Institution']", 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'experimenter'", 'unique': 'True', 'to': u"orm['auth.User']"}) + }, + u'core.experimenterrequest': { + 'Meta': {'object_name': 'ExperimenterRequest'}, + 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) + }, + u'core.experimentmetadata': { + 'Meta': {'ordering': "['namespace', 'date_created']", 'object_name': 'ExperimentMetadata'}, + 'about_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_configuration': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.ExperimentConfiguration']", 'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'namespace': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'short_name': ('django.db.models.fields.SlugField', [], {'max_length': '32', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'core.experimentparametervalue': { + 'Meta': {'object_name': 'ExperimentParameterValue'}, + 'boolean_value': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'experiment_configuration': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parameter_value_set'", 'to': u"orm['core.ExperimentConfiguration']"}), + 'float_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'int_value': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'parameter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Parameter']"}), + 'string_value': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + u'core.experimentsession': { + 'Meta': {'object_name': 'ExperimentSession'}, + 'capacity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '20'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'experiment_session_set'", 'to': u"orm['auth.User']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'experiment_metadata': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'experiment_session_set'", 'to': u"orm['core.ExperimentMetadata']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invitation_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'scheduled_date': ('django.db.models.fields.DateTimeField', [], {}), + 'scheduled_end_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + u'core.group': { + 'Meta': {'ordering': "['experiment', 'number']", 'object_name': 'Group'}, + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Experiment']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'max_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '5'}), + 'number': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'session_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'blank': 'True'}) + }, + u'core.groupactivitylog': { + 'Meta': {'object_name': 'GroupActivityLog', '_ormbases': [u'core.ActivityLog']}, + u'activitylog_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['core.ActivityLog']", 'unique': 'True', 'primary_key': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_log_set'", 'to': u"orm['core.Group']"}), + 'round_configuration': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.RoundConfiguration']"}) + }, + u'core.groupcluster': { + 'Meta': {'ordering': "['date_created']", 'object_name': 'GroupCluster'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_cluster_set'", 'to': u"orm['core.Experiment']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'session_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'blank': 'True'}) + }, + u'core.groupclusterdatavalue': { + 'Meta': {'object_name': 'GroupClusterDataValue'}, + 'boolean_value': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'float_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'group_cluster': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'data_value_set'", 'to': u"orm['core.GroupCluster']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'int_value': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'parameter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Parameter']"}), + 'round_data': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_cluster_data_value_set'", 'to': u"orm['core.RoundData']"}), + 'string_value': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + u'core.grouprelationship': { + 'Meta': {'ordering': "['date_created']", 'object_name': 'GroupRelationship'}, + 'cluster': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_relationship_set'", 'to': u"orm['core.GroupCluster']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'core.grouprounddatavalue': { + 'Meta': {'ordering': "['round_data', 'group', 'parameter']", 'object_name': 'GroupRoundDataValue'}, + 'boolean_value': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'float_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'data_value_set'", 'to': u"orm['core.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'int_value': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'parameter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Parameter']"}), + 'round_data': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_data_value_set'", 'to': u"orm['core.RoundData']"}), + 'string_value': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + u'core.institution': { + 'Meta': {'object_name': 'Institution'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + u'core.invitation': { + 'Meta': {'object_name': 'Invitation'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'experiment_session': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.ExperimentSession']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Participant']"}), + 'sender': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + u'core.like': { + 'Meta': {'ordering': "['-date_created', 'round_data', 'participant_group_relationship', 'parameter']", 'object_name': 'Like', '_ormbases': [u'core.ParticipantRoundDataValue']}, + u'participantrounddatavalue_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['core.ParticipantRoundDataValue']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'core.parameter': { + 'Meta': {'ordering': "['name']", 'unique_together': "(('name', 'experiment_metadata', 'scope'),)", 'object_name': 'Parameter'}, + 'class_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Experimenter']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_value_string': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'display_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'enum_choices': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'experiment_metadata': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.ExperimentMetadata']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'scope': ('django.db.models.fields.CharField', [], {'default': "'round'", 'max_length': '32'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + u'core.participant': { + 'Meta': {'ordering': "['user']", 'object_name': 'Participant'}, + 'address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Address']", 'null': 'True', 'blank': 'True'}), + 'authentication_token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'birthdate': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'can_receive_invitations': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'experiments': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'participant_set'", 'symmetrical': 'False', 'through': u"orm['core.ParticipantExperimentRelationship']", 'to': u"orm['core.Experiment']"}), + 'failed_password_attempts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'gender': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'participant_set'", 'symmetrical': 'False', 'through': u"orm['core.ParticipantGroupRelationship']", 'to': u"orm['core.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'institution': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Institution']", 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'participant'", 'unique': 'True', 'to': u"orm['auth.User']"}) + }, + u'core.participantexperimentrelationship': { + 'Meta': {'object_name': 'ParticipantExperimentRelationship'}, + 'additional_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'current_location': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'participant_relationship_set'", 'to': u"orm['core.Experiment']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_completed_round_sequence_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'experiment_relationship_set'", 'to': u"orm['core.Participant']"}), + 'participant_identifier': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'sequential_participant_identifier': ('django.db.models.fields.PositiveIntegerField', [], {}) + }, + u'core.participantgrouprelationship': { + 'Meta': {'ordering': "['group', 'participant_number']", 'object_name': 'ParticipantGroupRelationship'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'first_visit': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'participant_group_relationship_set'", 'to': u"orm['core.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notifications_since': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'blank': 'True'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'participant_group_relationship_set'", 'to': u"orm['core.Participant']"}), + 'participant_number': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'round_joined': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.RoundConfiguration']"}), + 'survey_completed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + u'core.participantrounddatavalue': { + 'Meta': {'ordering': "['-date_created', 'round_data', 'participant_group_relationship', 'parameter']", 'object_name': 'ParticipantRoundDataValue'}, + 'boolean_value': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'float_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'int_value': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'parameter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Parameter']"}), + 'participant_group_relationship': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'data_value_set'", 'to': u"orm['core.ParticipantGroupRelationship']"}), + 'round_data': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'participant_data_value_set'", 'to': u"orm['core.RoundData']"}), + 'string_value': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'submitted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'target_data_value': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'target_data_value_set'", 'null': 'True', 'to': u"orm['core.ParticipantRoundDataValue']"}) + }, + u'core.participantsignup': { + 'Meta': {'object_name': 'ParticipantSignup'}, + 'attendance': ('django.db.models.fields.PositiveIntegerField', [], {'max_length': '1', 'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invitation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signup_set'", 'to': u"orm['core.Invitation']"}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signup_set'", 'to': u"orm['core.Participant']"}) + }, + u'core.quizquestion': { + 'Meta': {'object_name': 'QuizQuestion'}, + 'answer': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'default_quiz_question_set'", 'null': 'True', 'to': u"orm['core.Experiment']"}), + 'explanation': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'input_type': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + 'round_configuration': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'quiz_question_set'", 'to': u"orm['core.RoundConfiguration']"}) + }, + u'core.roundconfiguration': { + 'Meta': {'ordering': "['experiment_configuration', 'sequence_number', 'date_created']", 'object_name': 'RoundConfiguration'}, + 'chat_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'create_group_clusters': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'debriefing': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'display_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'duration': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'experiment_configuration': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'round_configuration_set'", 'to': u"orm['core.ExperimentConfiguration']"}), + 'group_cluster_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'initialize_data_values': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'instructions': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'preserve_existing_groups': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'randomize_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'repeat': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'round_type': ('django.db.models.fields.CharField', [], {'default': "'REGULAR'", 'max_length': '32'}), + 'sequence_number': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'session_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'blank': 'True'}), + 'survey_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'template_filename': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'template_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}) + }, + u'core.rounddata': { + 'Meta': {'ordering': "['round_configuration']", 'unique_together': "(('round_configuration', 'repeating_round_sequence_number', 'experiment'),)", 'object_name': 'RoundData'}, + 'elapsed_time': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'round_data_set'", 'to': u"orm['core.Experiment']"}), + 'experimenter_notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'repeating_round_sequence_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'round_configuration': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'round_data_set'", 'to': u"orm['core.RoundConfiguration']"}) + }, + u'core.roundparametervalue': { + 'Meta': {'ordering': "['round_configuration', 'parameter', 'date_created']", 'object_name': 'RoundParameterValue'}, + 'boolean_value': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'float_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'int_value': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'parameter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Parameter']"}), + 'round_configuration': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parameter_value_set'", 'to': u"orm['core.RoundConfiguration']"}), + 'string_value': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + u'core.spoolparticipantstatistics': { + 'Meta': {'object_name': 'SpoolParticipantStatistics'}, + 'absences': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'discharges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invitations': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'spool_statistics_set'", 'to': u"orm['core.Participant']"}), + 'participations': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + } + } + + complete_apps = ['core'] \ No newline at end of file diff -r 30a09795c0e26fda7149c2e9def3fd8d45d29d95 -r 0b6215b6b9c87fb970f4f1904406c6d9bdf6ed47 vcweb/core/models.py --- a/vcweb/core/models.py +++ b/vcweb/core/models.py @@ -274,6 +274,7 @@ exchange_rate = models.DecimalField(null=True, blank=True, default=0.2, max_digits=6, decimal_places=2, help_text=_('The exchange rate of currency per in-game token, e.g., dollars per token')) treatment_id = models.CharField(blank=True, max_length=32, help_text=_('An alphanumeric ID that should be unique to the set of ExperimentConfigurations for a given ExperimentMetadata')) is_experimenter_driven = models.BooleanField(default=True) + has_daily_rounds = models.BooleanField(default=False, help_text=_("This experiment configuration has rounds that start and end each day starting at midnight.")) """ Experimenter driven experiments have checkpoints where the experimenter needs to explicitly signal the system to move to the next round or stage. @@ -393,6 +394,7 @@ # how long this experiment should run in a date format # 1w2d = 1 week 2 days = 9d duration = models.CharField(max_length=32, blank=True) + start_date = models.DateField(null=True, blank=True, help_text=_("Used in conjunction with an ExperimentConfiguration with has_daily_rounds set, signifies that the experiment should activate automatically at a certain time.")) tick_duration = models.CharField(max_length=32, blank=True) """ how often the experiment_metadata server should tick. """ @@ -933,7 +935,7 @@ self.current_repeated_round_sequence_number = 0 else: logger.warning("trying to advance past the last round - no-op") - return + return None return self.start_round() def get_or_create_round_data(self, round_configuration=None, is_next_round_data=False): @@ -1017,7 +1019,7 @@ self.start_round() def restart_round(self): - self.stop_round() + self.end_round() self.start_round() def complete(self): @@ -1138,6 +1140,7 @@ PLAYABLE_ROUND_CONFIGURATIONS = (RoundType.PRACTICE, RoundType.REGULAR) experiment_configuration = models.ForeignKey(ExperimentConfiguration, related_name='round_configuration_set') + sequence_number = models.PositiveIntegerField(help_text='Used internally to determine the ordering of the rounds in an experiment in ascending order, e.g., 1,2,3,4,5') display_number = models.PositiveIntegerField(default=0, help_text='The round number to be displayed with this round. If set to zero, defaults to the internally used sequence_number.') @@ -1168,8 +1171,8 @@ template_id = models.CharField(max_length=128, blank=True, help_text=_('A HTML template ID to use in a single page app, e.g., KO template')) survey_url = models.URLField(null=True, blank=True) - """ external survey url """ - chat_enabled = models.BooleanField(default=False, help_text=_("Is chat enabled in this round?")) + """ external survey url for qualtrics integration """ + chat_enabled = models.BooleanField(default=False, help_text=_("Chat enabled")) create_group_clusters = models.BooleanField(default=False, help_text=_("Create relationships (clusters) of groups that can share group cluster data values")) group_cluster_size = models.PositiveIntegerField(null=True, blank=True, default=2, help_text=_("How many groups should form a cluster?")) randomize_groups = models.BooleanField(default=False, help_text=_("Shuffle participants into new groups when the round begins?")) @@ -2230,6 +2233,23 @@ """ return hasattr(user, 'participant') and isinstance(user.participant, Participant) +@receiver(signals.midnight_tick) +def update_daily_experiments(sender, time=None, start=None, **kwargs): + """ + signal handler for activating daily experiments + """ + # first handle inactive daily experiments that need to be activated + inactive_daily_experiments = Experiment.objects.inactive(experiment_configuration__has_daily_rounds=True) + today = datetime.today().date() + for e in inactive_daily_experiments: + if e.experiment_start_date == today: + logger.debug("activating experiment %s with start date of %s", e, e.experiment_start_date) + e.activate() + active_daily_experiments = Experiment.objects.active(experiment_configuration__has_daily_rounds=True) + for e in active_daily_experiments: + # FIXME: check for none result and end the experiment? + e.advance_to_next_round() + # signal handlers for socialauth @receiver(social_auth.signals.socialauth_registered, sender=None) def handle_new_socialauth_user(sender, user, response, details, **kwargs): 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. |
From: <com...@bi...> - 2013-08-06 09:20:32
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/30a09795c0e2/ Changeset: 30a09795c0e2 User: alllee Date: 2013-08-06 11:20:11 Summary: convert forestry page to KO model Affected #: 1 file diff -r 445a90839743c95a8a1e48fb46d06909a001ec3a -r 30a09795c0e26fda7149c2e9def3fd8d45d29d95 vcweb/forestry/templates/forestry/participate.html --- a/vcweb/forestry/templates/forestry/participate.html +++ b/vcweb/forestry/templates/forestry/participate.html @@ -1,8 +1,42 @@ {% extends "participant/base.html" %} -{% block title %} -{{ experiment.status_line }} -{% endblock %} -{% block content %} +{% load static %} +{% block head %} +{{ block.super }} +<link rel='stylesheet' href='{% static "css/forestry/style.css" %}'/> +{% endblock head %} +{% block page %} +<div class='row' id='page'> + <div id='content' class='span8'> + <div data-bind='template: { name: templateId() }'> + </div> + </div> + <div id='sidebar' class='span4 filler sidebar'> + <div data-bind='ifnot: isInstructionsRound'> + <h3>Chat</h3> + <div data-bind='ifnot: chatEnabled'> + <div class='alert alert-error'> + <i class='icon-warning-sign'></i> Chat is currently disabled. + </div> + </div> + <form id="chat-form" class='form-inline' data-bind='submit: submitChatMessage' > + <div class='input-prepend input-block-level'> + <span class='add-on'><i class='text-info icon-comment'></i></span> + <input id='chatMessage' type="text" placeholder="Chat with your group"> + </div> + </form> + <div class='chat-sidebar'> + <div id='chat-div'> + <div class='chat-messages' data-bind='foreach: chatMessages'> + <i class='icon-user muted'></i><strong data-bind='text: participant_number'></strong> + <small><i class='icon-double-angle-right'></i></small><span data-bind='text: value'></span> + <br> + </div> + </div> + </div> + </div> + </div> +</div> + <h3>Forestry round number {{ experiment.current_round.round_number }}</h3> {% if max_harvest_decision > 0 %} <div style="background: url('{{STATIC_URL}}images/forestry/{{tree.name}}.png'); width:{{max_width}}px; height: {{max_height}}px;"> 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. |
From: A L. <iss...@bi...> - 2013-07-31 22:08:05
|
New issue 115: lighter footprints fall 2013 revamp https://bitbucket.org/virtualcommons/vcweb/issue/115/lighter-footprints-fall-2013-revamp A Lee: 1. convert lighter footprints to a round-based experiment, where a new round is triggered at midnight, e.g., add a boolean flag to RoundConfiguration representing a "round ends at midnight" and a DateField for "round starts on this day" 2. activities will now be available according to a preset schedule, according to the round configuration, this will need additional parameters, including one to switch on whether or not this experiment is a preset scheduled or level based experiment Responsible: alllee |
From: <com...@bi...> - 2013-07-31 21:47:43
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/445a90839743/ Changeset: 445a90839743 User: alllee Date: 2013-07-31 23:47:24 Summary: moving to the end of the page. Affected #: 1 file diff -r 2b69e7ac3f2bb953c0ad0929ee9b7859b20a6cdb -r 445a90839743c95a8a1e48fb46d06909a001ec3a vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -309,15 +309,15 @@ At a size of <span data-bind='text: initialResourceLevel() / 2'></span> trees, the forest will produce <span data-bind='text: initialResourceLevel() / 10'></span> trees each season. </p> -<div class='flex-video widescreen'> -<iframe width='640' height='480' src='//www.youtube.com/embed/eQ-N61ISpBc?rel=0' frameborder='0' allowfullscreen></iframe> -</div><h4>Sustainable Harvesting</h4><p> The highest amount of harvesting that the forest can permanently support occurs when the growth rate is fastest. The highest sustainable amount that can be harvested, when the forest is at half its capacity, is 6 trees per individual or 24 trees per group. </p> +<div class='flex-video widescreen'> +<iframe width='640' height='480' src='//www.youtube.com/embed/eQ-N61ISpBc?rel=0' frameborder='0' allowfullscreen></iframe> +</div><ul class='pager'><li class='previous'><a href='javascript: void();' data-bind='click: activateTemplate.bind($data, "GENERAL_INSTRUCTIONS")' >Back</a> 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. |
From: <com...@bi...> - 2013-07-31 21:44:54
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/2b69e7ac3f2b/ Changeset: 2b69e7ac3f2b User: alllee Date: 2013-07-31 23:44:42 Summary: adding embedded video tutorial to general instructions Affected #: 2 files diff -r e605e0666250a51d43574e54b4899c60920642ac -r 2b69e7ac3f2bb953c0ad0929ee9b7859b20a6cdb vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -309,6 +309,9 @@ At a size of <span data-bind='text: initialResourceLevel() / 2'></span> trees, the forest will produce <span data-bind='text: initialResourceLevel() / 10'></span> trees each season. </p> +<div class='flex-video widescreen'> +<iframe width='640' height='480' src='//www.youtube.com/embed/eQ-N61ISpBc?rel=0' frameborder='0' allowfullscreen></iframe> +</div><h4>Sustainable Harvesting</h4><p> The highest amount of harvesting that the forest can permanently support occurs when the growth rate is fastest. The diff -r e605e0666250a51d43574e54b4899c60920642ac -r 2b69e7ac3f2bb953c0ad0929ee9b7859b20a6cdb vcweb/static/css/style.css --- a/vcweb/static/css/style.css +++ b/vcweb/static/css/style.css @@ -138,6 +138,28 @@ width: 100%; border-left-style: none; } +.flex-video { + position: relative; + padding-top: 25px; + padding-bottom: 67.5%; + height: 0; + margin-bottom: 16px; + overflow: hidden; +} +.flex-video.widescreen { padding-bottom: 57.25%; } +.flex-video.vimeo { padding-top: 0; } +.flex-video iframe, +.flex-video object, +.flex-video embed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +@media only screen and (max-device-width: 800px), only screen and (device-width: 1024px) and (device-height: 600px), only screen and (width: 1280px) and (orientation: landscape), only screen and (device-width: 800px), only screen and (max-width: 767px) { + .flex-video { padding-top: 0; } +} /* @font-face { font-family: Entypo; 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. |
From: <com...@bi...> - 2013-07-31 00:35:25
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/e605e0666250/ Changeset: e605e0666250 User: alllee Date: 2013-07-31 02:35:12 Summary: fixing scrolling to top on submit Affected #: 2 files diff -r c16380ae139343c422451ac4adf6806acc44c714 -r e605e0666250a51d43574e54b4899c60920642ac vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -679,7 +679,7 @@ getWebSocket().send(createReadyEvent(message)); model.activateTemplate("WAITING_ROOM"); }); - $(window).scrollTop(); + scrollToTop(); } model.numberOfTreesPerRow = ko.observable(10); model.isResourceEmpty = ko.computed(function() { @@ -762,7 +762,7 @@ model.submitted(true); model.secondsLeft(0); model.disableChatForm(); - $(window).scrollTop(); + scrollToTop(); }); } diff -r c16380ae139343c422451ac4adf6806acc44c714 -r e605e0666250a51d43574e54b4899c60920642ac vcweb/static/js/common.js --- a/vcweb/static/js/common.js +++ b/vcweb/static/js/common.js @@ -4,6 +4,10 @@ console.warn = console.warn || function(){}; console.error = console.error || function(){}; console.info = console.info || function(){}; +function scrollToTop() { + // $("html, body").animate({ scrollTop: 0 }, "fast"); + window.scrollTo(0, 0); +} function scrollToBottom(element) { element.scrollTop = element.scrollHeight; } 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. |
From: <com...@bi...> - 2013-07-31 00:30:18
|
2 new commits in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/397f9f21ccfd/ Changeset: 397f9f21ccfd User: alllee Date: 2013-07-31 02:11:46 Summary: inlining submit decision button with the harvest decision trees Affected #: 3 files diff -r 625673027ce87ccd0c6633646605aceb9182fbcf -r 397f9f21ccfd7bd632853348d9dfaf9d36a4847b vcweb/bound/static/css/bound/style.css --- a/vcweb/bound/static/css/bound/style.css +++ b/vcweb/bound/static/css/bound/style.css @@ -60,6 +60,10 @@ padding: 0px; margin: 0px; } +.submit-decision-button { + margin-top: -25px; + margin-left: 10px; +} col.current-player { background-color: #ffb914; } @@ -69,3 +73,4 @@ } .group-status { } + diff -r 625673027ce87ccd0c6633646605aceb9182fbcf -r 397f9f21ccfd7bd632853348d9dfaf9d36a4847b vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -457,7 +457,7 @@ <div class='span8'><h3>Harvest</h3> {% comment %} - FIXME: disabled + XXX: disabled <div data-bind='if: submitHarvestDecisionEnabled'><div class='alert alert-success'><i class='icon-leaf'></i> You have currently <span data-bind='text: submitted ? "submitted" : "selected"'></span> a harvest decision of <span class='badge badge-info' data-bind='text: harvestDecision'></span> trees. Click the 'Ok I'm ready' button to continue. @@ -475,7 +475,6 @@ {% csrf_token %} <div id='harvestDecisionDiv' class='control-group'><div data-bind='template: { name: "harvest-decision-multiselect-template" }'></div> - <button id='submitDecisionButton' data-bind='enable: submitHarvestDecisionEnabled, click: $root.submitDecision' type='submit' class='btn btn-primary'>Ok, I'm ready</button></div></form></div> @@ -545,7 +544,8 @@ </script><script type='text/html' id='harvest-decision-multiselect-template'><table> - <tr class='harvest-decision-resources' data-bind='foreach: harvestDecisionOptions'> + <tr class='harvest-decision-resources'> + <!-- ko foreach: harvestDecisionOptions --><td class='harvest-decision-tree-td' data-bind='attr: { id: "harvest-decision-td-" + $data }'><form data-bind='attr: { id: $root.harvestDecisionFormId($data) }'><input id='participantGroupId' type='hidden' name='participant_group_id' data-bind='value: $root.participantGroupId'/> @@ -556,6 +556,10 @@ </div></form></td> + <!-- /ko --> + <td> + <button id='submitDecisionButton' data-bind='enable: submitHarvestDecisionEnabled, click: $root.submitDecision' type='submit' class='btn btn-primary submit-decision-button'>Ok, I'm ready</button> + </td></tr></table></script> diff -r 625673027ce87ccd0c6633646605aceb9182fbcf -r 397f9f21ccfd7bd632853348d9dfaf9d36a4847b vcweb/core/templates/experimenter/monitor.html --- a/vcweb/core/templates/experimenter/monitor.html +++ b/vcweb/core/templates/experimenter/monitor.html @@ -25,13 +25,15 @@ </div><div class='btn-toolbar'><div class='btn-group'> + {% comment %} <a class='btn btn-danger' data-bind="click: confirmExperimentControllerAction.bind($data, true)" - data-action='restart_round' data-content='Restart the round. This will reset timers and perform any additional round started setup logic.'> + data-action='restart_round' data-content='Restart the round. This will reset timers and perform any additional round started setup logic.' data-original-title='Warning'><i class='icon-repeat'></i> restart round</a> + {% endcomment %} <a class='btn btn-danger' data-bind="click: confirmExperimentControllerAction.bind($data, true)" - data-action='restart' data-content='Restart the entire experiment. This will reset all data and start the experiment over at round 1.'> + data-action='restart' data-content='Restart the entire experiment. This will reset all data and start the experiment over at round 1.' data-original-title='Warning'><i class='icon-circle-arrow-up'></i> restart experiment</a> - <a class='btn btn-danger' data-bind='click: confirmExperimentControllerAction.bind($data, false)' data-action='deactivate' data-content='Deactivates this experiment.'><i class='icon-off'></i> deactivate</a> + <a class='btn btn-danger' data-bind='click: confirmExperimentControllerAction.bind($data, false)' data-action='deactivate' data-content='Deactivates this experiment and disables participant logins.'><i class='icon-off'></i> deactivate</a></div></div></div> https://bitbucket.org/virtualcommons/vcweb/commits/c16380ae1393/ Changeset: c16380ae1393 User: alllee Date: 2013-07-31 02:30:01 Summary: reducing whitespace in interface Affected #: 2 files diff -r 397f9f21ccfd7bd632853348d9dfaf9d36a4847b -r c16380ae139343c422451ac4adf6806acc44c714 vcweb/bound/static/css/bound/style.css --- a/vcweb/bound/static/css/bound/style.css +++ b/vcweb/bound/static/css/bound/style.css @@ -72,5 +72,11 @@ color: #145bff !important; } .group-status { + margin-bottom: 10px; +} +h3.compact { + line-height: 10px; + } +. diff -r 397f9f21ccfd7bd632853348d9dfaf9d36a4847b -r c16380ae139343c422451ac4adf6806acc44c714 vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -455,7 +455,7 @@ </div><div class='row' data-bind='ifnot: isResourceEmpty()'><div class='span8'> - <h3>Harvest</h3> + <h3 class='compact'>Harvest</h3> {% comment %} XXX: disabled <div data-bind='if: submitHarvestDecisionEnabled'> @@ -490,7 +490,7 @@ </div><div data-bind='css: { span4: canObserveOtherGroup }'> -<h4>My Group</h4> +<h3 class='compact'>My Group</h3><div class='alert alert-message'><strong class='badge badge-success' data-bind='text:resourceLevel'></strong> trees</div><div data-bind="visible: isResourceEmpty"><div class='well'> @@ -505,7 +505,7 @@ <div data-bind='style: { width: remainderResourceImageWidth(resourceLevel), height: resourceImageHeight() + "px", background: resourceImageBackgroundUrl }'></div></div><br> -<table class='table table-bordered table-condensed table-striped'> +<table class='group-status table table-bordered table-condensed table-striped'><tbody><tr><td>forest</td><td data-bind='text:resourceLevel'></td></tr><tr><td>average harvest</td><td data-bind='text:averageHarvest().toFixed(1)'></td></tr> 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. |
From: <com...@bi...> - 2013-07-30 23:23:52
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/625673027ce8/ Changeset: 625673027ce8 User: alllee Date: 2013-07-31 01:23:40 Summary: fixing typo s/earnings/savings when referring to raw tokens Affected #: 1 file diff -r fd88c8e85c9defe1b2c56de99aaa23894312000a -r 625673027ce87ccd0c6633646605aceb9182fbcf vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -362,7 +362,7 @@ </p><h3>Harvest Choice</h3><p> -Each round you will have a maximum of 1 minute to make a harvest decision. THere are two ways to submit a harvest +Each round you will have a maximum of 1 minute to make a harvest decision. There are two ways to submit a harvest decision: </p><ol> @@ -435,7 +435,7 @@ <!-- /ko --></tr><tr> - <td>Earnings</td> + <td>Savings</td><!-- ko foreach: playerData --><td data-bind='text: Math.max(0, storage()), css: { "current-player": id() === $root.participantGroupId() }'></td><!-- /ko --> 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. |
From: <com...@bi...> - 2013-07-30 08:08:49
|
2 new commits in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/532fd35d60db/ Changeset: 532fd35d60db User: alllee Date: 2013-07-30 08:52:19 Summary: s/savings point/token and other minor text changes Affected #: 2 files diff -r 6a6724dec000338606f54ccc0a2be3574c2adfde -r 532fd35d60db95fd414c5f5ad97b1089e34e4bc8 vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -73,7 +73,7 @@ <script type='text/html' id='PRACTICE_ROUND_RESULTS'><h3>Practice Round Results</h3><p> - Your final savings were <span class='badge badge-success' data-bind='text: storage'></span> tokens. + Your earned a total of <span class='badge badge-success' data-bind='text: storage'></span> tokens. If this had been been a paid session, you would have earned <strong class='text-success' data-bind='text: totalEarnings'></strong>. </p><p> @@ -264,21 +264,21 @@ <div id='instructions1'><h4>Harvesting and Profit</h4><p> -In this experiment you will harvest trees <i class='icon-leaf text-success'></i> to make a +In this experiment you will harvest trees to make a profit and survive. You may choose to harvest between <strong>0</strong> and <strong data-bind='text: maxHarvestDecision'></strong> trees during each round. -Each tree you harvest will earn you one savings point, and extra savings will carry over into the next round. +Each tree you harvest will earn you one token, and extra tokens will carry over into the next round. </p><h4>Cost of living</h4><p> Each round you must pay living expenses of <span class='badge badge-important' data-bind='text: costOfLiving'></span> -tokens. If at any point you do not have enough savings to pay the living expenses, you will become +tokens. If at any point you do not have enough tokens to pay the living expenses, you will become "deceased" for the remainder of the session, preventing you from harvesting or receiving payment for that session. You will need to wait until the session is complete before joining again. </p><h4>Cash payment</h4><p> -If you have extra savings remaining at the end of play, you will be paid in cash -<strong data-bind='text: formatCurrency(exchangeRate())'></strong> for each savings point. It is possible that you +If you have extra tokens remaining at the end of play, you will be paid in cash +<strong data-bind='text: formatCurrency(exchangeRate())'></strong> for each token. It is possible that you could earn as much as <strong data-bind='text: formatCurrency(maxEarnings())'></strong> for the entire experiment. However, the final outcome depends on your choices and those of your group members. </p> @@ -298,14 +298,14 @@ <p> Trees in the forest regrow much as real trees do. The more trees there are, the faster empty places fill up with new trees. It is possible to destroy the forest by overharvesting. If the forest is destroyed, no more trees will regrow -and there will be no more trees to harvest for the rest of the experiment. +and there will be no more trees to harvest for the rest of the session. </p><h4>Carrying Capacity</h4><p> -Initially there are <span class='badge badge-success' data-bind='text: initialResourceLevel'></span> trees in the +Initially there are <span class='badge badge-success'>240</span> trees in the forest. The forest grows new trees after every harvesting round, but the rate of growth depends on how large the forest is. The forest cannot grow at all with zero trees, or at its full size of <span data-bind='text:initialResourceLevel'></span> trees. -Instead, it grows fastest at one half of full size; <span data-bind='text: initialResourceLevel() / 2'></span> trees. +Instead, it grows fastest at one half of full size; <span class='badge badge-success'>120</span> trees. At a size of <span data-bind='text: initialResourceLevel() / 2'></span> trees, the forest will produce <span data-bind='text: initialResourceLevel() / 10'></span> trees each season. </p> @@ -333,11 +333,11 @@ In this experiment you will be sharing a virtual forest with the <span data-bind='text: participantsPerGroup() - 1'></span> other members in your group. All participants in the experiment are in identical conditions and must harvest trees to pay the cost of living (<span data-bind='text: costOfLiving'></span> tokens per round) to make a profit -and accrue savings. You will be able to text chat only with members of your own group. +and accrue tokens. You will be able to text chat only with members of your own group. </p><p> We will now begin with some <strong>practice rounds</strong>. These rounds will <strong>not</strong> contribute to your -savings. If you have any questions please raise your hand and someone will come to address your question. Otherwise, +tokens. If you have any questions please raise your hand and someone will come to address your question. Otherwise, please click the button to continue. </p><ul class='pager'> @@ -392,20 +392,20 @@ <div class='alert bound-status-dashboard span1'><h4 id="dashboard-last-harvest">Last Harvest <i class='icon-info-sign' data-content="The number of trees you harvested last round."></i></h4><p> - <strong class='text-success'><span data-bind='text:lastHarvestDecision'></span><i class='icon-leaf'></i></strong> + <strong class='text-success'><span data-bind='text:lastHarvestDecision'></span></strong></p></div><div class='alert bound-status-dashboard span1'> - <h4 id='dashboard-storage'>Total Earnings <i class='icon-info-sign' data-bind='attr: {"data-content": "You need at least " + costOfLiving() + " trees to survive each round."}'></i></h4> + <h4 id='dashboard-storage'>Savings <i class='icon-info-sign' data-bind='attr: {"data-content": "You need at least " + costOfLiving() + " trees to survive each round."}'></i></h4><p> - <strong data-bind='css: { "text-error": storage() < costOfLiving(), "text-success": storage() > costOfLiving()}'><span data-bind='text: clampedStorage'></span><i class='icon-leaf'></i></strong> + <strong><span data-bind='text: clampedStorage'></span></strong></p></div><div class='alert bound-status-dashboard span1'><h4 id='dashboard-time'><i class='icon-time'></i> Time</h4><p><strong data-bind='css: { "text-error": secondsLeft() < warningCountdownTime(), "text-info": secondsLeft() > warningCountdownTime()}'> - <span data-bind='text: secondsLeft'></span> s + <span data-bind='text: secondsLeft'></span></strong></p></div> @@ -561,7 +561,7 @@ </script><script type='text/html' id='harvest-decision-select-template'><div class='input-prepend'> - <span class='add-on'><i class='icon-leaf'></i></span> + <span class='add-on'></span><input id='participantGroupId' type='hidden' name='participant_group_id' data-bind='value: participantGroupId'/><input id='harvestDecisionTextInput' type='hidden' name='integer_decision' data-bind='value: harvestDecision'><select id='harvestDecisionSelect' name='harvest_decision' required="required" form="vcweb-form" data-bind='options: harvestDecisionOptions, value: harvestDecision'> @@ -675,6 +675,7 @@ getWebSocket().send(createReadyEvent(message)); model.activateTemplate("WAITING_ROOM"); }); + $(window).scrollTop(); } model.numberOfTreesPerRow = ko.observable(10); model.isResourceEmpty = ko.computed(function() { @@ -757,6 +758,7 @@ model.submitted(true); model.secondsLeft(0); model.disableChatForm(); + $(window).scrollTop(); }); } diff -r 6a6724dec000338606f54ccc0a2be3574c2adfde -r 532fd35d60db95fd414c5f5ad97b1089e34e4bc8 vcweb/vcweb-sockjs.py --- a/vcweb/vcweb-sockjs.py +++ b/vcweb/vcweb-sockjs.py @@ -12,6 +12,7 @@ sys.path.append( path.abspath(path.join(path.dirname(path.abspath(__file__)), '..')) ) os.environ['DJANGO_SETTINGS_MODULE'] = 'vcweb.settings' +from vcweb import settings from vcweb.core.models import (Experiment, ParticipantGroupRelationship, ParticipantExperimentRelationship, Participant, Experimenter, ChatMessage) # redefine logger @@ -401,7 +402,8 @@ app = web.Application(urls) logger.info("starting sockjs server on port %s", port) app.listen(port) - app.sentry_client = AsyncSentryClient('http://d266113006054187b70e3af60d9561f3:4f0...@vc...:9000/1') + if hasattr(settings, 'SENTRY_DSN'): + app.sentry_client = AsyncSentryClient(settings.SENTRY_DSN) ioloop.IOLoop.instance().start() if __name__ == '__main__': https://bitbucket.org/virtualcommons/vcweb/commits/fd88c8e85c9d/ Changeset: fd88c8e85c9d User: alllee Date: 2013-07-30 09:59:36 Summary: disabling tour Affected #: 2 files diff -r 532fd35d60db95fd414c5f5ad97b1089e34e4bc8 -r fd88c8e85c9defe1b2c56de99aaa23894312000a vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -721,7 +721,7 @@ if (model.templateId() != "REGULAR") { return; } - model.setupTour(); + // model.setupTour(); if (! model.alive()) { model.secondsLeft(0); model.clearCurrentInterval(); diff -r 532fd35d60db95fd414c5f5ad97b1089e34e4bc8 -r fd88c8e85c9defe1b2c56de99aaa23894312000a vcweb/bound/views.py --- a/vcweb/bound/views.py +++ b/vcweb/bound/views.py @@ -115,7 +115,7 @@ experiment_model_dict['templateName'] = current_round.template_name experiment_model_dict['isPracticeRound'] = current_round.is_practice_round # FIXME: only show the tour on the first practice round.. this is a bit brittle, maybe simply tie it to round #2? - experiment_model_dict['showTour'] = current_round.is_practice_round and not previous_round.is_practice_round + # experiment_model_dict['showTour'] = current_round.is_practice_round and not previous_round.is_practice_round # instructions round parameters if current_round.is_instructions_round: experiment_model_dict['isInstructionsRound'] = True 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. |
From: <com...@bi...> - 2013-07-28 08:08:58
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/6a6724dec000/ Changeset: 6a6724dec000 User: alllee Date: 2013-07-28 10:08:45 Summary: fixing tests, can't do straight string concatenatation with proxy objects Affected #: 2 files diff -r 048136b560820b2b9e299a33ad92ef56caf1ff14 -r 6a6724dec000338606f54ccc0a2be3574c2adfde vcweb/core/models.py --- a/vcweb/core/models.py +++ b/vcweb/core/models.py @@ -1126,12 +1126,12 @@ ROUND_TYPES_DICT = dict( 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'), - PRACTICE=('Practice round', 'practice.html'), - QUIZ=('Quiz round', 'quiz.html')) + REGULAR=(_('Regular experiment round'), 'participate.html'), + CHAT=(_('Chat round'), 'chat.html'), + DEBRIEFING=(_('Debriefing round'), 'debriefing.html'), + INSTRUCTIONS=(_('Instructions round'), 'instructions.html'), + PRACTICE=(_('Practice round'), 'practice.html'), + QUIZ=(_('Quiz round'), 'quiz.html')) ROUND_TYPES = (CHAT, DEBRIEFING, GENERAL_INSTRUCTIONS, INSTRUCTIONS, PRACTICE, QUIZ, REGULAR, WELCOME) = sorted(ROUND_TYPES_DICT.keys()) RoundType = Choices(*[(round_type, ROUND_TYPES_DICT[round_type][0]) for round_type in ROUND_TYPES]) diff -r 048136b560820b2b9e299a33ad92ef56caf1ff14 -r 6a6724dec000338606f54ccc0a2be3574c2adfde vcweb/core/tests.py --- a/vcweb/core/tests.py +++ b/vcweb/core/tests.py @@ -385,6 +385,6 @@ logger.debug("inspecting round type: %s with data %s", round_type, data) rc = self.create_new_round_configuration(round_type=round_type) e.current_round_sequence_number = rc.sequence_number - self.assertEqual(e.current_round_template, "%s/%s" % (e.namespace, data[1]), 'should have returned template for ' + data[0]) + self.assertEqual(e.current_round_template, "%s/%s" % (e.namespace, data[1]), 'should have returned template for %s' % data[0]) 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. |
From: <com...@bi...> - 2013-07-27 20:37:21
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/048136b56082/ Changeset: 048136b56082 User: alllee Date: 2013-07-27 22:36:45 Summary: setting up json response for completed_survey view function Affected #: 1 file diff -r c307e13b0c2e527a1153a3f2e29bfd298ef8fbad -r 048136b560820b2b9e299a33ad92ef56caf1ff14 vcweb/core/views.py --- a/vcweb/core/views.py +++ b/vcweb/core/views.py @@ -586,14 +586,20 @@ @participant_required def completed_survey(request): pgr_id = request.GET.get('pid', None) - if pgr_id is None: - participant = request.user.participant - experiment = get_active_experiment(participant) - pgr = experiment.get_participant_group_relationship(participant) - else: - pgr = get_object_or_404(ParticipantGroupRelationship, pk=pgr_id) - pgr.survey_completed = True - pgr.save() + success = False + try: + if pgr_id is None: + participant = request.user.participant + experiment = get_active_experiment(participant) + pgr = experiment.get_participant_group_relationship(participant) + else: + pgr = get_object_or_404(ParticipantGroupRelationship, pk=pgr_id) + pgr.survey_completed = True + pgr.save() + success = True + except ParticipantGroupRelationship.DoesNotExist as e: + logger.debug("No ParticipantGroupRelationship found with id %s", pgr_id) + return JsonResponse(dumps({'success': success})) @participant_required def check_survey_completed(request, pk=None): 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. |
From: <com...@bi...> - 2013-07-26 00:28:41
|
2 new commits in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/3d853fc699c8/ Changeset: 3d853fc699c8 User: alllee Date: 2013-07-26 02:26:42 Summary: adding basic treatment c instructions Affected #: 2 files diff -r d5ba79814c6b17d10d94087252167ce1732fc453 -r 3d853fc699c89c9e7e4281894e1b7b68af796e4c vcweb/bound/models.py --- a/vcweb/bound/models.py +++ b/vcweb/bound/models.py @@ -450,8 +450,6 @@ player_status_dv.update_boolean(False) # clamp storage to 0 to avoid negative earnings storage_dv.update_int(max(0, updated_storage)) - logger.debug("updating participant %s (storage: %s, harvest: %s, status: %s)", pgr, storage_dv.int_value, - harvest_decision, player_status_dv.boolean_value) pgr.copy_to_next_round(player_status_dv, storage_dv, next_round_data=next_round_data) @receiver(signals.round_ended, sender=EXPERIMENT_METADATA_NAME) diff -r d5ba79814c6b17d10d94087252167ce1732fc453 -r 3d853fc699c89c9e7e4281894e1b7b68af796e4c vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -190,6 +190,26 @@ </li></ul></script> +<script type='text/html' id='TREATMENT_C_INSTRUCTIONS'> + <h3>Session <span data-bind='text: sessionId'></span> Instructions</h3> + <p> + In this session, there are two groups or villages. Both groups share a forest to harvest. The shared forest has + twice the capacity of a single-group forest. In addition, you may observe the other group, their storage amounts, + and their forest status, and they may observe your group in the same way. + </p> + <h3>Key points</h3> + <ul> + <li>Two new groups formed at random</li> + <li>Groups share a single forest</li> + <li>You may observe the other group</li> + <li>The other group may observe your group</li> + </ul> + <ul class='pager'> + <li class='next'> + <a href='javascript: void()' data-bind='click: participantReady.bind($data, participantGroupId() + " finished treatment C instructions")'>Ok, I am ready to start</a> + </li> + </ul> +</script><script type='text/html' id='TREATMENT_D_INSTRUCTIONS'><h3>Session <span data-bind='text: sessionId'></span> Instructions</h3><p> https://bitbucket.org/virtualcommons/vcweb/commits/c307e13b0c2e/ Changeset: c307e13b0c2e User: alllee Date: 2013-07-26 02:28:28 Summary: disabling automatic advancing for the time being, double invocations are still being detected in the logs causing duplicate group and participant data values. There isn't really a way for advance to next round to be idempotent. Possible solution is to trigger advancing to the next round when all participants are ready on the server side only and then notify & update the experimenter and participants accordingly. Affected #: 1 file diff -r 3d853fc699c89c9e7e4281894e1b7b68af796e4c -r c307e13b0c2e527a1153a3f2e29bfd298ef8fbad vcweb/core/templates/experimenter/monitor.html --- a/vcweb/core/templates/experimenter/monitor.html +++ b/vcweb/core/templates/experimenter/monitor.html @@ -334,10 +334,10 @@ 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 the next round"); + model.addMessage("All participants are ready, ready to 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() + // invoking advance to next round twice. See https://bitbucket.org/virtualcommons/vcweb/issue/113/ + // model.advanceToNextRound() } }); model.isCheckingParticipants(false); 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. |
From: A L. <iss...@bi...> - 2013-07-25 22:26:57
|
New issue 114: migrate sentry to ssl https://bitbucket.org/virtualcommons/vcweb/issue/114/migrate-sentry-to-ssl A Lee: Need to re-enable sentry on SSL, still haven't heard back from getsentry.com about a free academic account. Will likely need to do something like https://gist.github.com/j4mie/1778918 but with apache / mod_rewrite. Sentry configuration in sentry.conf.py should be similar. Responsible: alllee |
From: <com...@bi...> - 2013-07-25 20:43:05
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/d5ba79814c6b/ Changeset: d5ba79814c6b User: alllee Date: 2013-07-25 22:42:50 Summary: adding TREATMENT_B_INSTRUCTIONS Affected #: 3 files diff -r c7b9411d3f90cf7c6074269351d54e83f2d55753 -r d5ba79814c6b17d10d94087252167ce1732fc453 vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -173,6 +173,23 @@ </li></ul></script> +<script type='text/html' id='TREATMENT_B_INSTRUCTIONS'> + <h3>Session <span data-bind='text: sessionId'></span> Instructions</h3> + <p> + In this session, there are two groups or villages. Each group has a forest that the <b>other group cannot harvest</b>. + Both forests have the same growth capacity per person. + </p> + <h3>Key points</h3> + <ul> + <li>Two new groups formed at random</li> + <li>Each group has an exclusive forest of the same size</li> + </ul> + <ul class='pager'> + <li class='next'> + <a href='javascript: void()' data-bind='click: participantReady.bind($data, participantGroupId() + " finished treatment B instructions")'>Ok, I am ready to start</a> + </li> + </ul> +</script><script type='text/html' id='TREATMENT_D_INSTRUCTIONS'><h3>Session <span data-bind='text: sessionId'></span> Instructions</h3><p> diff -r c7b9411d3f90cf7c6074269351d54e83f2d55753 -r d5ba79814c6b17d10d94087252167ce1732fc453 vcweb/core/models.py --- a/vcweb/core/models.py +++ b/vcweb/core/models.py @@ -769,7 +769,7 @@ logger.debug("Aborting, round configuration isn't set to initialize data values") return elif round_configuration.is_repeating_round and self.current_repeated_round_sequence_number > 0: - logger.debug("repeating round # %d - aborting data value initialization", self.current_repeated_round_sequence_number) + logger.debug("ignoring initialize data values for repeating round # %d", self.current_repeated_round_sequence_number) return logger.debug("initializing data values for [participant params: %s] [group parameters: %s] [group_cluster_parameters: %s] ", participant_parameters, group_parameters, group_cluster_parameters) diff -r c7b9411d3f90cf7c6074269351d54e83f2d55753 -r d5ba79814c6b17d10d94087252167ce1732fc453 vcweb/core/templates/experimenter/monitor.html --- a/vcweb/core/templates/experimenter/monitor.html +++ b/vcweb/core/templates/experimenter/monitor.html @@ -95,14 +95,17 @@ <div class='alert alert-message alert-block'><h4><i class='icon-download'></i> Download Data</h4><ul class='inline'> - <li><a href='download/csv'><img src='{{STATIC_URL}}images/famfamfam/page_white_text.png' alt='csv' />csv</a></li> - <li><a href='download/xls'><img src='{{STATIC_URL}}images/famfamfam/page_white_excel.png' alt='xls'/>xls</a></li> + <li><a href='download/csv'><i class='icon-file-text'></i> csv</a></li> + <li><a href='download/xls'><img src='{{STATIC_URL}}images/famfamfam/page_white_excel.png' alt='xls'/> excel</a></li></ul> + {% comment %} + FIXME: disabled for the time being <h4><i class='icon-download-alt'></i>Download Configuration Files</h4><ul class='inline'> - <li><a href='export/configuration.xml'><i class='icon-cog'></i>xml</a></li> - <li><a href='export/configuration.json'><i class='icon-pencil'></i>json</a></li> + <li><a href='export/configuration.xml'><i class='icon-cog'></i> xml</a></li> + <li><a href='export/configuration.json'><i class='icon-pencil'></i> json</a></li></ul> + {% endcomment %} <a class='btn btn-danger' data-bind='css: { disabled: isArchived}, click: confirmExperimentControllerAction.bind($data, false)' data-action='complete' data-content='Mark this experiment as completed and archive it.'><i class='icon-save'></i> archive</a></div></div> 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. |
From: <com...@bi...> - 2013-07-25 08:23:03
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/c7b9411d3f90/ Changeset: c7b9411d3f90 User: alllee Date: 2013-07-25 10:22:50 Summary: fixing setInterval bug, needs to be pushed into the practice round results template only, not just when the survey is enabled otherwise it'll keep redirecting to the instructions page. Affected #: 1 file diff -r 2f5ef7df071be592cca7078232ea17c7c73112fa -r c7b9411d3f90cf7c6074269351d54e83f2d55753 vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -782,19 +782,20 @@ } model.afterRenderTemplate = function(elements) { if (model.isSurveyEnabled()) { - model.setCurrentInterval( - setInterval(function() { - $.get('/participate/{{experiment.pk}}/check-survey-completed', function(response) { - model.surveyCompleted(response.survey_completed); - if (model.surveyCompleted()) { - model.clearCurrentInterval(); - model.practiceRoundSurveyCompleted(); - } - }); - }, - 6000)); switch (model.templateId()) { case "PRACTICE_ROUND_RESULTS": + // FIXME: only set this interval check if we're in the practice round results section. + model.setCurrentInterval( + setInterval(function() { + $.get('/participate/{{experiment.pk}}/check-survey-completed', function(response) { + model.surveyCompleted(response.survey_completed); + if (model.surveyCompleted()) { + model.clearCurrentInterval(); + model.practiceRoundSurveyCompleted(); + } + }); + }, 6000) + ); case "FINAL_DEBRIEFING": $('#sidebar').hide(); $('#content').removeClass('span8').addClass('span12'); 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. |
From: <com...@bi...> - 2013-07-25 08:16:04
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/2f5ef7df071b/ Changeset: 2f5ef7df071b User: alllee Date: 2013-07-25 10:15:48 Summary: making class setting more explicit than a toggle, survey refresh is causing some issues with the chat sidebar Affected #: 1 file diff -r 6baee48ca9aaa79110d72137ea7ddeaf3c910b65 -r 2f5ef7df071be592cca7078232ea17c7c73112fa vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -625,8 +625,8 @@ return; } model.clearCurrentInterval(); + $('#content').removeClass('span12').addClass('span8'); $('#sidebar').show(); - $('#content').toggleClass('span8 span12'); model.activateTemplate("PAID_EXPERIMENT_INSTRUCTIONS"); }; model.readyParticipantsPercentage = ko.computed(function() { @@ -797,7 +797,9 @@ case "PRACTICE_ROUND_RESULTS": case "FINAL_DEBRIEFING": $('#sidebar').hide(); - $('#content').toggleClass('span8 span12'); + $('#content').removeClass('span8').addClass('span12'); + break; + default: break; } } 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. |
From: <com...@bi...> - 2013-07-25 00:31:57
|
2 new commits in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/e753c6a9b1da/ Changeset: e753c6a9b1da User: alllee Date: 2013-07-25 02:28:44 Summary: fixing bug in group.log during resource depletion Affected #: 1 file diff -r 69702eed89fcbc307fa20a90ac73754fde978751 -r e753c6a9b1daebbf39bd8cbbb52fe530f891bb52 vcweb/bound/models.py --- a/vcweb/bound/models.py +++ b/vcweb/bound/models.py @@ -296,8 +296,7 @@ existing_resource_level = get_resource_level_dv(group, round_data, round_configuration, shared_resource_enabled=shared_resource_enabled) if existing_resource_level.int_value <= 0: - group.log("setting all participant ready flags because of depleted resource %s", - existing_resource_level) + group.log("setting all participant ready flags because of depleted resource %s" % existing_resource_level) _zero_harvest_decisions(group.participant_group_relationship_set.all(), round_data) # check for dead participants and set their ready and harvest decision flags https://bitbucket.org/virtualcommons/vcweb/commits/6baee48ca9aa/ Changeset: 6baee48ca9aa User: alllee Date: 2013-07-25 02:31:42 Summary: adding varargs to experiment / group loggers Affected #: 1 file diff -r e753c6a9b1daebbf39bd8cbbb52fe530f891bb52 -r 6baee48ca9aaa79110d72137ea7ddeaf3c910b65 vcweb/core/models.py --- a/vcweb/core/models.py +++ b/vcweb/core/models.py @@ -797,9 +797,10 @@ defaults=parameter_defaults[parameter]) logger.debug("prdv: %s (%s)", participant_data_value, created) - def log(self, log_message): + def log(self, log_message, *args, **kwargs): if log_message: - logger.debug("%s: %s", self, log_message) + log_message = "%s: %s" % (self, log_message) + logger.debug(log_message, *args) self.activity_log_set.create(round_configuration=self.current_round, log_message=log_message) def configuration_file_name(self, file_ext='.xml'): 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. |
From: <com...@bi...> - 2013-07-24 22:58:59
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/69702eed89fc/ Changeset: 69702eed89fc User: alllee Date: 2013-07-25 00:58:47 Summary: client side logic cleanup to fix a few bugs around tour initialization / destruction and time remaining not being displayed / initialized properly on the first practice round (round 3.0) - timeRemaining was not being set *before* afterRenderTemplate was invoked to switch the template, so invoking startRound in afterRenderTemplate was failing to setInterval properly. Fixed by pushing startRound() into update() and on first page load and encapsulating logic on whether or not to execute in startRound itself - Experiment.time_remaining now always returns a number instead of sometimes a number and sometimes a string Affected #: 3 files diff -r 97acfa367928126f0dff098ab24d8ce89785955d -r 69702eed89fcbc307fa20a90ac73754fde978751 vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -7,7 +7,7 @@ {% block page %} <div class='row' id='page'><div id='content' class='span8'> - <div data-bind='template: { name: templateId(), afterRender: afterRenderTemplate }'> + <div data-bind='template: { name: templateId(), afterRender: afterRenderTemplate}'></div><div id='progress-modal' class='modal hide fade'><div class='model-header'> @@ -569,6 +569,9 @@ } }); model.setupTour = function() { + if (! model.showTour()) { + return; + } var tour = new Tour(); model.tour(tour); tour.addStep({ element: "#dashboard-last-harvest", title: "Last harvest", @@ -589,7 +592,7 @@ tour.addStep({ element: "#harvest-decision-td-10", title: "Harvest Decision", placement: "right", content: "Click on the number of trees you'd like to harvest here. For the purposes of this practice round, please select 10 now and click the 'Ok, I'm ready' button."}); - tour.start(); + tour.start(true); } model.endTour = function() { var tour = model.tour(); @@ -676,16 +679,19 @@ return (remainder * model.resourceImageWidth()) + "px"; }; model.startRound = function() { - if (model.showTour()) { - model.setupTour(); + model.initializeChat(); + if (model.templateId() != "REGULAR") { + return; } - if (!model.alive()) { + model.setupTour(); + if (! model.alive()) { model.secondsLeft(0); model.clearCurrentInterval(); return; } - if (isInt(model.timeRemaining()) && model.timeRemaining() > 0) { - model.initializeChat(); + console.debug("starting round, checking time remaining: " + model.timeRemaining()); + if (model.timeRemaining() > 0) { + console.debug("setting seconds left to: " + model.timeRemaining()); model.secondsLeft(model.timeRemaining()); model.setCurrentInterval( setInterval(function() { @@ -745,6 +751,7 @@ $.get('view-model', { participant_group_id: model.participantGroupId() }, function(data) { ko.mapping.fromJS(data, model); $('#progress-modal').modal('hide'); + model.startRound(); }); } model.setFormDisabled = function(formId, disabled) { @@ -774,9 +781,6 @@ model.chatEnabled(! chatDisabled); } model.afterRenderTemplate = function(elements) { - if (model.templateId() === "REGULAR") { - model.startRound() - } if (model.isSurveyEnabled()) { model.setCurrentInterval( setInterval(function() { @@ -825,6 +829,7 @@ break; } }; + experimentModel.startRound(); return experimentModel; } model = initialize($.parseJSON("{{ experimentModelJson|escapejs }}")); diff -r 97acfa367928126f0dff098ab24d8ce89785955d -r 69702eed89fcbc307fa20a90ac73754fde978751 vcweb/bound/views.py --- a/vcweb/bound/views.py +++ b/vcweb/bound/views.py @@ -104,7 +104,6 @@ previous_round = experiment.previous_round previous_round_data = experiment.get_round_data(round_configuration=previous_round, previous_round=True) experiment_model_dict = experiment.to_dict(include_round_data=False, default_value_dict=experiment_model_defaults) - logger.debug("returning view model json for round %s" % current_round) # round / experiment configuration data experiment_model_dict['timeRemaining'] = experiment.time_remaining diff -r 97acfa367928126f0dff098ab24d8ce89785955d -r 69702eed89fcbc307fa20a90ac73754fde978751 vcweb/core/models.py --- a/vcweb/core/models.py +++ b/vcweb/core/models.py @@ -432,12 +432,16 @@ @property def time_remaining(self): if self.is_timed_round: - tr = self.current_round.duration - self.current_round_elapsed_time.seconds - if tr <= 0: - return u"Expired (%s seconds ago)" % abs(tr) + return max(0, self.current_round.duration - self.current_round_elapsed_time.seconds) + return -1 + + @property + def time_remaining_label(self): + tr = self.time_remaining + if tr < 0: + return "Untimed round, advance manually or via automated checkpointing" + else: return tr - else: - return "Untimed round (advance manually or via automated checkpointing)" @property def is_timed_round(self): 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. |
From: <com...@bi...> - 2013-07-24 01:22:22
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/97acfa367928/ Changeset: 97acfa367928 User: alllee Date: 2013-07-24 03:22:09 Summary: capturing pid from query params for more direct access to the participant completing the survey Affected #: 1 file diff -r 987921500c7c8fe83e24edb28ee1bdf825d2c093 -r 97acfa367928126f0dff098ab24d8ce89785955d vcweb/core/views.py --- a/vcweb/core/views.py +++ b/vcweb/core/views.py @@ -585,9 +585,13 @@ @participant_required def completed_survey(request): - participant = request.user.participant - experiment = get_active_experiment(participant) - pgr = experiment.get_participant_group_relationship(participant) + pgr_id = request.GET.get('pid', None) + if pgr_id is None: + participant = request.user.participant + experiment = get_active_experiment(participant) + pgr = experiment.get_participant_group_relationship(participant) + else: + pgr = get_object_or_404(ParticipantGroupRelationship, pk=pgr_id) pgr.survey_completed = True pgr.save() 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. |
From: <com...@bi...> - 2013-07-24 01:12:34
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/987921500c7c/ Changeset: 987921500c7c User: alllee Date: 2013-07-24 03:12:20 Summary: adding basic survey integration to "know" when a survey has been completed: 1. end of survey redirects to /participate/survey-completed?pid=<participant_group_id> 2. this sets a flag on the given participant group relationship (TODO: brittle, should include the pid as well if possible 3. when rendering a survey, setInterval is used to periodically poll for the participant_group_relationship.survey_completed flag 4. when set to true, move on to the rest of the instructions and clear out the current interval 5. at the end of every round, survey completed flags are reset to False Affected #: 6 files diff -r a054cd4e45f617d5be397be39048fd4fd5a3b081 -r 987921500c7c8fe83e24edb28ee1bdf825d2c093 vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -86,13 +86,9 @@ {% comment %} need to disable this until the survey is completed {% endcomment %} - <div class='alert alert-error'> - FIXME: this button should be initially disabled when we wire up proper activation from the qualtrics survey and then enabled after the - qualtrics survey is completed - </div> - <ul class='pager'> - <li class='next'> - <a href='javascript: void()' data-bind='click: completePracticeRoundSurvey'>Ok, continue</a> + <ul class='pager' data-bind='visible: surveyCompleted()'> + <li class='next' data-bind='css: { disabled: ! surveyCompleted() }'> + <a href='javascript: void()' data-bind='click: practiceRoundSurveyCompleted'>Ok, continue</a></li></ul></script> @@ -621,9 +617,12 @@ model.activateTemplate = function(name, experimentModel) { model.templateName(name); } - model.completePracticeRoundSurvey = function() { - console.debug("completing practice round survey"); - $('#sidebar').show(); + model.practiceRoundSurveyCompleted = function() { + if (! model.surveyCompleted()) { + return; + } + model.clearCurrentInterval(); + $('#sidebar').show(); $('#content').toggleClass('span8 span12'); model.activateTemplate("PAID_EXPERIMENT_INSTRUCTIONS"); }; @@ -779,6 +778,17 @@ model.startRound() } if (model.isSurveyEnabled()) { + model.setCurrentInterval( + setInterval(function() { + $.get('/participate/{{experiment.pk}}/check-survey-completed', function(response) { + model.surveyCompleted(response.survey_completed); + if (model.surveyCompleted()) { + model.clearCurrentInterval(); + model.practiceRoundSurveyCompleted(); + } + }); + }, + 6000)); switch (model.templateId()) { case "PRACTICE_ROUND_RESULTS": case "FINAL_DEBRIEFING": diff -r a054cd4e45f617d5be397be39048fd4fd5a3b081 -r 987921500c7c8fe83e24edb28ee1bdf825d2c093 vcweb/bound/views.py --- a/vcweb/bound/views.py +++ b/vcweb/bound/views.py @@ -93,6 +93,7 @@ 'averageStorage': 0, 'numberAlive': '4 out of 4', 'isSurveyEnabled': False, + 'surveyCompleted': False, 'surveyUrl': 'http://survey.qualtrics.com/SE/?SID=SV_0vzmIj5UsOgjoTX', } # FIXME: bloated method with too many special cases, try to refactor @@ -144,6 +145,7 @@ separator = '&' experiment_model_dict['surveyUrl'] = "{0}{1}{2}".format(current_round.survey_url, separator, query_parameters) experiment_model_dict['isSurveyEnabled'] = True + experiment_model_dict['surveyCompleted'] = participant_group_relationship.survey_completed logger.debug("survey was enabled, setting survey url to %s", experiment_model_dict['surveyUrl']) diff -r a054cd4e45f617d5be397be39048fd4fd5a3b081 -r 987921500c7c8fe83e24edb28ee1bdf825d2c093 vcweb/core/migrations/0010_auto__add_field_participantgrouprelationship_survey_completed.py --- /dev/null +++ b/vcweb/core/migrations/0010_auto__add_field_participantgrouprelationship_survey_completed.py @@ -0,0 +1,410 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'ParticipantGroupRelationship.survey_completed' + db.add_column(u'core_participantgrouprelationship', 'survey_completed', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'ParticipantGroupRelationship.survey_completed' + db.delete_column(u'core_participantgrouprelationship', 'survey_completed') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'core.activitylog': { + 'Meta': {'object_name': 'ActivityLog'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'log_message': ('django.db.models.fields.TextField', [], {}) + }, + u'core.address': { + 'Meta': {'object_name': 'Address'}, + 'city': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'street1': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'street2': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'zipcode': ('django.db.models.fields.CharField', [], {'max_length': '8', 'blank': 'True'}) + }, + u'core.bookmarkedexperimentmetadata': { + 'Meta': {'ordering': "['experimenter', 'experiment_metadata']", 'unique_together': "(('experimenter', 'experiment_metadata'),)", 'object_name': 'BookmarkedExperimentMetadata'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'experiment_metadata': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bookmarked_experiment_metadata_set'", 'to': u"orm['core.ExperimentMetadata']"}), + 'experimenter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bookmarked_experiment_metadata_set'", 'to': u"orm['core.Experimenter']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'core.chatmessage': { + 'Meta': {'ordering': "['-date_created']", 'object_name': 'ChatMessage', '_ormbases': [u'core.ParticipantRoundDataValue']}, + u'participantrounddatavalue_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['core.ParticipantRoundDataValue']", 'unique': 'True', 'primary_key': 'True'}), + 'target_participant': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'target_participant_chat_message_set'", 'null': 'True', 'to': u"orm['core.ParticipantGroupRelationship']"}) + }, + u'core.comment': { + 'Meta': {'ordering': "['-date_created']", 'object_name': 'Comment', '_ormbases': [u'core.ParticipantRoundDataValue']}, + u'participantrounddatavalue_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['core.ParticipantRoundDataValue']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'core.experiment': { + 'Meta': {'ordering': "['date_created', 'status']", 'object_name': 'Experiment'}, + 'amqp_exchange_name': ('django.db.models.fields.CharField', [], {'default': "'vcweb.default.exchange'", 'max_length': '64'}), + 'authentication_code': ('django.db.models.fields.CharField', [], {'default': "'vcweb.auth.code'", 'max_length': '32'}), + 'current_repeated_round_sequence_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'current_round_sequence_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'current_round_start_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_activated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'duration': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'experiment_configuration': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.ExperimentConfiguration']"}), + 'experiment_metadata': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.ExperimentMetadata']"}), + 'experimenter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Experimenter']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'INACTIVE'", 'max_length': '32'}), + 'tick_duration': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}) + }, + u'core.experimentactivitylog': { + 'Meta': {'object_name': 'ExperimentActivityLog', '_ormbases': [u'core.ActivityLog']}, + u'activitylog_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['core.ActivityLog']", 'unique': 'True', 'primary_key': 'True'}), + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_log_set'", 'to': u"orm['core.Experiment']"}), + 'round_configuration': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.RoundConfiguration']"}) + }, + u'core.experimentconfiguration': { + 'Meta': {'ordering': "['experiment_metadata', 'creator', 'date_created']", 'object_name': 'ExperimentConfiguration'}, + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'experiment_configuration_set'", 'to': u"orm['core.Experimenter']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'exchange_rate': ('django.db.models.fields.DecimalField', [], {'default': '0.2', 'null': 'True', 'max_digits': '6', 'decimal_places': '2', 'blank': 'True'}), + 'experiment_metadata': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'experiment_configuration_set'", 'to': u"orm['core.ExperimentMetadata']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invitation_subject': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'invitation_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_experimenter_driven': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'max_group_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '5'}), + 'max_number_of_participants': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'treatment_id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}) + }, + u'core.experimenter': { + 'Meta': {'ordering': "['user']", 'object_name': 'Experimenter'}, + 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'authentication_token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'failed_password_attempts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'institution': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Institution']", 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'experimenter'", 'unique': 'True', 'to': u"orm['auth.User']"}) + }, + u'core.experimenterrequest': { + 'Meta': {'object_name': 'ExperimenterRequest'}, + 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) + }, + u'core.experimentmetadata': { + 'Meta': {'ordering': "['namespace', 'date_created']", 'object_name': 'ExperimentMetadata'}, + 'about_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_configuration': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.ExperimentConfiguration']", 'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'namespace': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'short_name': ('django.db.models.fields.SlugField', [], {'max_length': '32', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'core.experimentparametervalue': { + 'Meta': {'object_name': 'ExperimentParameterValue'}, + 'boolean_value': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'experiment_configuration': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parameter_value_set'", 'to': u"orm['core.ExperimentConfiguration']"}), + 'float_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'int_value': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'parameter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Parameter']"}), + 'string_value': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + u'core.experimentsession': { + 'Meta': {'object_name': 'ExperimentSession'}, + 'capacity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '20'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'experiment_session_set'", 'to': u"orm['auth.User']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'experiment_metadata': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'experiment_session_set'", 'to': u"orm['core.ExperimentMetadata']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invitation_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'scheduled_date': ('django.db.models.fields.DateTimeField', [], {}), + 'scheduled_end_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + u'core.group': { + 'Meta': {'ordering': "['experiment', 'number']", 'object_name': 'Group'}, + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Experiment']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'max_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '5'}), + 'number': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'session_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'blank': 'True'}) + }, + u'core.groupactivitylog': { + 'Meta': {'object_name': 'GroupActivityLog', '_ormbases': [u'core.ActivityLog']}, + u'activitylog_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['core.ActivityLog']", 'unique': 'True', 'primary_key': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_log_set'", 'to': u"orm['core.Group']"}), + 'round_configuration': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.RoundConfiguration']"}) + }, + u'core.groupcluster': { + 'Meta': {'ordering': "['date_created']", 'object_name': 'GroupCluster'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_cluster_set'", 'to': u"orm['core.Experiment']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'session_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'blank': 'True'}) + }, + u'core.groupclusterdatavalue': { + 'Meta': {'object_name': 'GroupClusterDataValue'}, + 'boolean_value': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'float_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'group_cluster': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'data_value_set'", 'to': u"orm['core.GroupCluster']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'int_value': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'parameter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Parameter']"}), + 'round_data': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_cluster_data_value_set'", 'to': u"orm['core.RoundData']"}), + 'string_value': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + u'core.grouprelationship': { + 'Meta': {'ordering': "['date_created']", 'object_name': 'GroupRelationship'}, + 'cluster': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_relationship_set'", 'to': u"orm['core.GroupCluster']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'core.grouprounddatavalue': { + 'Meta': {'ordering': "['round_data', 'group', 'parameter']", 'object_name': 'GroupRoundDataValue'}, + 'boolean_value': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'float_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'data_value_set'", 'to': u"orm['core.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'int_value': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'parameter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Parameter']"}), + 'round_data': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_data_value_set'", 'to': u"orm['core.RoundData']"}), + 'string_value': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + u'core.institution': { + 'Meta': {'object_name': 'Institution'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + u'core.invitation': { + 'Meta': {'object_name': 'Invitation'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'experiment_session': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.ExperimentSession']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Participant']"}), + 'sender': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + u'core.like': { + 'Meta': {'ordering': "['-date_created', 'round_data', 'participant_group_relationship', 'parameter']", 'object_name': 'Like', '_ormbases': [u'core.ParticipantRoundDataValue']}, + u'participantrounddatavalue_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['core.ParticipantRoundDataValue']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'core.parameter': { + 'Meta': {'ordering': "['name']", 'unique_together': "(('name', 'experiment_metadata', 'scope'),)", 'object_name': 'Parameter'}, + 'class_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Experimenter']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_value_string': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'display_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'enum_choices': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'experiment_metadata': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.ExperimentMetadata']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'scope': ('django.db.models.fields.CharField', [], {'default': "'round'", 'max_length': '32'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + u'core.participant': { + 'Meta': {'ordering': "['user']", 'object_name': 'Participant'}, + 'address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Address']", 'null': 'True', 'blank': 'True'}), + 'authentication_token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'birthdate': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'can_receive_invitations': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'experiments': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'participant_set'", 'symmetrical': 'False', 'through': u"orm['core.ParticipantExperimentRelationship']", 'to': u"orm['core.Experiment']"}), + 'failed_password_attempts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'gender': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'participant_set'", 'symmetrical': 'False', 'through': u"orm['core.ParticipantGroupRelationship']", 'to': u"orm['core.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'institution': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Institution']", 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'participant'", 'unique': 'True', 'to': u"orm['auth.User']"}) + }, + u'core.participantexperimentrelationship': { + 'Meta': {'object_name': 'ParticipantExperimentRelationship'}, + 'additional_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'current_location': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'participant_relationship_set'", 'to': u"orm['core.Experiment']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_completed_round_sequence_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'experiment_relationship_set'", 'to': u"orm['core.Participant']"}), + 'participant_identifier': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'sequential_participant_identifier': ('django.db.models.fields.PositiveIntegerField', [], {}) + }, + u'core.participantgrouprelationship': { + 'Meta': {'ordering': "['group', 'participant_number']", 'object_name': 'ParticipantGroupRelationship'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'first_visit': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'participant_group_relationship_set'", 'to': u"orm['core.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notifications_since': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'blank': 'True'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'participant_group_relationship_set'", 'to': u"orm['core.Participant']"}), + 'participant_number': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'round_joined': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.RoundConfiguration']"}), + 'survey_completed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + u'core.participantrounddatavalue': { + 'Meta': {'ordering': "['-date_created', 'round_data', 'participant_group_relationship', 'parameter']", 'object_name': 'ParticipantRoundDataValue'}, + 'boolean_value': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'float_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'int_value': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'parameter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Parameter']"}), + 'participant_group_relationship': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'data_value_set'", 'to': u"orm['core.ParticipantGroupRelationship']"}), + 'round_data': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'participant_data_value_set'", 'to': u"orm['core.RoundData']"}), + 'string_value': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'submitted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'target_data_value': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'target_data_value_set'", 'null': 'True', 'to': u"orm['core.ParticipantRoundDataValue']"}) + }, + u'core.participantsignup': { + 'Meta': {'object_name': 'ParticipantSignup'}, + 'attendance': ('django.db.models.fields.PositiveIntegerField', [], {'max_length': '1', 'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invitation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signup_set'", 'to': u"orm['core.Invitation']"}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signup_set'", 'to': u"orm['core.Participant']"}) + }, + u'core.quizquestion': { + 'Meta': {'object_name': 'QuizQuestion'}, + 'answer': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'default_quiz_question_set'", 'null': 'True', 'to': u"orm['core.Experiment']"}), + 'explanation': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'input_type': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + 'round_configuration': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'quiz_question_set'", 'to': u"orm['core.RoundConfiguration']"}) + }, + u'core.roundconfiguration': { + 'Meta': {'ordering': "['experiment_configuration', 'sequence_number', 'date_created']", 'object_name': 'RoundConfiguration'}, + 'chat_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'create_group_clusters': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'debriefing': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'display_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'duration': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'experiment_configuration': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'round_configuration_set'", 'to': u"orm['core.ExperimentConfiguration']"}), + 'group_cluster_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'initialize_data_values': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'instructions': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'preserve_existing_groups': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'randomize_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'repeat': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'round_type': ('django.db.models.fields.CharField', [], {'default': "'REGULAR'", 'max_length': '32'}), + 'sequence_number': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'session_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'blank': 'True'}), + 'survey_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'template_filename': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'template_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}) + }, + u'core.rounddata': { + 'Meta': {'ordering': "['round_configuration']", 'unique_together': "(('round_configuration', 'repeating_round_sequence_number', 'experiment'),)", 'object_name': 'RoundData'}, + 'elapsed_time': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'experiment': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'round_data_set'", 'to': u"orm['core.Experiment']"}), + 'experimenter_notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'repeating_round_sequence_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'round_configuration': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'round_data_set'", 'to': u"orm['core.RoundConfiguration']"}) + }, + u'core.roundparametervalue': { + 'Meta': {'ordering': "['round_configuration', 'parameter', 'date_created']", 'object_name': 'RoundParameterValue'}, + 'boolean_value': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'float_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'int_value': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_modified': ('vcweb.core.models.AutoDateTimeField', [], {'default': 'datetime.datetime.now'}), + 'parameter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Parameter']"}), + 'round_configuration': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parameter_value_set'", 'to': u"orm['core.RoundConfiguration']"}), + 'string_value': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + u'core.spoolparticipantstatistics': { + 'Meta': {'object_name': 'SpoolParticipantStatistics'}, + 'absences': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'discharges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invitations': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'spool_statistics_set'", 'to': u"orm['core.Participant']"}), + 'participations': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + } + } + + complete_apps = ['core'] \ No newline at end of file diff -r a054cd4e45f617d5be397be39048fd4fd5a3b081 -r 987921500c7c8fe83e24edb28ee1bdf825d2c093 vcweb/core/models.py --- a/vcweb/core/models.py +++ b/vcweb/core/models.py @@ -984,6 +984,8 @@ def end_round(self, sender=None): self.status = Experiment.Status.ACTIVE self.save() + # reset all survey completed flags on participant group relationships within this experiment + self._reset_survey_completed_flags() self.log('Ending round with elapsed time %s' % self.current_round_elapsed_time) sender = intern(self.experiment_metadata.namespace.encode('utf8')) if sender is None else sender #sender = self.namespace.encode('utf-8') @@ -1104,6 +1106,9 @@ else: logger.warning("Tried to transfer participants to an experiment %s that already had participants %s", experiment, experiment.participant_set.all()) + def _reset_survey_completed_flags(self): + ParticipantGroupRelationship.objects.for_experiment(self).update(survey_completed=False) + def __unicode__(self): return u"%s #%s | %s" % (self.experiment_configuration, self.pk, self.experimenter) @@ -1907,6 +1912,7 @@ active = models.BooleanField(default=True) first_visit = models.BooleanField(default=True) notifications_since = models.DateTimeField(default=datetime.now, null=True, blank=True) + survey_completed = models.BooleanField(default=False) objects = PassThroughManager.for_queryset_class(ParticipantGroupRelationshipQuerySet)() diff -r a054cd4e45f617d5be397be39048fd4fd5a3b081 -r 987921500c7c8fe83e24edb28ee1bdf825d2c093 vcweb/core/urls.py --- a/vcweb/core/urls.py +++ b/vcweb/core/urls.py @@ -2,9 +2,9 @@ from django.contrib.auth.decorators import login_required from vcweb import settings from vcweb.core.views import (Dashboard, LoginView, LogoutView, RegistrationView, monitor, CloneExperimentView, - RegisterEmailListView, RegisterTestParticipantsView, ClearParticipantsExperimentView, add_experiment, - Participate, download_data, export_configuration, api_logger, participant_api_login, api_logout, - participant_ready, get_number_of_ready_participants, deactivate) + RegisterEmailListView, RegisterTestParticipantsView, ClearParticipantsExperimentView, completed_survey, + check_survey_completed, Participate, download_data, export_configuration, api_logger, participant_api_login, + api_logout, participant_ready, check_ready_participants) import logging import urllib @@ -19,17 +19,17 @@ url(r'^accounts/add/$', RegistrationView.as_view(), name='register'), url(r'^accounts/profile/$', 'account_profile', name='profile'), url(r'^participate/?$', Participate.as_view(), name='participate'), - url(r'^participate/(?P<namespace>\w+)/instructions', 'instructions', name='namespace_instructions'), - url(r'^experiment/add$', add_experiment, name='add_experiment'), + url(r'^participate/survey-completed', completed_survey, name='survey_completed'), + url(r'^participate/(?P<pk>\d+)/check-survey-completed', check_survey_completed, name='check_survey_completed'), url(r'^experiment/participant-ready$', participant_ready, name='participant_ready'), - url(r'^experiment/(?P<pk>\d+)/check-ready-participants$', get_number_of_ready_participants, name='number_of_ready_participants'), + url(r'^experiment/(?P<pk>\d+)/check-ready-participants$', check_ready_participants, name='check_ready_participants'), url(r'^experiment/(?P<pk>\d+)/monitor$', monitor, name='monitor_experiment'), url(r'^experiment/(?P<pk>\d+)/register-email-list$', RegisterEmailListView.as_view(), name='register_email_list'), url(r'^experiment/(?P<pk>\d+)/register-test-participants$', RegisterTestParticipantsView.as_view(), name='register_test_participants'), # FIXME: refactor these into POSTs using the ExperimentActionForm - url(r'^experiment/(?P<pk>\d+)/deactivate$', deactivate, name='deactivate'), - url(r'^experiment/(?P<pk>\d+)/clone$', CloneExperimentView.as_view(), name='clone'), - url(r'^experiment/(?P<pk>\d+)/clear-participants', ClearParticipantsExperimentView.as_view(), name='clear_participants'), +# url(r'^experiment/(?P<pk>\d+)/deactivate$', deactivate, name='deactivate'), +# url(r'^experiment/(?P<pk>\d+)/clone$', CloneExperimentView.as_view(), name='clone'), +# url(r'^experiment/(?P<pk>\d+)/clear-participants', ClearParticipantsExperimentView.as_view(), name='clear_participants'), # url(r'^experiment/(?P<pk>\d+)/add-participants/(?P<count>[\d]+)$', 'add_participants', name='add_participants'), url(r'^experiment/(?P<pk>\d+)/download/(?P<file_type>[\w]+)$', download_data, name='download_data'), url(r'^experiment/(?P<pk>\d+)/export/configuration(?P<file_extension>.[\w]+)$', export_configuration, name='export_configuration'), diff -r a054cd4e45f617d5be397be39048fd4fd5a3b081 -r 987921500c7c8fe83e24edb28ee1bdf825d2c093 vcweb/core/views.py --- a/vcweb/core/views.py +++ b/vcweb/core/views.py @@ -264,13 +264,6 @@ def dispatch(self, *args, **kwargs): return super(ParticipantMixin, self).dispatch(*args, **kwargs) -@login_required -def instructions(request, namespace=None): - if namespace is not None: - return render(request, '%s/instructions.html' % namespace) - else: - return redirect('home') - """ experimenter views FIXME: add has_perms authorization to ensure that only experimenters can access @@ -398,7 +391,6 @@ return redirect('core:monitor_experiment', pk=self.experiment.pk) class ClearParticipantsExperimentView(ExperimenterSingleExperimentView): - def process(self): e = self.experiment logger.debug("clearing all participants for experiment %s", e) @@ -410,15 +402,6 @@ def render_to_response(self, context): return redirect('core:dashboard') -# FIXME: replace method with class-based view later (if beneficial) -class AddExperimentView(ExperimenterMixin, TemplateView): - pass - -@experimenter_required -def add_experiment(request): - return render('experimenter/add-experiment.html', - { 'experiment_list': ExperimentMetadata.objects.all() }) - class DataExportMixin(ExperimenterSingleExperimentMixin): file_extension = '.csv' def render_to_response(self, context, **response_kwargs): @@ -600,6 +583,21 @@ logger.error("Failed to validate log message form %s (%s)", request, form) return json_response(request, dumps({'success': success})) +@participant_required +def completed_survey(request): + participant = request.user.participant + experiment = get_active_experiment(participant) + pgr = experiment.get_participant_group_relationship(participant) + pgr.survey_completed = True + pgr.save() + +@participant_required +def check_survey_completed(request, pk=None): + participant = request.user.participant + experiment = get_object_or_404(Experiment, pk=pk) + return JsonResponse(dumps({ + 'survey_completed': experiment.get_participant_group_relationship(participant).survey_completed, + })) @participant_required def participant_ready(request): @@ -621,7 +619,7 @@ return { 'success': True, 'number_of_ready_participants': number_of_ready_participants, 'all_participants_ready': all_participants_ready } @login_required -def get_number_of_ready_participants(request, pk=None): +def check_ready_participants(request, pk=None): experiment = get_object_or_404(Experiment, pk=pk) return JsonResponse(dumps(_ready_participants_dict(experiment))) 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. |
From: <com...@bi...> - 2013-07-23 23:08:20
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/a054cd4e45f6/ Changeset: a054cd4e45f6 User: alllee Date: 2013-07-24 01:08:09 Summary: fixing bugs in deceased participant checking and clamping storage to 0 on the server-side if it goes negative (after flagging participant as deceased) Affected #: 4 files diff -r 8bcfa12db8decb4481f87df8726705a08a3b36c8 -r a054cd4e45f617d5be397be39048fd4fd5a3b081 vcweb/bound/models.py --- a/vcweb/bound/models.py +++ b/vcweb/bound/models.py @@ -302,8 +302,8 @@ # check for dead participants and set their ready and harvest decision flags deceased_participants = ParticipantRoundDataValue.objects.select_related('participant_group_relationship').filter(parameter=get_player_status_parameter(), - round_data=round_data, boolean_value=False).values_list('participant_group_relationship', flatten=True) - _zero_harvest_decisions(deceased_participants, round_data) + round_data=round_data, boolean_value=False) + _zero_harvest_decisions([prdv.participant_group_relationship for prdv in deceased_participants], round_data) ''' for prdv in deceased_participants: pgr = prdv.participant_group_relationship @@ -448,9 +448,9 @@ updated_storage = storage_dv.int_value + harvest_decision - cost_of_living if updated_storage < 0: # player has "died" - player_status_dv.boolean_value = False - player_status_dv.save() - storage_dv.update_int(updated_storage) + player_status_dv.update_boolean(False) +# clamp storage to 0 to avoid negative earnings + storage_dv.update_int(max(0, updated_storage)) logger.debug("updating participant %s (storage: %s, harvest: %s, status: %s)", pgr, storage_dv.int_value, harvest_decision, player_status_dv.boolean_value) pgr.copy_to_next_round(player_status_dv, storage_dv, next_round_data=next_round_data) diff -r 8bcfa12db8decb4481f87df8726705a08a3b36c8 -r a054cd4e45f617d5be397be39048fd4fd5a3b081 vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -404,7 +404,7 @@ <tr><td>Earnings</td><!-- ko foreach: playerData --> - <td data-bind='text: clampedStorage, css: { "current-player": id() === $root.participantGroupId() }'></td> + <td data-bind='text: Math.max(0, storage()), css: { "current-player": id() === $root.participantGroupId() }'></td><!-- /ko --></tr><tr> diff -r 8bcfa12db8decb4481f87df8726705a08a3b36c8 -r a054cd4e45f617d5be397be39048fd4fd5a3b081 vcweb/core/models.py --- a/vcweb/core/models.py +++ b/vcweb/core/models.py @@ -1505,6 +1505,10 @@ self.int_value = integer_value self.save() + def update_boolean(self, boolean_value): + self.boolean_value = boolean_value + self.save() + def to_dict(self, cacheable=False, **kwargs): p = self.parameter data = {'pk' : self.pk, diff -r 8bcfa12db8decb4481f87df8726705a08a3b36c8 -r a054cd4e45f617d5be397be39048fd4fd5a3b081 vcweb/core/templates/experimenter/monitor.html --- a/vcweb/core/templates/experimenter/monitor.html +++ b/vcweb/core/templates/experimenter/monitor.html @@ -225,7 +225,7 @@ model.tick = function() { model.timeRemaining(model.timeRemaining() - 1); if (model.timeRemaining() < 0) { - model.addMessage("Round time has expired!"); + model.addMessage("Round time has expired, ready to move on to next round."); model.clearCurrentInterval(); } } @@ -320,7 +320,7 @@ 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. + // FIXME: hacky and unconvinced that it'll provide true thread safety. Revisit. model.isCheckingParticipants = ko.observable(false); model.checkAllParticipantsReady = function() { if (model.isCheckingParticipants()) { @@ -331,7 +331,7 @@ 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, please advance to the next round"); + model.addMessage("All participants are ready, advancing 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() 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. |
From: <com...@bi...> - 2013-07-23 22:49:08
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/8bcfa12db8de/ Changeset: 8bcfa12db8de User: alllee Date: 2013-07-24 00:48:56 Summary: removing errant rparen Affected #: 1 file diff -r 17f4caf0785518a6e6312f740c5ff7ecb0c44cbd -r 8bcfa12db8decb4481f87df8726705a08a3b36c8 vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -562,7 +562,7 @@ return formatCurrency(model.sessionTwoStorage() * model.exchangeRate()) }); model.totalEarnings = ko.computed(function() { - return formatCurrency(model.clampedStorage() * model.exchangeRate())); + return formatCurrency(model.clampedStorage() * model.exchangeRate()); }); model.templateId = ko.computed(function() { switch ( model.templateName() ) { 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. |