Index: Dev/trunk/src/server/app.js
===================================================================
--- Dev/trunk/src/server/app.js	(revision 507)
+++ Dev/trunk/src/server/app.js	(revision 508)
@@ -154,5 +154,5 @@
     }
     function areDocsPublished(docs) {
-        return _.every(docs,isDocPublished);
+        return _.every(docs, isDocPublished);
     }
     function isDocPublished(doc) {
@@ -592,5 +592,5 @@
             if ( !areDocsUnique(doc.questions) ) {
                 hr = new HTTPResult(400,{error:"Survey contains duplicate questions."});
-            } else if ( !areDocsPublished(doc.questions) || isDocPublished(doc) ) {
+            } else if ( isDocPublished(doc) && !areDocsPublished(doc.questions) ) {
                 hr = new HTTPResult(400,{error:"Cannot publish Survey with unpublished questions."});
             } else {
@@ -610,10 +610,10 @@
             var doc = req.body;
             var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev);
-            var hr;
             if ( !areDocsUnique(doc.questions) ) {
                 new HTTPResult(400,{error:"Survey contains duplicate questions."})
                 .handle(res.send.bind(res));
-            } else if ( !areDocsPublished(doc.questions) || isDocPublished(doc) ) {
-                hr = new HTTPResult(400,{error:"Cannot publish Survey with unpublished questions."});
+            } else if ( isDocPublished(doc) && !areDocsPublished(doc.questions) ) {
+                new HTTPResult(400,{error:"Cannot publish Survey with unpublished questions."})
+                .handle(res.send.bind(res));
             } else {
                 putDocument(id,rev,'Survey',doc)
@@ -718,10 +718,12 @@
             .handle({
                 200: function(responses) {
-                    var flatResponses = responsesToVariables(responses);
+                    var answers = _.map(responses,function(response){
+                        return response.answers;
+                    });
                     res.set({
                         'Content-Disposition': 'attachment; filename=surveyRun-'+id+'-responses.csv'
                     });
                     res.status(200);
-                    writeObjectsToCSVStream(flatResponses, res);
+                    writeObjectsToCSVStream(answers, res);
                     res.end();
                 },
@@ -953,42 +955,4 @@
 }
 
-function responsesToVariables(responses) {
-    return _.map(responses, responseToVariables);
-}
-
-function responseToVariables(response) {
-    var result = flattenObject(response.answers);
-    return result;
-}
-
-function flattenObject(value) {
-    var result = {};
-    (function agg(val,key,res){
-        if ( _.isObject(val) ) {
-            var keys = _.keys(val);
-            // FIXME : dirty hack for questions with only one input
-            if ( keys.length === 1 ) {
-                agg(val[keys[0]],key,res);
-            } else {
-                _.forEach(val, function(v,k){
-                    agg(v,(key ? key+'.' : '')+k,res);
-                });
-            }
-        } else if ( _.isArray(val) ) {
-            // FIXME : dirty hack for questions with only one input
-            if ( val.length === 1 ) {
-                agg(val[0],key,res);
-            } else {
-                _.forEach(val, function(v,i){
-                    agg(v,(key ? key+'.' : '')+i,res);
-                });
-            }
-        } else {
-            res[key] = val;
-        }
-    })(value,null,result);
-    return result;
-}
-
 function writeObjectsToCSVStream(objects, stream, prelude) {
     var keys = _.chain(objects)
Index: Dev/trunk/src/server/bin/upgrade-db-3-to-4.js
===================================================================
--- Dev/trunk/src/server/bin/upgrade-db-3-to-4.js	(revision 508)
+++ Dev/trunk/src/server/bin/upgrade-db-3-to-4.js	(revision 508)
@@ -0,0 +1,33 @@
+var env = require('../env')
+  , upgradeCouch = require('../config/upgrade-couchdb')
+  , cryptoken = require('../util/crypto-token')
+  , Q = require('q')
+  , _ = require('underscore')
+  ;
+
+function flatten(obj,prefix,newObj) {
+    prefix = prefix || "";
+    newObj = newObj || {};
+    _.each(obj,function(val,key){
+        key = key.replace(/^[0-9]+\//,'');
+        if ( _.isObject(val) && !_.isArray(val) ) {
+            flatten(val,prefix+key,newObj);
+        } else {
+            newObj[prefix+key] = val;
+        }
+    });
+    return newObj;
+}
+
+upgradeCouch(env.couchServerURL,env.dbName,function(doc){
+    if ( doc.type === "Response" ) {
+        doc.answers = flatten(doc.answers);
+        return doc;
+    } else {
+        return Q.reject();
+    }
+}).then(function(res){
+    console.log("done",res);
+}, function(err){
+    console.error("fail",err);
+});
Index: Dev/trunk/src/server/config/couchdb-design-docs.js
===================================================================
--- Dev/trunk/src/server/config/couchdb-design-docs.js	(revision 507)
+++ Dev/trunk/src/server/config/couchdb-design-docs.js	(revision 508)
@@ -11,5 +11,5 @@
 
     "qed/schemaInfo": {
-        version: "3"
+        version: "4"
     },
 
Index: Dev/trunk/src/server/config/couchdb-schema.json
===================================================================
--- Dev/trunk/src/server/config/couchdb-schema.json	(revision 507)
+++ Dev/trunk/src/server/config/couchdb-schema.json	(revision 508)
@@ -2,5 +2,5 @@
   "$schema": "http://json-schema.org/draft-04/schema#",
   "title": "QED Object Schema",
-  "version": "3",
+  "version": "4",
   "type": "object",
   "oneOf": [
@@ -10,4 +10,5 @@
   "definitions": {
     "nonEmptyString": { "type": "string", "minLength": 1 },
+    "codeString": { "type": "string", "pattern": "^[A-Za-z0-9]+$" },
     "schemaInfo": {
       "type": "object",
@@ -37,5 +38,5 @@
           "_rev": { "$ref": "#/definitions/nonEmptyString" },
           "categories": { "type": "array", "items": { "$ref": "#/definitions/nonEmptyString" } },
-          "code": { "$ref": "#/definitions/nonEmptyString" },
+          "code": { "$ref": "#/definitions/codeString" },
           "content": { "type": "array", "items": { "$ref": "#/definitions/content/any" } },
           "description": { "$ref": "#/definitions/nonEmptyString" },
@@ -86,5 +87,11 @@
           "_id": { "$ref": "#/definitions/nonEmptyString" },
           "_rev": { "$ref": "#/definitions/nonEmptyString" },
-          "answers": { "type": "object" },
+          "answers": {
+              "type": "object",
+              "patternProperties": {
+                  "^[A-Za-z0-9]+$": {}
+              },
+              "additionalProperties": false
+          },
           "email": { "type": "string", "format": "email" },
           "publicationDate": { "type": "string", "format": "datetime" },
@@ -139,5 +146,5 @@
         "properties": {
           "type": { "type": "string", "pattern": "^StringInput$" },
-          "subcode": { "$ref": "#/definitions/nonEmptyString" },
+          "subcode": { "$ref": "#/definitions/codeString" },
           "text": { "$ref": "#/definitions/nonEmptyString" }
         },
@@ -150,5 +157,5 @@
           "type": { "type": "string", "pattern": "^TextInput$" },
           "maxLength": { "type": "integer" },
-          "subcode": { "$ref": "#/definitions/nonEmptyString" },
+          "subcode": { "$ref": "#/definitions/codeString" },
           "text": { "$ref": "#/definitions/nonEmptyString" }
         },
@@ -163,5 +170,5 @@
           "max": { "type": "integer" },
           "places": { "type": "integer" },
-          "subcode": { "$ref": "#/definitions/nonEmptyString" },
+          "subcode": { "$ref": "#/definitions/codeString" },
           "text": { "$ref": "#/definitions/nonEmptyString" }
         },
@@ -183,5 +190,5 @@
               "minLabel": { "$ref": "#/definitions/nonEmptyString" },
               "maxLabel": { "$ref": "#/definitions/nonEmptyString" },
-              "subcode": { "$ref": "#/definitions/nonEmptyString" },
+              "subcode": { "$ref": "#/definitions/codeString" },
               "text": { "$ref": "#/definitions/nonEmptyString" }
             },
@@ -201,5 +208,5 @@
               "type": "object",
               "properties": {
-                  "subcode": { "$ref": "#/definitions/nonEmptyString" },
+                  "subcode": { "$ref": "#/definitions/codeString" },
                   "text": { "$ref": "#/definitions/nonEmptyString" }
               },
@@ -210,5 +217,5 @@
               "type": "object",
               "properties": {
-                  "subcode": { "$ref": "#/definitions/nonEmptyString" },
+                  "subcode": { "$ref": "#/definitions/codeString" },
                   "text": { "$ref": "#/definitions/nonEmptyString" }
               },
