From 699a15919c90a9b2d413261ccef566fabdc1b0d6 Mon Sep 17 00:00:00 2001 From: Imperadeiro98 Date: Fri, 16 Jan 2015 20:55:20 +0000 Subject: [PATCH 1/5] Update loading-error.jade --- app/templates/core/loading-error.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/core/loading-error.jade b/app/templates/core/loading-error.jade index fdd566a45..d0d2ac5ca 100644 --- a/app/templates/core/loading-error.jade +++ b/app/templates/core/loading-error.jade @@ -25,4 +25,4 @@ strong(data-i18n="loading_error.unknown") Unknown error. button.btn.btn-xs.retry-loading-resource(data-i18n="common.retry", data-resource-index=resourceIndex) Retry - button.btn.btn-xs.skip-loading-resource(data-i18n="common.skip", data-resource-index=resourceIndex) Skip + button.btn.btn-xs.skip-loading-resource(data-i18n="play_level.skip", data-resource-index=resourceIndex) Skip From 91515ef5a1514fe97b3b69b891bd6acd569c3313 Mon Sep 17 00:00:00 2001 From: Imperadeiro98 Date: Fri, 16 Jan 2015 20:58:17 +0000 Subject: [PATCH 2/5] Update pt-PT.coffee --- app/locale/pt-PT.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/locale/pt-PT.coffee b/app/locale/pt-PT.coffee index 990c9d71d..7318e9a60 100644 --- a/app/locale/pt-PT.coffee +++ b/app/locale/pt-PT.coffee @@ -81,7 +81,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: awaiting_levels_adventurer_prefix: "Nós adicionamos cinco níveis por semana." awaiting_levels_adventurer: "Regista-te como Aventureiro" awaiting_levels_adventurer_suffix: "para seres o primeiro a jogar níveis novos." -# adjust_volume: "Adjust volume" + adjust_volume: "Ajustar volume" choose_your_level: "Escolhe o Teu Nível" # The rest of this section is the old play view at /play-old and isn't very important. adventurer_prefix: "Podes saltar para um dos níveis abaixo ou discutir os níveis no " adventurer_forum: "fórum do Aventureiro" @@ -480,7 +480,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: versions: save_version_title: "Guardar Nova Versão" new_major_version: "Nova Versão Principal" -# submitting_patch: "Submitting Patch..." + submitting_patch: "A Submeter Atualização..." cla_prefix: "Para guardares as alterações, precisas de concordar com o nosso" cla_url: "CLA" cla_suffix: "." @@ -636,8 +636,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: level_tab_thangs_all: "Todos" level_tab_thangs_conditions: "Condições Iniciais" level_tab_thangs_add: "Adicionar Thangs" -# add_components: "Add Components" -# component_configs: "Component Configurations" + add_components: "Adicionar Componentes" + component_configs: "Configurações dos Componentes" config_thang: "Clica duas vezes para configurares uma thang" delete: "Eliminar" duplicate: "Duplicar" @@ -885,7 +885,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: leaderboard: "Tabela de Classificação" user_schema: "Esquema do Utilizador" user_profile: "Perfil do Utilizador" -# patch: "Patch" + patch: "Atualização" patches: "Atualizações" patched_model: "Documento Fonte" model: "Modelo" From 974c9e538d3178e148db406c4f19e42082360a2f Mon Sep 17 00:00:00 2001 From: Imperadeiro98 Date: Fri, 16 Jan 2015 21:08:32 +0000 Subject: [PATCH 3/5] Update add-thang-components-modal.jade --- app/templates/editor/component/add-thang-components-modal.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/editor/component/add-thang-components-modal.jade b/app/templates/editor/component/add-thang-components-modal.jade index fe2bbd40f..2d40a7aee 100644 --- a/app/templates/editor/component/add-thang-components-modal.jade +++ b/app/templates/editor/component/add-thang-components-modal.jade @@ -1,7 +1,7 @@ extends /templates/core/modal-base block modal-header-content - h1 Add Components + h1(data-i18n="editor.add_components") Add Components block modal-body-content form From d532d9fe6e536d65255a35e87e073668c924e6f9 Mon Sep 17 00:00:00 2001 From: Matt Lott Date: Fri, 16 Jan 2015 15:44:24 -0800 Subject: [PATCH 4/5] Add date picker to campaign editor analytics Only for overall campaign editor analytics. Still need to add date pickers to individual level views. Moving campaign editor analytics to its own modal to support reloading with new dates. The date picker widget works poorly, but you can type in dates if necessary. Also moving analytics button up into the top right header, with a little stats glyph icon. --- .../campaign/campaign-analytics-modal.jade | 37 +++++++ .../editor/campaign/campaign-editor-view.jade | 41 +------ .../campaign/CampaignAnalyticsModal.coffee | 101 ++++++++++++++++++ .../editor/campaign/CampaignEditorView.coffee | 72 ++----------- 4 files changed, 150 insertions(+), 101 deletions(-) create mode 100644 app/templates/editor/campaign/campaign-analytics-modal.jade create mode 100644 app/views/editor/campaign/CampaignAnalyticsModal.coffee diff --git a/app/templates/editor/campaign/campaign-analytics-modal.jade b/app/templates/editor/campaign/campaign-analytics-modal.jade new file mode 100644 index 000000000..a19be9ac9 --- /dev/null +++ b/app/templates/editor/campaign/campaign-analytics-modal.jade @@ -0,0 +1,37 @@ +extends /templates/core/modal-base + +block modal-header-content + h3 Campaign Analytics + if campaignCompletions.startDay && campaignCompletions.endDay + .input-group.input-group-sm + input.form-control#input-startday(type='text', style='width:100px;', value=campaignCompletions.startDay) + input.form-control#input-endday(type='text', style='width:100px;', value=campaignCompletions.endDay) + button.btn.btn-default.btn-sm#reload-button(style='margin-left:10px;') Reload + +block modal-body-content + if campaignCompletions && campaignCompletions.levels + table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt') + thead + tr + td Level + td Started + td Finished + td Playtime (s) + td Completion % + tbody + - for (var i = 0; i < campaignCompletions.levels.length; i++) + tr + td= campaignCompletions.levels[i].level + td= campaignCompletions.levels[i].started + td= campaignCompletions.levels[i].finished + td= campaignCompletions.levels[i].averagePlaytime + if campaignCompletions.top3.indexOf(campaignCompletions.levels[i].level) >= 0 + td(style='background-color:lightblue;')= campaignCompletions.levels[i].completionRate + else if campaignCompletions.bottom3.indexOf(campaignCompletions.levels[i].level) >= 0 + td(style='background-color:pink;')= campaignCompletions.levels[i].completionRate + else + td= campaignCompletions.levels[i].completionRate + else + div Loading... + +block modal-footer \ No newline at end of file diff --git a/app/templates/editor/campaign/campaign-editor-view.jade b/app/templates/editor/campaign/campaign-editor-view.jade index 0e873ddf7..b97154825 100644 --- a/app/templates/editor/campaign/campaign-editor-view.jade +++ b/app/templates/editor/campaign/campaign-editor-view.jade @@ -16,6 +16,9 @@ block header span.glyphicon-home.glyphicon ul.nav.navbar-nav.navbar-right + li#analytics-button + a + span.glyphicon-stats.glyphicon if me.isAdmin() li#save-button a @@ -40,43 +43,5 @@ block outer_content #right-column #campaign-view #campaign-level-view.hidden - if campaignCompletions - button.btn.btn-default#analytics-button(title="Analytics", data-toggle="modal" data-target="#analytics-modal") Analytics - .modal.fade#analytics-modal(tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true") - .modal-dialog.modal-lg - .modal-content - .modal-header - button.close(type="button", data-dismiss="modal", aria-label="Close") - span(aria-hidden="true") × - h4.modal-title Analytics - .modal-body - if campaignCompletions.startDay - if campaignCompletions.endDay - div #{campaignCompletions.startDay} to #{campaignCompletions.endDay} - else - div #{campaignCompletions.startDay} to yesterday - table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt') - thead - tr - td Level - td Started - td Finished - td Playtime (s) - td Completion % - tbody - - for (var i = 0; i < campaignCompletions.levels.length; i++) - tr - td= campaignCompletions.levels[i].level - td= campaignCompletions.levels[i].started - td= campaignCompletions.levels[i].finished - td= campaignCompletions.levels[i].averagePlaytime - if campaignCompletions.top3.indexOf(campaignCompletions.levels[i].level) >= 0 - td(style='background-color:lightblue;')= campaignCompletions.levels[i].completionRate - else if campaignCompletions.bottom3.indexOf(campaignCompletions.levels[i].level) >= 0 - td(style='background-color:pink;')= campaignCompletions.levels[i].completionRate - else - td= campaignCompletions.levels[i].completionRate - else - button.btn.btn-default.disabled#analytics-button Analytics Loading... block footer diff --git a/app/views/editor/campaign/CampaignAnalyticsModal.coffee b/app/views/editor/campaign/CampaignAnalyticsModal.coffee new file mode 100644 index 000000000..0b47bf6b5 --- /dev/null +++ b/app/views/editor/campaign/CampaignAnalyticsModal.coffee @@ -0,0 +1,101 @@ +template = require 'templates/editor/campaign/campaign-analytics-modal' +utils = require 'core/utils' +ModalView = require 'views/core/ModalView' + +# TODO: jquery-ui datepicker doesn't work well in this view +# TODO: the date format handling is confusing (yyyy-mm-dd <=> yyyymmdd) + +module.exports = class CampaignAnalyticsModal extends ModalView + id: 'campaign-analytics-modal' + template: template + plain: true + + events: + 'click #reload-button': 'onClickReloadButton' + + constructor: (options, @campaignHandle, @campaignCompletions) -> + super options + @getCampaignAnalytics() unless @campaignCompletions?.levels? + + getRenderData: -> + c = super() + c.campaignCompletions = @campaignCompletions + c + + afterRender: -> + super() + $("#input-startday").datepicker dateFormat: "yy-mm-dd" + $("#input-endday").datepicker dateFormat: "yy-mm-dd" + + onClickReloadButton: () => + startDay = $('#input-startday').val() + endDay = $('#input-endday').val() + delete @campaignCompletions.levels + @render() + @getCampaignAnalytics startDay, endDay + + getCampaignAnalytics: (startDay, endDay) => + # Fetch campaign analytics, unless dates given + + startDay = startDay.replace(/-/g, '') if startDay? + endDay = endDay.replace(/-/g, '') if endDay? + + startDay ?= utils.getUTCDay -14 + endDay ?= utils.getUTCDay -1 + + success = (data) => + return if @destroyed + mapFn = (item) -> + item.completionRate = (item.finished / item.started * 100).toFixed(2) + item + @campaignCompletions.levels = _.map data, mapFn, @ + sortedLevels = _.cloneDeep @campaignCompletions.levels + sortedLevels = _.filter sortedLevels, ((a) -> a.finished >= 10), @ + sortedLevels.sort (a, b) -> b.completionRate - a.completionRate + @campaignCompletions.top3 = _.pluck sortedLevels[0..2], 'level' + sortedLevels.sort (a, b) -> a.completionRate - b.completionRate + @campaignCompletions.bottom3 = _.pluck sortedLevels[0..2], 'level' + @campaignCompletions.startDay = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}" + @campaignCompletions.endDay = "#{endDay[0..3]}-#{endDay[4..5]}-#{endDay[6..7]}" + @getCampaignAveragePlaytimes startDay, endDay + + # TODO: Why do we need this url dash? + request = @supermodel.addRequestResource 'campaign_completions', { + url: '/db/analytics_perday/-/campaign_completions' + data: {startDay: startDay, endDay: endDay, slug: @campaignHandle} + method: 'POST' + success: success + }, 0 + request.load() + + getCampaignAveragePlaytimes: (startDay, endDay) => + # Fetch level average playtimes + success = (data) => + return if @destroyed + levelAverages = {} + for item in data + levelAverages[item.level] ?= [] + levelAverages[item.level].push item.average + for level in @campaignCompletions.levels + if levelAverages[level.level] + if levelAverages[level.level].length > 0 + total = _.reduce levelAverages[level.level], ((sum, num) -> sum + num) + level.averagePlaytime = (total / levelAverages[level.level].length).toFixed(2) + else + level.averagePlaytime = 0.0 + @render() + + startDay ?= utils.getUTCDay -14 + startDay = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}" + endDay ?= utils.getUTCDay -1 + endDay = "#{endDay[0..3]}-#{endDay[4..5]}-#{endDay[6..7]}" + + levelSlugs = _.pluck @campaignCompletions.levels, 'level' + + request = @supermodel.addRequestResource 'playtime_averages', { + url: '/db/level/-/playtime_averages' + data: {startDay: startDay, endDay: endDay, slugs: levelSlugs} + method: 'POST' + success: success + }, 0 + request.load() diff --git a/app/views/editor/campaign/CampaignEditorView.coffee b/app/views/editor/campaign/CampaignEditorView.coffee index 7f9fe1969..db3cb140e 100644 --- a/app/views/editor/campaign/CampaignEditorView.coffee +++ b/app/views/editor/campaign/CampaignEditorView.coffee @@ -7,9 +7,10 @@ CampaignView = require 'views/play/CampaignView' CocoCollection = require 'collections/CocoCollection' treemaExt = require 'core/treema-ext' utils = require 'core/utils' -SaveCampaignModal = require './SaveCampaignModal' RelatedAchievementsCollection = require 'collections/RelatedAchievementsCollection' +CampaignAnalyticsModal = require './CampaignAnalyticsModal' CampaignLevelView = require './CampaignLevelView' +SaveCampaignModal = require './SaveCampaignModal' achievementProject = ['related', 'rewards', 'name', 'slug'] thangTypeProject = ['name', 'original'] @@ -20,6 +21,7 @@ module.exports = class CampaignEditorView extends RootView className: 'editor' events: + 'click #analytics-button': 'onClickAnalyticsButton' 'click #save-button': 'onClickSaveButton' constructor: (options, @campaignHandle) -> @@ -27,6 +29,9 @@ module.exports = class CampaignEditorView extends RootView @campaign = new Campaign({_id:@campaignHandle}) @supermodel.loadModel(@campaign, 'campaign') + # Save reference to data used by anlytics modal so it persists across modal open/closes. + @campaignAnalytics = {} + @levels = new CocoCollection([], { model: Level url: "/db/campaign/#{@campaignHandle}/levels" @@ -47,8 +52,6 @@ module.exports = class CampaignEditorView extends RootView @listenToOnce @levels, 'sync', @onFundamentalLoaded @listenToOnce @achievements, 'sync', @onFundamentalLoaded - _.delay @getCampaignAnalytics, 500 - loadThangTypeNames: -> # Load the names of the ThangTypes that this level's Treema nodes might want to display. originals = [] @@ -132,9 +135,11 @@ module.exports = class CampaignEditorView extends RootView getRenderData: -> c = super() c.campaign = @campaign - c.campaignCompletions = @campaignCompletions c + onClickAnalyticsButton: -> + @openModalView new CampaignAnalyticsModal {}, @campaignHandle, @campaignAnalytics + onClickSaveButton: -> @toSave.set @toSave.filter (m) -> m.hasLocalChanges() @openModalView new SaveCampaignModal({}, @toSave) @@ -240,65 +245,6 @@ module.exports = class CampaignEditorView extends RootView if achievement.hasLocalChanges() @toSave.add achievement - getCampaignAnalytics: => - # Fetch last 14 days of campaign analytics - - startDay = utils.getUTCDay -14 - endDay = utils.getUTCDay -1 - - success = (data) => - return if @destroyed - mapFn = (item) -> - item.completionRate = (item.finished / item.started * 100).toFixed(2) - item - @campaignCompletions = levels: _.map data, mapFn, @ - sortedLevels = _.cloneDeep @campaignCompletions.levels - sortedLevels = _.filter sortedLevels, ((a) -> a.completionRate > 1.0), @ - sortedLevels.sort (a, b) -> b.completionRate - a.completionRate - @campaignCompletions.top3 = _.pluck sortedLevels[0..2], 'level' - sortedLevels.sort (a, b) -> a.completionRate - b.completionRate - @campaignCompletions.bottom3 = _.pluck sortedLevels[0..2], 'level' - @campaignCompletions.startDay = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}" - @campaignCompletions.endDay = "#{endDay[0..3]}-#{endDay[4..5]}-#{endDay[6..7]}" - @getCampaignAveragePlaytimes() - - # TODO: Why do we need this url dash? - request = @supermodel.addRequestResource 'campaign_completions', { - url: '/db/analytics_perday/-/campaign_completions' - data: {startDay: startDay, slug: @campaignHandle} - method: 'POST' - success: success - }, 0 - request.load() - - getCampaignAveragePlaytimes: => - # Fetch last 14 days of level average playtimes - success = (data) => - return if @destroyed - levelAverages = {} - for item in data - levelAverages[item.level] ?= [] - levelAverages[item.level].push item.average - for level in @campaignCompletions.levels - if levelAverages[level.level] - if levelAverages[level.level].length > 0 - total = _.reduce levelAverages[level.level], ((sum, num) -> sum + num) - level.averagePlaytime = (total / levelAverages[level.level].length).toFixed(2) - else - level.averagePlaytime = 0.0 - @render() - - levelSlugs = _.pluck @campaignCompletions.levels, 'level' - startDay = utils.getUTCDay -14 - startDay = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}" - request = @supermodel.addRequestResource 'playtime_averages', { - url: '/db/level/-/playtime_averages' - data: {startDay: startDay, slugs: levelSlugs} - method: 'POST' - success: success - }, 0 - request.load() - class LevelsNode extends TreemaObjectNode valueClass: 'treema-levels' From 706b2ca9300a28071bc3875080d9a742a8c71515 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Fri, 16 Jan 2015 18:47:30 -0800 Subject: [PATCH 5/5] Don't includeFlow for Thangs with skipProtectAPI on, because we don't care about their flow anyway (non-read-write custom Programmables). --- app/views/play/level/tome/Spell.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/play/level/tome/Spell.coffee b/app/views/play/level/tome/Spell.coffee index 6d3683e2b..1c3fe142d 100644 --- a/app/views/play/level/tome/Spell.coffee +++ b/app/views/play/level/tome/Spell.coffee @@ -151,7 +151,8 @@ module.exports = class Spell writable = @permissions.readwrite.length > 0 skipProtectAPI = @skipProtectAPI or not writable problemContext = @createProblemContext thang - aetherOptions = createAetherOptions functionName: @name, codeLanguage: @language, functionParameters: @parameters, skipProtectAPI: skipProtectAPI, includeFlow: @levelType in ['hero', 'hero-ladder', 'hero-coop'], problemContext: problemContext + includeFlow = (@levelType in ['hero', 'hero-ladder', 'hero-coop']) and not skipProtectAPI + aetherOptions = createAetherOptions functionName: @name, codeLanguage: @language, functionParameters: @parameters, skipProtectAPI: skipProtectAPI, includeFlow: includeFlow, problemContext: problemContext aether = new Aether aetherOptions if @worker workerMessage =