[virtualcommons-svn] commit/vcweb: alllee: adding resource visualization for peer group observation
Status: Beta
Brought to you by:
alllee
From: <com...@bi...> - 2013-04-22 22:17:38
|
1 new commit in vcweb: https://bitbucket.org/virtualcommons/vcweb/commits/4a98b2a30619/ Changeset: 4a98b2a30619 User: alllee Date: 2013-04-23 00:17:26 Summary: adding resource visualization for peer group observation condition Affected #: 7 files diff -r 92a35d99eafc44b4adbbf9a2727535b73307d1ce -r 4a98b2a3061967787df85d09ec201e25e5878f2c vcweb/bound/models.py --- a/vcweb/bound/models.py +++ b/vcweb/bound/models.py @@ -191,9 +191,12 @@ return participant_group_relationship.get_data_value(parameter=get_player_status_parameter(), round_data=round_data, default=default) -def get_player_status(participant_group_relationship, round_data, default=True): +def is_player_alive(participant_group_relationship, round_data, default=True): return get_player_status_dv(participant_group_relationship, round_data, default).boolean_value +def get_number_alive(group, round_data): + return ParticipantRoundDataValue.objects.for_group(group, parameter=get_player_status_parameter(), round_data=round_data, boolean_value=True).count() + def set_player_status(participant_group_relationship, round_data, value): status_dv = get_player_status_dv(participant_group_relationship, round_data) status_dv.boolean_value = value diff -r 92a35d99eafc44b4adbbf9a2727535b73307d1ce -r 4a98b2a3061967787df85d09ec201e25e5878f2c vcweb/bound/static/css/bound/style.css --- a/vcweb/bound/static/css/bound/style.css +++ b/vcweb/bound/static/css/bound/style.css @@ -22,7 +22,7 @@ } .bound-status-dashboard { height: 100px; - width: 110px; + width: 115px; margin-right: 0px; padding: 8px 8px 8px 8px; } @@ -40,9 +40,11 @@ background-color: #dff0d8; } col.current-player { - background-color: #dff0d8; + background-color: #ffb914; } .current-player { font-weight: bold; - font-size: 1em; + color: #145bff !important; } +.group-status { +} diff -r 92a35d99eafc44b4adbbf9a2727535b73307d1ce -r 4a98b2a3061967787df85d09ec201e25e5878f2c vcweb/bound/static/images/bound/observe.png Binary file vcweb/bound/static/images/bound/observe.png has changed diff -r 92a35d99eafc44b4adbbf9a2727535b73307d1ce -r 4a98b2a3061967787df85d09ec201e25e5878f2c vcweb/bound/static/images/bound/tree-resource.png Binary file vcweb/bound/static/images/bound/tree-resource.png has changed diff -r 92a35d99eafc44b4adbbf9a2727535b73307d1ce -r 4a98b2a3061967787df85d09ec201e25e5878f2c vcweb/bound/templates/bound/participate.html --- a/vcweb/bound/templates/bound/participate.html +++ b/vcweb/bound/templates/bound/participate.html @@ -4,46 +4,50 @@ {{ block.super }} <link rel='stylesheet' href='{% static "css/bound/style.css" %}'/> {% endblock %} -{% block content %} -<h3><span data-bind='text: roundSequenceLabel'></span></h3> -<div data-bind='template: { name: templateId(), afterRender: afterRenderTemplate }'> -</div> -<div id='progress-modal' class='modal hide fade'> - <div class='model-header'> - <h3>Updating experiment data</h3> +{% block page %} +<div class='row' id='page'> + <div id='content' class='span8'> + <h3><span data-bind='text: roundSequenceLabel'></span></h3> + <div data-bind='template: { name: templateId(), afterRender: afterRenderTemplate }'> + </div> + <div id='progress-modal' class='modal hide fade'> + <div class='model-header'> + <h3>Updating experiment data</h3> + </div> + <div class='modal-body'> + <div class='progress progress-striped active'> + <div id='progress-bar' class='bar' style='width: 100%'></div> + </div> + </div> + </div></div> - <div class='modal-body'> - <div class='progress progress-striped active'> - <div id='progress-bar' class='bar' style='width: 100%'></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: message'></span> + <br> + </div> + </div> + </div></div></div></div> -{% endblock content %} -{% block 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: message'></span> - <br> - </div> - </div> -</div> -</div> -{% endblock sidebar %} +{% endblock page %} {% block javascript %} {{ block.super }} <!-- ko templates --> @@ -331,14 +335,14 @@ <div class='span5'><h3>My Status</h3><div class='row'> - <div class='alert bound-status-dashboard span2'> - <h4 id="dashboard-last-harvest">Last harvest <i class='icon-info-sign' data-content="The number of trees you harvested last round."></i></h4> + <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></p></div> - <div class='alert bound-status-dashboard span2'> - <h4 id='dashboard-storage'>Storage <i class='icon-info-sign' data-bind='attr: {"data-content": "You need at least " + costOfLiving() + " trees to survive each round."}'></i></h4> + <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><p><strong data-bind='css: { "text-error": storage() < costOfLiving(), "text-success": storage() > costOfLiving()}'><span data-bind='text: storage'></span><i class='icon-leaf'></i></strong></p> @@ -353,9 +357,9 @@ </div></div></div> - <div class='span4'> + <div class='span3'><h3>Group Status</h3> - <table class='table table-bordered table-condensed'> + <table class='table table-bordered table-condensed group-status'><colgroup><col><!-- ko foreach: playerData --> @@ -365,7 +369,7 @@ <thead><tr><th>Player</th><!-- ko foreach: playerData --> - <th data-bind='text: number'></th> + <th data-bind='css: { "current-player": id() === $root.participantGroupId() }, text: number'></th><!-- /ko --></tr></thead> @@ -377,79 +381,22 @@ <!-- /ko --></tr><tr> - <td>Storage</td> + <td>Earnings</td><!-- ko foreach: playerData --><td data-bind='text: storage, css: { "current-player": id() === $root.participantGroupId() }'></td><!-- /ko --></tr><tr> - <td>Status</td> + <td>Alive?</td><!-- ko foreach: playerData --> - <td data-bind='css: { "current-player": id() === $root.participantGroupId(), "text-error": ! alive(), "text-success": alive() }, text: alive() ? "Alive" : "Deceased"'></td> + <td data-bind='css: { "current-player": id() === $root.participantGroupId(), "text-error": ! alive(), "text-success": alive() }, text: alive() ? "Y" : "N"'></td><!-- /ko --></tr></tbody></table></div></div> -<h3>Forest</h3> -<div data-bind='if: isPracticeRound'> - <div class='alert alert-error alert-block'> - <h4>PRACTICE ROUND</h4> - This is a practice round. The decisions you make in this round will <b>NOT</b> contribute to your earnings. - </div> -</div> - -<div data-bind="ifnot: isResourceEmpty" id='resourceDisplay'> - <div class='alert alert-info'>There are <strong class='badge badge-success' data-bind='text:resourceLevel'></strong> trees remaining in the forest.</div> - <div data-bind='style: { width: uniformResourceImageWidth, height: uniformResourceImageHeight, background: resourceImageBackgroundUrl }'> - - </div> - {% comment %} FIXME: crummy use of "px" concatenation, use something like inPixels(..) instead? {% endcomment %} - <div data-bind='style: { width: leftoverResourceImageWidth, height: resourceImageHeight() + "px", background: resourceImageBackgroundUrl }'> - - </div> -</div> -<div data-bind="if: isResourceEmpty"> - <div class='well'> - <center><img src="{% static 'images/forestry/deforestation.jpg' %}" class="img-polaroid" width="425" - height="282"></center> - </div> - <!-- this centers the image - <div style='padding: 8px; margin: auto; border: solid 1px; background: url("{{STATIC_URL}}images/forestry/deforestation.jpg") no-repeat center; height: 282px; width:425px;'> - - </div> - --> - <div class='alert alert-error'><i class='icon-warning-sign'></i> There are no more trees to harvest. Please wait until the next round begins.</div> -</div> -<div data-bind='if: canObserveOtherGroup'> - <h3>Observe Other Group</h3> - <div class='row'> - <div class='span4'> - <table> - <tr> - <td><button class='btn' data-bind='click: showOtherGroup'><i class='icon-eye-open'></i> Observe</a></td> - <td><img src='{{STATIC_URL}}/images/bound/observe.png' alt='observe'></td> - </tr> - </table> - </div> - <div class='span5'> - <div data-bind='if: shouldShowOtherGroup'> - <table class='table table-bordered table-condensed'> - <caption>Observation Results</caption> - <thead> - <tr><th></th><th>Other Group</th></tr> - </thead> - <tbody> - <tr><td>trees in forest</td><td data-bind='text:otherGroupResourceLevel()'></td></tr> - <tr><td>average harvest</td><td data-bind='text:otherGroupAverageHarvest().toFixed(1)'></td></tr> - <tr><td>average storage</td><td data-bind='text:otherGroupAverageStorage().toFixed(1)'></td></tr> - </tbody> - </table> - </div> - </div> - </div> -</div> +<div class='row' data-bind='template: { name: "resource-visualization-template" }'></div><div data-bind='visible: ! isResourceEmpty()'><h3>Harvest</h3><div data-bind='if: submitted'> @@ -475,6 +422,62 @@ </div></div></script> +<script type='text/html' id='resource-visualization-template'> +<div data-bind='if: isPracticeRound'> + <div class='alert alert-error alert-block'> + <h4>PRACTICE ROUND</h4> + This is a practice round. The decisions you make in this round will <b>NOT</b> contribute to your earnings. + </div> +</div> + +<div data-bind='css: { "span4": canObserveOtherGroup }'> +<h4>My Group</h4> +<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'> + <center><img src="{% static 'images/forestry/deforestation.jpg' %}" class="img-polaroid" width="425" + height="282"></center> + </div> + <div class='alert alert-error'><i class='icon-warning-sign'></i> There are no more trees to harvest. Please wait until the next round begins.</div> +</div> +<div data-bind='ifnot: isResourceEmpty'> +<div data-bind='style: { width: blockResourceVisualizationImageWidth(resourceLevel), height: blockResourceVisualizationImageHeight(resourceLevel), background: resourceImageBackgroundUrl }'></div> +{% comment %} FIXME: crummy use of "px" concatenation, use something like inPixels(..) instead? {% endcomment %} +<div data-bind='style: { width: remainderResourceImageWidth(resourceLevel), height: resourceImageHeight() + "px", background: resourceImageBackgroundUrl }'></div> +</div> +<table class='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> + <tr><td>average earnings</td><td data-bind='text:averageStorage().toFixed(1)'></td></tr> + <tr><td>number alive</td><td data-bind='text:numberAlive'></td></tr> + </tbody> +</table> +</div> +<div data-bind='if: canObserveOtherGroup' class='span4'> +<h4>Other Group</h4> +<div class='alert alert-message'><strong class='badge badge-success' data-bind='text:otherGroupResourceLevel'></strong> trees</div> +<div data-bind="visible: isResourceEmpty"> + <div class='well'> + <center><img alt='deforested' src="{% static 'images/forestry/deforestation.jpg' %}" class="img-polaroid" width="425" height="282"></center> + </div> + <div class='alert alert-error'><i class='icon-warning-sign'></i> There are no more trees to harvest.</div> +</div> +<div data-bind='ifnot: isResourceEmpty'> +<div data-bind='style: { width: blockResourceVisualizationImageWidth(otherGroupResourceLevel), height: blockResourceVisualizationImageHeight(otherGroupResourceLevel), background: resourceImageBackgroundUrl }'></div> +{% comment %} FIXME: crummy use of "px" concatenation, use something like inPixels(..) instead? {% endcomment %} +<div data-bind='style: { width: remainderResourceImageWidth(otherGroupResourceLevel), height: resourceImageHeight() + "px", background: resourceImageBackgroundUrl }'></div> +</div> +<table class='table table-bordered table-condensed table-striped'> + <tbody> + <tr><td>forest</td><td data-bind='text:otherGroupResourceLevel()'></td></tr> + <tr><td>average harvest</td><td data-bind='text:otherGroupAverageHarvest().toFixed(1)'></td></tr> + <tr><td>average earnings</td><td data-bind='text:otherGroupAverageStorage().toFixed(1)'></td></tr> + <tr><td>number alive</td><td data-bind='text:otherGroupNumberAlive'></td></tr> + </tbody> +</table> +</div> +</script><script type='text/html' id='harvest-decision-multi-select-template'><table class='table'><tr class='row harvest-decision-resources' data-bind='foreach: harvestDecisionOptions'> @@ -591,12 +594,19 @@ model.isResourceEmpty = ko.computed(function() { return model.resourceLevel() == 0; }) - model.resourcesPerRow = ko.observable(40); - model.resourceImageWidth = ko.observable(20); - model.resourceImageHeight = ko.observable(40); - model.rowsToDisplay = ko.computed(function() { - return Math.floor(model.resourceLevel() / model.resourcesPerRow()); + model.resourcesPerRow = ko.computed(function() { + if (model.canObserveOtherGroup()) { + return 37; + } + else { + return 80; + } }); + model.resourceImageWidth = ko.observable(10); + model.resourceImageHeight = ko.observable(20); + model.rowsToDisplay = function(resourceLevel) { + return Math.floor(resourceLevel() / model.resourcesPerRow()); + }; model.individualResourcesToDisplay = ko.computed(function() { return model.resourceLevel() % model.resourcesPerRow(); }); @@ -608,18 +618,18 @@ }); model.resourceImageUrl = ko.observable("{{STATIC_URL}}images/bound/tree-inactive.png"); model.resourceImageBackgroundUrl = ko.observable("url('{{STATIC_URL}}images/bound/tree-resource.png')"); - model.uniformResourceImageWidth = ko.computed(function() { + model.blockResourceVisualizationImageWidth = function(resourceLevel) { return (model.resourcesPerRow() * model.resourceImageWidth()) + "px"; - }); - model.uniformResourceImageHeight = ko.computed(function() { - var rowsToDisplay = model.rowsToDisplay(); + }; + model.blockResourceVisualizationImageHeight = function(resourceLevel) { + var rowsToDisplay = model.rowsToDisplay(resourceLevel); console.debug("displaying " + rowsToDisplay + " rows."); return (rowsToDisplay * model.resourceImageHeight()) + "px"; - }); - model.leftoverResourceImageWidth = ko.computed(function() { - var remainder = model.resourceLevel() % model.resourcesPerRow(); + }; + model.remainderResourceImageWidth = function(resourceLevel) { + var remainder = resourceLevel() % model.resourcesPerRow(); return (remainder * model.resourceImageWidth()) + "px"; - }); + }; model.startRound = function() { // reset other group view model.shouldShowOtherGroup(false); diff -r 92a35d99eafc44b4adbbf9a2727535b73307d1ce -r 4a98b2a3061967787df85d09ec201e25e5878f2c vcweb/bound/views.py --- a/vcweb/bound/views.py +++ b/vcweb/bound/views.py @@ -1,3 +1,5 @@ +from collections import Counter +from operator import itemgetter from django.http import Http404 from django.shortcuts import render, redirect, get_object_or_404 from vcweb.core import dumps @@ -8,8 +10,8 @@ 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_last_harvest_decision, get_harvest_decision_dv, get_harvest_decision_parameter, - set_harvest_decision, can_observe_other_group, get_player_status, get_average_harvest, get_average_storage, - get_total_harvest) + set_harvest_decision, can_observe_other_group, is_player_alive, get_average_harvest, get_average_storage, + get_total_harvest, get_number_alive) from urllib import urlencode import logging @@ -137,18 +139,22 @@ own_group = participant_group_relationship.group own_resource_level = get_resource_level(own_group) last_harvest_decision = get_last_harvest_decision(participant_group_relationship, round_data=previous_round_data) + # FIXME: redundancy with playerData and direct values experiment_model_dict['playerData'] = [{ 'id': pgr.pk, 'number': pgr.participant_number, 'lastHarvestDecision': get_last_harvest_decision(pgr, round_data=previous_round_data), - 'alive': get_player_status(pgr, current_round_data), + 'alive': is_player_alive(pgr, current_round_data), 'storage': get_storage(pgr, current_round_data), } for pgr in own_group.participant_group_relationship_set.all()] - # FIXME: redundancy with playerData experiment_model_dict['lastHarvestDecision'] = last_harvest_decision experiment_model_dict['storage'] = get_storage(participant_group_relationship, current_round_data) experiment_model_dict['resourceLevel'] = own_resource_level - experiment_model_dict['alive'] = get_player_status(participant_group_relationship, current_round_data) + experiment_model_dict['alive'] = is_player_alive(participant_group_relationship, current_round_data) + experiment_model_dict['averageHarvest'] = get_average_harvest(own_group, previous_round_data) + experiment_model_dict['averageStorage'] = get_average_storage(own_group, current_round_data) + c = Counter(map(itemgetter('alive'), experiment_model_dict['playerData'])) + experiment_model_dict['numberAlive'] = "%s out of %s" % (c[True], sum(c.values())) # participant group data parameters are only needed if this round is a data round or the previous round was a data round if previous_round.is_playable_round or current_round.is_playable_round: harvest_decision = get_harvest_decision_dv(participant_group_relationship, current_round_data) @@ -165,5 +171,7 @@ experiment_model_dict['otherGroupResourceLevel'] = get_resource_level(other_group, current_round_data) experiment_model_dict['otherGroupAverageHarvest'] = get_average_harvest(other_group, previous_round_data) experiment_model_dict['otherGroupAverageStorage'] = get_average_storage(other_group, current_round_data) + number_alive = get_number_alive(other_group, current_round_data) + experiment_model_dict['otherGroupNumberAlive'] = "%s out of %s" % (number_alive, other_group.size) return dumps(experiment_model_dict) diff -r 92a35d99eafc44b4adbbf9a2727535b73307d1ce -r 4a98b2a3061967787df85d09ec201e25e5878f2c vcweb/static/images/boundaries/observe.png Binary file vcweb/static/images/boundaries/observe.png has changed 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. |