- Timestamp:
- 03/27/14 14:44:36 (11 years ago)
- Location:
- Dev/trunk
- Files:
-
- 4 added
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
Dev/trunk/Gruntfile.js
r522 r531 74 74 ,'heroku-config' 75 75 ,'http:db-push-local-to-cloud']); 76 grunt.registerTask('cloudant-url', 77 "Print the Cloudant URL to the console.", 78 ['path-check:heroku' 79 ,'heroku-config' 80 ,'print:herokuConfig.CLOUDANT_URL:CLOUDANT_URL' 81 ]); 76 82 grunt.registerTask('#', 77 83 "---\nTASKS BELOW ARE INTERNAL AND SHOULD NOT USUALLY BE CALLED FROM THE COMMAND-LINE\n---", … … 304 310 grunt.loadTasks('./grunt-tasks'); 305 311 312 grunt.registerTask('print',"Print a variable.", 313 function(varname,name) { 314 grunt.log.write(grunt.template.process((name||varname)+"=<%= "+varname+" %>")); 315 }); 316 306 317 // UTIL FUNCTIONS 307 318 -
Dev/trunk/src/client/qed-client/model/classes/_Class.js
r525 r531 64 64 }, 65 65 _formatDate: function(date) { 66 return stamp.toISOString(date,{zulu:true,milliseconds: false});66 return stamp.toISOString(date,{zulu:true,milliseconds:true}); 67 67 }, 68 68 _sanitize: function(obj) { -
Dev/trunk/src/client/qed-client/model/widgets/SurveyRunWidget.js
r513 r531 11 11 var endDateBox = this.endDateBox; 12 12 this.own(this.startDateBox.on('change', function(value){ 13 endDateBox.constraints.min 13 endDateBox.constraints.min = value; 14 14 })); 15 15 }, … … 24 24 value.respondentCanDeleteOwnResponse ? ["on"] : []; 25 25 this.inherited(arguments); 26 this.endDateBox.constraints.min = 27 this.startDateBox.get('value'); 26 28 } 27 29 }); -
Dev/trunk/src/client/qed-client/pages/surveys.js
r529 r531 18 18 this.draftsTab.set('title','Drafts (<span class="qedLoading"></span>)'); 19 19 this.publishedTab.set('title','Published (<span class="qedLoading"></span>)'); 20 this.runsTab.set('title','Runs (<span class="qedLoading"></span>)');21 20 this.refresh(); 22 21 }, … … 56 55 _onRunSurvey:function(survey){ 57 56 var surveyRun = surveyRuns.create(); 58 surveyRun.title = 'Run of "' + survey.title + '" of '+(new Date().toString()); 57 surveyRun.title = 'Run of "'+survey.title+'" of '+ 58 (new Date().toString()); 59 59 surveyRun.survey = survey; 60 60 surveyRuns.save(surveyRun) 61 61 .then(lang.hitch(this,function(surveyRun){ 62 this._onRunDetails(surveyRun); 63 }),lang.hitch(this,function(err){ 64 this.notify(err.error,'error'); 65 })); 66 }, 67 _onRunDetails: function(surveyRun) { 68 Router.go(surveyRuns.getObjectPath(surveyRun)); 69 }, 70 _onRunDelete: function(surveyRun) { 71 if ( !confirm("Are you sure you want to delete this survey run?") ) { 72 return; 73 } 74 surveyRuns.remove(surveyRun) 75 .then(lang.hitch(this,function(){ 76 this.notify("SurveyRun successfully deleted."); 77 this.refreshRuns(); 62 Router.go(surveyRuns.getObjectPath( 63 surveyRuns.getId(surveyRun))); 78 64 }),lang.hitch(this,function(err){ 79 65 this.notify(err.error,'error'); … … 83 69 this.refreshDrafts(); 84 70 this.refreshPublished(); 85 this.refreshRuns();86 71 }, 87 72 refreshDrafts: function() { 88 this.draftsContainer.set('content',' ');73 this.draftsContainer.set('content','Loading draft surveys <span class="qedLoading"></span>'); 89 74 when(surveys.query({drafts:true}), lang.hitch(this,function(surveys) { 90 75 this.draftsTab.set('title','Drafts ('+surveys.length+')'); 76 this.draftsContainer.set('content',''); 91 77 array.forEach(surveys,function(survey){ 92 78 var w = new LineWithActionsWidget({ … … 127 113 }, 128 114 refreshPublished: function() { 129 this.publishedContainer.set('content',' ');115 this.publishedContainer.set('content','Loading published surveys <span class="qedLoading"></span>'); 130 116 when(surveys.query({published:true}), lang.hitch(this, function(surveys) { 117 this.publishedContainer.set('content',''); 131 118 this.publishedTab.set('title','Published ('+surveys.length+')'); 132 119 array.forEach(surveys,function(survey){ … … 152 139 },this); 153 140 })); 154 },155 refreshRuns: function() {156 this.runsContainer.set('content','');157 when(surveyRuns.query(), lang.hitch(this,function(surveyRuns){158 this.runsTab.set('title','Runs ('+surveyRuns.length+')');159 array.forEach(surveyRuns,function(surveyRun){160 var w = new LineWithActionsWidget({161 title: surveyRun.title || "",162 actions:[{163 callback: lang.hitch(this,'_onRunDetails',surveyRun),164 properties: {165 label: 'Details',166 tooltip: 'Show details for this run',167 icon: 'Edit'168 }169 },{170 callback: lang.hitch(this,'_onRunDelete',surveyRun),171 properties: {172 label: 'Delete',173 tooltip: 'Delete this run',174 icon: 'Delete'175 }176 }]177 });178 this.runsContainer.addChild(w);179 },this);180 }));181 141 } 182 142 }); -
Dev/trunk/src/client/qed-client/pages/templates/surveys.html
r529 r531 34 34 </div> 35 35 36 <div data-dojo-type="dijit/layout/BorderContainer"37 title="Runs"38 data-dojo-attach-point="runsTab">39 <div data-dojo-type="dijit/layout/ContentPane"40 data-dojo-props="region: 'center'"41 data-dojo-attach-point="runsContainer">42 </div>43 <div data-dojo-type="dijit/layout/ContentPane"44 data-dojo-props="region: 'bottom'"45 style="height: 40px;">46 </div>47 </div>48 36 </div> 49 37 -
Dev/trunk/src/client/qed-client/routes.js
r490 r531 1 1 define([ 2 2 "./model/classes/questions", 3 "./model/classes/sessions", 3 4 "./model/classes/surveyRuns", 4 5 "./model/classes/surveys", … … 9 10 "./pages/survey", 10 11 "./pages/surveyRun", 12 "./pages/surveyRuns", 11 13 "./pages/surveys" 12 ], function(questionsClass, s urveyRunsClass, surveysClass, index, previewSurvey, question, questions, survey, surveyRun, surveys) {14 ], function(questionsClass, sessionsClass, surveyRunsClass, surveysClass, index, previewSurvey, question, questions, survey, surveyRun, surveyRuns, surveys) { 13 15 14 16 return [ … … 19 21 { path: surveysClass.getCollectionPath(), constructor: surveys }, 20 22 { path: surveysClass.getObjectPath(':objectId'), constructor: survey }, 23 { path: surveyRunsClass.getCollectionPath(), constructor: surveyRuns }, 21 24 { path: surveyRunsClass.getObjectPath(':objectId'), constructor: surveyRun }, 22 //{ path: "/sessions", constructor: sessions },23 //{ path: "/session/:sessionId", constructor: session },25 //{ path: sessionsClass.getObjectPath(''), constructor: sessions }, 26 //{ path: sessionsClass.getObjectPath(':objectId'), constructor: session }, 24 27 { path: surveysClass.getPreviewPath(':surveyId'), constructor: previewSurvey } 25 28 ]; -
Dev/trunk/src/client/qed-client/ui/templates/MainMenu.html
r466 r531 1 1 <div class="mainMenu"> 2 2 <div data-dojo-type="dijit/MenuBar"> 3 <div class="rftMainMenuButton" data-dojo-type="./MenuBarLink" data-dojo-props="path:'/sessions'">Sessions</div> 4 <div class="rftMainMenuButton" data-dojo-type="dijit/PopupMenuBarItem"> 3 <div class="rftMainMenuButton" 4 data-dojo-type="./MenuBarLink" data-dojo-props="path:'/sessions'">Sessions</div> 5 <div class="rftMainMenuButton" 6 data-dojo-type="dijit/PopupMenuBarItem"> 5 7 <span>Content</span> 6 8 <div data-dojo-type="dijit/DropDownMenu"> 7 <div data-dojo-type="./MenuLink" class="blue bgColorHover" data-dojo-props="path:'/surveys', iconClass:'rftIcon rftIconSurvey'">Surveys</div> 8 <div data-dojo-type="./MenuLink" class="orange bgColorHover" data-dojo-props="path:'/questions', iconClass:'rftIcon rftIconQuestion'">Questions</div> 9 <div data-dojo-type="./MenuLink" class="purple bgColorHover" data-dojo-props="path:'/applications', iconClass: 'rftIcon rftIconApplication'">Applications</div> 10 <div data-dojo-type="./MenuLink" class="red bgColorHover" data-dojo-props="path:'/dashboards', iconClass: 'rftIcon rftIconDashboard'">Dashboards</div> 9 <div data-dojo-type="./MenuLink" 10 class="blue bgColorHover" 11 data-dojo-props="path:'/surveys', iconClass:'rftIcon rftIconSurvey'"> 12 Surveys</div> 13 <div data-dojo-type="./MenuLink" 14 class="orange bgColorHover" 15 data-dojo-props="path:'/questions', iconClass:'rftIcon rftIconQuestion'"> 16 Questions</div> 11 17 </div> 12 18 </div> 13 <div class="rftMainMenuButton" data-dojo-type="./MenuBarLink" data-dojo-props="path:'/results'">Results</div> 14 <div class="rftMainMenuButton" data-dojo-type="./SessionMenu"></div> 19 <div class="rftMainMenuButton" 20 data-dojo-type="./MenuBarLink" data-dojo-props="path:'/surveyRuns'"> 21 Results</div> 22 <div class="rftMainMenuButton" 23 data-dojo-type="./SessionMenu"></div> 15 24 </div> 16 25 </div> -
Dev/trunk/src/server/api/responses.js
r527 r531 13 13 14 14 var getResponsesBySurveyRunId = exports.getResponsesBySurveyRunId = function(surveyRunId) { 15 var url = '_design/responses/_view/by_surveyrun?key='+JSON.stringify(surveyRunId);16 return couch.get(url)15 return couch.get('_design/responses/_view/by_surveyrun', 16 {query:{key:surveyRunId,reduce:false,include_docs:true}}) 17 17 .handle({ 18 18 '-1': _.identity, 19 200: util.handleRow Values,19 200: util.handleRowDocs, 20 20 default: util.handleUnknownResponse 21 21 }); -
Dev/trunk/src/server/api/surveyRuns.js
r527 r531 15 15 app.get('/', 16 16 util.ensureMIME(util.JSON_MIME), 17 util.makeDocsGet('SurveyRun')); 17 function(req,res) { 18 var qs = {reduce:false,include_docs:true}; 19 var now = new Date().toISOString(); 20 var hr; 21 if ( 'current' in req.query ) { 22 // we make the assumption that there will be fewer 23 // runs with no or a future enddate than with a 24 // startdate in the past, i.e. we assume fewer future 25 // surveys are planned than past surveys are run. 26 hr = couch.get('_design/surveyRuns/_view/by_end_date',{ 27 query: {startkey:now,reduce:false,include_docs:true} 28 }).handle({ 29 '-1': _.identity, 30 200: function(result) { 31 return _.filter( 32 util.handleRowDocs(result), 33 function(doc){ 34 return !doc.startDate || 35 doc.startDate <= now; 36 }); 37 }, 38 default: util.handleUnknownResponse 39 }); 40 } else if ( 'future' in req.query ) { 41 hr = couch.get('_design/surveyRuns/_view/by_start_date',{ 42 query: {startkey:now,reduce:false,include_docs:true} 43 }).handle({ 44 '-1': _.identity, 45 200: util.handleRowDocs, 46 default: util.handleUnknownResponse 47 }); 48 } else if ( 'past' in req.query ) { 49 hr = couch.get('_design/surveyRuns/_view/by_end_date',{ 50 query: {endkey:now,reduce:false,include_docs:true} 51 }).handle({ 52 '-1': _.identity, 53 200: util.handleRowDocs, 54 default: util.handleUnknownResponse 55 }); 56 } else { 57 hr = util.getDocumentsOfType('SurveyRun'); 58 } 59 hr.handle({'-1': util.handleException}) 60 .handle(res.send.bind(res)); 61 }); 18 62 app.post('/', 19 63 util.ensureMIME(util.JSON_MIME), -
Dev/trunk/src/server/api/util.js
r527 r531 81 81 82 82 var getDocumentsOfType = exports.getDocumentsOfType = function(type) { 83 var url = '_design/default/_view/by_type?key='+JSON.stringify(type);84 return couch.get(url)83 return couch.get('_design/default/_view/by_type', 84 {query:{reduce:false,include_docs:true,key:type}}) 85 85 .handle({ 86 86 '-1': _.identity, 87 200: handleRow Values,87 200: handleRowDocs, 88 88 404: function() { return {error: "Cannot find collection of type "+type}; }, 89 89 default: handleUnknownResponse -
Dev/trunk/src/server/config/couchdb-design-docs.js
r525 r531 30 30 _id: "schemaInfo", 31 31 version: "4", 32 viewsVersion: " 1"32 viewsVersion: "2" 33 33 }, 34 34 … … 49 49 by_type: { 50 50 map: function(doc){ 51 emit(doc.type, doc); 52 } 51 emit(doc.type, 1); 52 }, 53 reduce: function(keys,values){ return sum(values); } 53 54 }, 54 55 typeless: { … … 104 105 emit(doc.topic||"(default)",1); 105 106 }, 106 reduce: function(key, values , rereduce) { return sum(values); }107 reduce: function(key, values ) { return sum(values); } 107 108 }, 108 109 all_variables: { … … 137 138 map: function(doc){ 138 139 if ( doc.type !== 'Question' ) { return; } 139 emit(doc.code,doc); 140 } 140 emit(doc.code,1); 141 }, 142 reduce: function(key, values) { return sum(values); } 141 143 }, 142 144 published_by_code: { 143 145 map: function(doc){ 144 if ( doc.type !== 'Question' || !doc.publicationDate ) { return; } 145 emit(doc.code,doc); 146 } 146 if ( doc.type !== 'Question' || 147 !doc.publicationDate ) { return; } 148 emit(doc.code,1); 149 }, 150 reduce: function(key, values) { return sum(values); } 147 151 }, 148 152 lib: { … … 159 163 drafts: { 160 164 map: function(doc){ 161 if ( doc.type !== 'Survey' || doc.publicationDate ) { return; } 165 if ( doc.type !== 'Survey' || 166 doc.publicationDate ) { return; } 162 167 emit(doc._id,doc); 163 168 } … … 165 170 published: { 166 171 map: function(doc){ 167 if ( doc.type !== 'Survey' || !doc.publicationDate ) { return; } 172 if ( doc.type !== 'Survey' || 173 !doc.publicationDate ) { return; } 168 174 emit(doc._id,doc); 169 175 } … … 176 182 language: "javascript", 177 183 views: { 178 by_ dates: {184 by_start_date: { 179 185 map: function(doc){ 180 186 if ( doc.type !== 'SurveyRun' ) { return; } 181 var startDate = doc.startDate || ""; 182 var endDate = doc.endDate || {}; 183 emit([startDate,endDate,doc.liveName||null],doc); 184 } 187 emit(doc.startDate||null,1); 188 }, 189 reduce: function(keys,values){ return sum(values); } 190 }, 191 by_end_date: { 192 map: function(doc){ 193 if ( doc.type !== 'SurveyRun' ) { return; } 194 emit(doc.endDate||{},1); 195 }, 196 reduce: function(keys,values){ return sum(values); } 185 197 } 186 198 } … … 194 206 map: function(doc){ 195 207 if ( doc.type !== 'Response' ) { return; } 196 emit(doc.surveyRunId, doc); 197 } 208 emit(doc.surveyRunId, 1); 209 }, 210 reduce: function(keys,values){ return sum(values); } 198 211 } 199 212 } -
Dev/trunk/src/server/config/couchdb-schema.json
r525 r531 10 10 "definitions": { 11 11 "nonEmptyString": { "type": "string", "minLength": 1 }, 12 "codeString": { "type": "string", "pattern": "^[A-Za-z0-9]+$" }, 13 "subcodeString": { "type": "string", "pattern": "^[A-Za-z0-9]*$" }, 12 "code": { "type": "string", "pattern": "^[A-Za-z0-9]+$" }, 13 "subcode": { "type": "string", "pattern": "^[A-Za-z0-9]*$" }, 14 "datetime": {"type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}Z$"}, 15 "html5Email": {"type": "string", "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"}, 14 16 "schemaInfo": { 15 17 "type": "object", … … 40 42 "_rev": { "$ref": "#/definitions/nonEmptyString" }, 41 43 "categories": { "type": "array", "items": { "$ref": "#/definitions/nonEmptyString" } }, 42 "code": { "$ref": "#/definitions/code String" },44 "code": { "$ref": "#/definitions/code" }, 43 45 "content": { "type": "array", "items": { "$ref": "#/definitions/content/any" } }, 44 46 "description": { "$ref": "#/definitions/nonEmptyString" }, 45 "publicationDate": { " type": "string", "format": "datetime" },47 "publicationDate": { "$ref": "#/definitions/datetime" }, 46 48 "title": { "$ref": "#/definitions/nonEmptyString" }, 47 49 "topic": { "$ref": "#/definitions/nonEmptyString" } … … 57 59 "_rev": { "$ref": "#/definitions/nonEmptyString" }, 58 60 "description": { "$ref": "#/definitions/nonEmptyString" }, 59 "publicationDate": { " type": "string", "format": "datetime" },61 "publicationDate": { "$ref": "#/definitions/datetime" }, 60 62 "questions": { "type": "array", "items": { "$ref": "#/definitions/docs/Question" } }, 61 63 "title": { "$ref": "#/definitions/nonEmptyString" } … … 71 73 "_rev": { "$ref": "#/definitions/nonEmptyString" }, 72 74 "description": { "$ref": "#/definitions/nonEmptyString" }, 73 "endDate": { "type": "string", "format": "datetime" }, 74 "liveName": { "$ref": "#/definitions/nonEmptyString" }, 75 "endDate": { "$ref": "#/definitions/datetime" }, 75 76 "mode": { "type": "string", "enum": [ "open", "closed" ] }, 76 77 "respondentCanDeleteOwnResponse": { "type": "boolean" }, 77 78 "secret": { "$ref": "#/definitions/nonEmptyString" }, 78 "startDate": { " type": "string", "format": "datetime" },79 "startDate": { "$ref": "#/definitions/datetime" }, 79 80 "survey": { "$ref": "#/definitions/docs/Survey" }, 80 81 "title": { "$ref": "#/definitions/nonEmptyString" } … … 96 97 "additionalProperties": false 97 98 }, 98 "email": { "type": "string", "format": "email" }, 99 "publicationDate": { "type": "string", "format": "datetime" }, 99 "publicationDate": { "$ref": "#/definitions/datetime" }, 100 100 "secret": { "$ref": "#/definitions/nonEmptyString" }, 101 101 "surveyRunId": { "$ref": "#/definitions/nonEmptyString" } … … 149 149 "properties": { 150 150 "type": { "type": "string", "pattern": "^StringInput$" }, 151 "subcode": { "$ref": "#/definitions/subcode String" },151 "subcode": { "$ref": "#/definitions/subcode" }, 152 152 "text": { "$ref": "#/definitions/nonEmptyString" } 153 153 }, … … 160 160 "type": { "type": "string", "pattern": "^TextInput$" }, 161 161 "maxLength": { "type": "integer" }, 162 "subcode": { "$ref": "#/definitions/subcode String" },162 "subcode": { "$ref": "#/definitions/subcode" }, 163 163 "text": { "$ref": "#/definitions/nonEmptyString" } 164 164 }, … … 173 173 "max": { "type": "integer" }, 174 174 "places": { "type": "integer" }, 175 "subcode": { "$ref": "#/definitions/subcode String" },175 "subcode": { "$ref": "#/definitions/subcode" }, 176 176 "text": { "$ref": "#/definitions/nonEmptyString" } 177 177 }, … … 193 193 "minLabel": { "$ref": "#/definitions/nonEmptyString" }, 194 194 "maxLabel": { "$ref": "#/definitions/nonEmptyString" }, 195 "subcode": { "$ref": "#/definitions/subcode String" },195 "subcode": { "$ref": "#/definitions/subcode" }, 196 196 "text": { "$ref": "#/definitions/nonEmptyString" } 197 197 }, … … 219 219 "type": "object", 220 220 "properties": { 221 "subcode": { "$ref": "#/definitions/subcode String" }221 "subcode": { "$ref": "#/definitions/subcode" } 222 222 }, 223 223 "required": ["subcode"], 224 224 "additionalProperties": false 225 225 }, 226 "subcode": { "$ref": "#/definitions/subcode String" }226 "subcode": { "$ref": "#/definitions/subcode" } 227 227 }, 228 228 "required":["type","items","subcode"], … … 236 236 "type": "object", 237 237 "properties": { 238 "subcode": { "$ref": "#/definitions/subcode String" },238 "subcode": { "$ref": "#/definitions/subcode" }, 239 239 "text": { "$ref": "#/definitions/nonEmptyString" } 240 240 }, … … 245 245 "type": "object", 246 246 "properties": { 247 "subcode": { "$ref": "#/definitions/subcode String" }247 "subcode": { "$ref": "#/definitions/subcode" } 248 248 }, 249 249 "required": ["subcode"], -
Dev/trunk/src/server/util/http-result.js
r527 r531 63 63 if ( status in fOrObj ) { 64 64 return fOrObj[status](result); 65 } else if ( status >= 200 && status < 300 && 66 'success' in fOrObj ) { 67 return fOrObj.success(status,result); 68 } else if ( !(status >= 200 && status < 300) && 69 'failure' in fOrObj ) { 70 return fOrObj.failure(status,result); 65 71 } else if ( 'default' in fOrObj ) { 66 72 return fOrObj['default'](status,result); -
Dev/trunk/src/server/util/validator.js
r493 r531 1 1 var tv4 = require('tv4'); 2 3 // from: http://www.w3.org/TR/html5/forms.html#valid-e-mail-address4 var html5EmailRe = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;5 var datetimeRe = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z/;6 7 tv4.addFormat({8 email: function(data){9 if ( typeof data === "string" && html5EmailRe.test(data) ) {10 return null;11 } else {12 return "Probably an invalid email address.";13 }14 },15 datetime: function(data){16 if ( typeof data === "string" && datetimeRe.test(data) ) {17 return null;18 } else {19 return "Invalid timestamp.";20 }21 }22 });23 2 24 3 module.exports = function() {
Note: See TracChangeset
for help on using the changeset viewer.