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" 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 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/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 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' 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 =