From b0c7b50761e8611f72d3ea80573f1385b00e52f4 Mon Sep 17 00:00:00 2001
From: Nick Winter <livelily@gmail.com>
Date: Tue, 30 Jun 2015 11:27:58 -0700
Subject: [PATCH] Added programming concept tags to campaign and level editor.

---
 app/locale/en.coffee                          | 20 ++++++++++++++++++
 app/schemas/models/campaign.schema.coffee     |  1 +
 app/schemas/models/level.coffee               |  2 ++
 app/schemas/schemas.coffee                    | 21 +++++++++++++++++++
 app/styles/play/campaign-view.sass            |  5 +++++
 app/templates/play/campaign-view.jade         |  4 ++++
 .../level/settings/SettingsTabView.coffee     |  2 +-
 server/levels/level_handler.coffee            |  1 +
 8 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/app/locale/en.coffee b/app/locale/en.coffee
index 9ecca7e4e..3647f5d28 100644
--- a/app/locale/en.coffee
+++ b/app/locale/en.coffee
@@ -1192,6 +1192,26 @@
     poll: "Poll"
     user_polls_record: "Poll Voting History"
 
+  concepts:
+    advanced_strings: "Advanced Strings"
+    algorithms: "Algorithms"
+    arithmetic: "Arithmetic"
+    arrays: "Arrays"
+    basic_syntax: "Basic Syntax"
+    boolean_logic: "Boolean Logic"
+    break_statements: "Break Statements"
+    classes: "Classes"
+    for_loops: "For Loops"
+    functions: "Functions"
+    if_statements: "If Statements"
+    input_handling: "Input Handling"
+    math_operations: "Math Operations"
+    object_literals: "Object Literals"
+    strings: "Strings"
+    variables: "Variables"
+    vectors: "Vectors"
+    while_loops: "While Loops"
+
   delta:
     added: "Added"
     modified: "Modified"
diff --git a/app/schemas/models/campaign.schema.coffee b/app/schemas/models/campaign.schema.coffee
index eae2985dc..5fba3ba9f 100644
--- a/app/schemas/models/campaign.schema.coffee
+++ b/app/schemas/models/campaign.schema.coffee
@@ -115,6 +115,7 @@ _.extend CampaignSchema.properties, {
       campaign: c.shortString title: 'Campaign', description: 'Which campaign this level is part of (like "desert").', format: 'hidden'  # Automatically set by campaign editor.
 
       tasks: c.array {title: 'Tasks', description: 'Tasks to be completed for this level.'}, c.task
+      concepts: c.array {title: 'Programming Concepts', description: 'Which programming concepts this level covers.'}, c.concept
 
       #- normal properties
       position: c.point2d()
diff --git a/app/schemas/models/level.coffee b/app/schemas/models/level.coffee
index 1a49a0eaf..e1bb882e8 100644
--- a/app/schemas/models/level.coffee
+++ b/app/schemas/models/level.coffee
@@ -263,6 +263,7 @@ LevelSchema = c.object {
       {id: 'ogres-die', name: 'Ogres must die.', killThangs: ['ogres'], worldEndsAfter: 3}
       {id: 'humans-survive', name: 'Your hero must survive.', saveThangs: ['Hero Placeholder'], howMany: 1, worldEndsAfter: 3, hiddenGoal: true}
     ]
+    concepts: ['basic_syntax']
 }
 c.extendNamedProperties LevelSchema  # let's have the name be the first property
 _.extend LevelSchema.properties,
@@ -344,6 +345,7 @@ _.extend LevelSchema.properties,
   campaign: c.shortString title: 'Campaign', description: 'Which campaign this level is part of (like "desert").', format: 'hidden'  # Automatically set by campaign editor.
   scoreTypes: c.array {title: 'Score Types', description: 'What metric to show leaderboards for.', uniqueItems: true},
      c.shortString(title: 'Score Type', 'enum': ['time', 'damage-taken', 'damage-dealt', 'gold-collected', 'difficulty'])  # TODO: good version of LoC; total gear value.
+  concepts: c.array {title: 'Programming Concepts', description: 'Which programming concepts this level covers.', uniqueItems: true}, c.concept
 
 
 c.extendBasicProperties LevelSchema, 'level'
diff --git a/app/schemas/schemas.coffee b/app/schemas/schemas.coffee
index 8240f2e8b..c9437860b 100644
--- a/app/schemas/schemas.coffee
+++ b/app/schemas/schemas.coffee
@@ -235,3 +235,24 @@ me.RewardSchema = (descriptionFragment='earned by achievements') ->
 me.task = me.object {title: 'Task', description: 'A task to be completed', format: 'task', default: {name: 'TODO', complete: false}},
   name: {title: 'Name', description: 'What must be done?', type: 'string'}
   complete: {title: 'Complete', description: 'Whether this task is done.', type: 'boolean', format: 'checkbox'}
+
+me.concept = me.shortString enum: [
+    'advanced_strings'
+    'algorithms'
+    'arithmetic'
+    'arrays'
+    'basic_syntax'
+    'boolean_logic'
+    'break_statements'
+    'classes'
+    'for_loops'
+    'functions'
+    'if_statements'
+    'input_handling'
+    'math_operations'
+    'object_literals'
+    'strings'
+    'variables'
+    'vectors'
+    'while_loops'
+  ]
diff --git a/app/styles/play/campaign-view.sass b/app/styles/play/campaign-view.sass
index 6f358bd6e..9611133cd 100644
--- a/app/styles/play/campaign-view.sass
+++ b/app/styles/play/campaign-view.sass
@@ -238,6 +238,11 @@ $gameControlMargin: 30px
             margin: 0px auto
             max-width: 100%
 
+        kbd
+          margin: 0 2px 2px 0
+          display: inline-block
+          font-size: 12px
+
         .level-status
           background: transparent url(/images/pages/play/level-info-status-spritesheet.png) no-repeat 0 0
           width: 60px
diff --git a/app/templates/play/campaign-view.jade b/app/templates/play/campaign-view.jade
index ecb95043a..eb3e536df 100644
--- a/app/templates/play/campaign-view.jade
+++ b/app/templates/play/campaign-view.jade
@@ -39,6 +39,10 @@ if campaign
                 a.spr(href="/contribute/adventurer")
                   strong(data-i18n="play.awaiting_levels_adventurer") Sign up as an Adventurer
                 span.spl(data-i18n="play.awaiting_levels_adventurer_suffix") to be the first to play new levels.
+            if level.concepts && level.concepts.length
+              p
+                for concept in level.concepts
+                  kbd(data-i18n="concepts." + concept)
 
             if !level.disabled && !level.locked
               if playCount && playCount.sessions
diff --git a/app/views/editor/level/settings/SettingsTabView.coffee b/app/views/editor/level/settings/SettingsTabView.coffee
index 5c7181824..f3a42a20d 100644
--- a/app/views/editor/level/settings/SettingsTabView.coffee
+++ b/app/views/editor/level/settings/SettingsTabView.coffee
@@ -15,7 +15,7 @@ module.exports = class SettingsTabView extends CocoView
   editableSettings: [
     'name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals',
     'type', 'terrain', 'showsGuide', 'banner', 'employerDescription', 'loadingTip', 'requiresSubscription',
-    'tasks', 'helpVideos', 'replayable', 'scoreTypes'
+    'tasks', 'helpVideos', 'replayable', 'scoreTypes', 'concepts'
   ]
 
   subscriptions:
diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee
index b112cce8c..26ea7ac26 100644
--- a/server/levels/level_handler.coffee
+++ b/server/levels/level_handler.coffee
@@ -60,6 +60,7 @@ LevelHandler = class LevelHandler extends Handler
     'replayable'
     'buildTime'
     'scoreTypes'
+    'concepts'
   ]
 
   postEditableProperties: ['name']