Skip to content

Commit f45deb5

Browse files
committed
Merge branch 'reports' into dev
2 parents 432d14c + 3bf32f2 commit f45deb5

File tree

5 files changed

+151
-95
lines changed

5 files changed

+151
-95
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,15 @@
1414
instead of
1515
```
1616
<form-input ng-repeat="field in formSchema" info={{field}}>
17-
```
17+
```
18+
* findFunc option now returns a query object such as
19+
```
20+
{field:'value'}
21+
```
22+
rather than a MongooseJS Query such as
23+
```
24+
model.find().where('field', 'value')
25+
```
1826
* Bespoke directives need to be modified (changes will normally be from something like
1927
```
2028
var info = JSON.parse(attrs.info);

app/code/b_using_options.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,28 @@ BSchema.statics.form = function(layout) {
8080

8181
BSchema.statics.findAccepted = function(req,cb) {
8282
// Only show the accepted items
83-
cb(null, B.find().where('accepted', true));
83+
cb(null, {accepted:true});
8484
};
8585

8686
BSchema.statics.prepareSave = function(doc, req, cb) {
8787
doc.ipAddress = req.ip;
8888
cb(null);
8989
};
9090

91+
BSchema.statics.report = function(report) {
92+
var reportSchema = '';
93+
switch (report) {
94+
case 'allVisible' :
95+
reportSchema = {
96+
pipeline: [{$group:{_id:"$accepted",count:{"$sum":1}}}],
97+
title: "Numbers of Applicants By Status"
98+
};
99+
break;
100+
}
101+
return reportSchema;
102+
};
103+
104+
91105
module.exports = {
92106
model : B // pass the model in an object if you want to add options
93107
, findFunc: BSchema.statics.findAccepted // this can be used to 'pre' filter selections.

server/lib/data_form.js

Lines changed: 103 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ DataForm.prototype.models = function (req, res, next) {
284284

285285
return function (req, res, next) {
286286
res.send(that.resources);
287-
}
287+
};
288288

289289
// return _.bind(function (req, res, next) {
290290
// res.send(this.resources)
@@ -364,7 +364,7 @@ DataForm.prototype.preprocess = function (paths, formSchema) {
364364
}
365365
}
366366
}
367-
var returnObj = {paths: outPath}
367+
var returnObj = {paths: outPath};
368368
if (hiddenFields.length > 0) {
369369
returnObj.hide = hiddenFields;
370370
}
@@ -408,80 +408,92 @@ DataForm.prototype.report = function () {
408408
var schemaCopy = {};
409409
extend(schemaCopy, reportSchema);
410410
schemaCopy.params = schemaCopy.params || [];
411-
// Bit crap here switching back and forth to string
412-
runPipeline = JSON.stringify(schemaCopy.pipeline);
413-
for (var param in url_parts.query) {
414-
if (param !== 'r') { // we don't want to copy the whole report schema (again!)
415-
schemaCopy.params[param].value = url_parts.query[param];
416-
}
417-
}
418-
runPipeline = runPipeline.replace(/\"\(.+?\)\"/g, function(match){
419-
param = schemaCopy.params[match.slice(2,-2)];
420-
return param.type === 'number' ? param.value : '"'+param.value+'"';
421-
})
422-
runPipeline = JSON.parse(runPipeline);
423411

424-
var toDo = {runAggregation: function(cb,results) {
425-
req.resource.model.aggregate(runPipeline, cb)
426-
}
427-
};
428-
429-
// if we need to do any column translations add the function to the tasks list
430-
if (reportSchema.columnTranslations) {
431-
toDo.apply_translations = ['runAggregation', function(cb,results) {
432-
reportSchema.columnTranslations.forEach(function(columnTranslation){
433-
results.runAggregation.forEach(function(resultRow){
434-
var thisTranslation = _.find(columnTranslation.translations, function(option){
435-
return resultRow[columnTranslation.field].toString() === option.value.toString()
436-
});
437-
resultRow[columnTranslation.field] = thisTranslation.display;
438-
})
412+
self.doFindFunc(req, req.resource, function(err, queryObj) {
413+
414+
if (err) {
415+
return self.renderError(new Error("There was a problem with the findFunc for model " + req.resource.modelName), null, req, res, next);
416+
} else {
417+
if (queryObj) {
418+
schemaCopy.pipeline.unshift({$match:queryObj});
419+
}
420+
421+
// Bit crap here switching back and forth to string
422+
runPipeline = JSON.stringify(schemaCopy.pipeline);
423+
for (var param in url_parts.query) {
424+
if (param !== 'r') { // we don't want to copy the whole report schema (again!)
425+
schemaCopy.params[param].value = url_parts.query[param];
426+
}
427+
}
428+
runPipeline = runPipeline.replace(/\"\(.+?\)\"/g, function(match){
429+
param = schemaCopy.params[match.slice(2,-2)];
430+
return param.type === 'number' ? param.value : '"'+param.value+'"';
439431
});
440-
cb(null,null);
441-
}];
442-
443-
// if any of the column translations are refs, set up the tasks to look up the values and populate the translations
444-
for (var i=0; i < reportSchema.columnTranslations.length; i++) {
445-
var thisColumnTranslation = reportSchema.columnTranslations[i]
446-
, translateName = thisColumnTranslation.field;
447-
if (translateName){
448-
if (thisColumnTranslation.ref) {
449-
var lookup = self.getResource(thisColumnTranslation.ref);
450-
if (lookup) {
451-
if (toDo[translateName]) {
452-
return self.renderError(new Error("Cannot have two columnTranslations for field " + translateName ), null, req, res, next);
453-
} else {
454-
thisColumnTranslation.translations = thisColumnTranslation.translations || [];
455-
toDo[translateName] = function(cb,results) {lookup.model.find({},{},{lean:true},function(err,findResults){
456-
if (err) {
457-
cb(err);
432+
runPipeline = JSON.parse(runPipeline);
433+
434+
var toDo = {runAggregation: function(cb,results) {
435+
req.resource.model.aggregate(runPipeline, cb)
436+
}
437+
};
438+
439+
// if we need to do any column translations add the function to the tasks list
440+
if (reportSchema.columnTranslations) {
441+
toDo.apply_translations = ['runAggregation', function(cb,results) {
442+
reportSchema.columnTranslations.forEach(function(columnTranslation){
443+
results.runAggregation.forEach(function(resultRow){
444+
var thisTranslation = _.find(columnTranslation.translations, function(option){
445+
return resultRow[columnTranslation.field].toString() === option.value.toString()
446+
});
447+
resultRow[columnTranslation.field] = thisTranslation.display;
448+
})
449+
});
450+
cb(null,null);
451+
}];
452+
453+
// if any of the column translations are refs, set up the tasks to look up the values and populate the translations
454+
for (var i=0; i < reportSchema.columnTranslations.length; i++) {
455+
var thisColumnTranslation = reportSchema.columnTranslations[i]
456+
, translateName = thisColumnTranslation.field;
457+
if (translateName){
458+
if (thisColumnTranslation.ref) {
459+
var lookup = self.getResource(thisColumnTranslation.ref);
460+
if (lookup) {
461+
if (toDo[translateName]) {
462+
return self.renderError(new Error("Cannot have two columnTranslations for field " + translateName ), null, req, res, next);
458463
} else {
459-
for (var j=0; j<findResults.length;j++){
460-
thisColumnTranslation.translations[j] = {value: findResults[j]._id, display: self.getListFields(lookup, findResults[j])};
461-
}
462-
cb(null,null);
464+
thisColumnTranslation.translations = thisColumnTranslation.translations || [];
465+
toDo[translateName] = function(cb,results) {lookup.model.find({},{},{lean:true},function(err,findResults){
466+
if (err) {
467+
cb(err);
468+
} else {
469+
for (var j=0; j<findResults.length;j++){
470+
thisColumnTranslation.translations[j] = {value: findResults[j]._id, display: self.getListFields(lookup, findResults[j])};
471+
}
472+
cb(null,null);
473+
}
474+
})};
475+
toDo.apply_translations.unshift(translateName); // Make sure we populate lookup before doing translation
463476
}
464-
})};
465-
toDo.apply_translations.unshift(translateName); // Make sure we populate lookup before doing translation
477+
} else {
478+
return self.renderError(new Error("Invalid ref property of " + thisColumnTranslation.ref + " in columnTranslations " + translateName ), null, req, res, next);
479+
}
480+
} else if (!thisColumnTranslation.translations) {
481+
return self.renderError(new Error("A column translation needs a ref or a translations property - " + translateName + " has neither" ), null, req, res, next);
466482
}
467483
} else {
468-
return self.renderError(new Error("Invalid ref property of " + thisColumnTranslation.ref + " in columnTranslations " + translateName ), null, req, res, next);
484+
return self.renderError(new Error("A column translation needs a field property" ), null, req, res, next);
469485
}
470-
} else if (!thisColumnTranslation.translations) {
471-
return self.renderError(new Error("A column translation needs a ref or a translations property - " + translateName + " has neither" ), null, req, res, next);
472486
}
473-
} else {
474-
return self.renderError(new Error("A column translation needs a field property" ), null, req, res, next);
475487
}
476-
}
477-
}
478488

479-
async.auto(toDo, function(err, results){
480-
if (err) {
481-
return self.renderError(err, null, req, res, next);
482-
} else {
483-
// TODO: Could loop through schemaCopy.params and just send back the values
484-
res.send({success:true, schema:reportSchema, report: results.runAggregation, paramsUsed: schemaCopy.params});
489+
async.auto(toDo, function(err, results){
490+
if (err) {
491+
return self.renderError(err, null, req, res, next);
492+
} else {
493+
// TODO: Could loop through schemaCopy.params and just send back the values
494+
res.send({success:true, schema:reportSchema, report: results.runAggregation, paramsUsed: schemaCopy.params});
495+
}
496+
});
485497
}
486498
});
487499
}, this);
@@ -566,8 +578,18 @@ DataForm.prototype.collectionGet = function () {
566578
}, this);
567579
};
568580

581+
DataForm.prototype.doFindFunc = function(req, resource, cb) {
582+
if (resource.options.findFunc) {
583+
resource.options.findFunc(req, cb)
584+
} else {
585+
cb(null);
586+
}
587+
};
588+
569589
DataForm.prototype.filteredFind = function (resource, req, aggregationParam, findParam, sortOrder, limit, skip, callback) {
570-
var hidden_fields = this.generateHiddenFields(resource, false);
590+
591+
var that = this
592+
, hidden_fields = this.generateHiddenFields(resource, false);
571593

572594
function doAggregation(cb) {
573595
if (aggregationParam) {
@@ -585,33 +607,24 @@ DataForm.prototype.filteredFind = function (resource, req, aggregationParam, fin
585607
}
586608
}
587609

588-
function doFindFunc(cb) {
589-
if (resource.options.findFunc) {
590-
resource.options.findFunc(req, function (err, query) {
591-
if (err) {
592-
throw err;
593-
} else {
594-
cb(query);
595-
}
596-
})
597-
} else {
598-
cb(resource.model.find({}));
599-
}
600-
}
601-
602610
doAggregation(function (idArray) {
603611
if (aggregationParam && idArray.length === 0) {
604612
callback(null, [])
605613
} else {
606-
doFindFunc(function (query) {
607-
if (idArray.length > 0) {
608-
query = query.where('_id').in(idArray)
614+
that.doFindFunc(req, resource, function (err, queryObj) {
615+
if (err) {
616+
callback(err)
617+
} else {
618+
var query = resource.model.find(queryObj);
619+
if (idArray.length > 0) {
620+
query = query.where('_id').in(idArray)
621+
}
622+
query = query.find(findParam).select(hidden_fields);
623+
if (limit) query = query.limit(limit);
624+
if (skip) query = query.skip(skip);
625+
if (sortOrder) query = query.sort(sortOrder);
626+
query.exec(callback);
609627
}
610-
query = query.find(findParam).select(hidden_fields);
611-
if (limit) query = query.limit(limit);
612-
if (skip) query = query.skip(skip);
613-
if (sortOrder) query = query.sort(sortOrder);
614-
query.exec(callback);
615628
})
616629
}
617630
})
@@ -729,7 +742,7 @@ DataForm.prototype.replaceHiddenFields = function (record, data) {
729742
}
730743
});
731744
delete self._replacingHiddenFields;
732-
}
745+
};
733746

734747
DataForm.prototype.entityPut = function () {
735748
return _.bind(function (req, res, next) {
@@ -757,8 +770,6 @@ DataForm.prototype.entityPut = function () {
757770
} else {
758771
that.saveAndRespond(req, res);
759772
}
760-
;
761-
762773
}, this);
763774
};
764775

server/models/b_using_options.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,28 @@ BSchema.statics.form = function(layout) {
8080

8181
BSchema.statics.findAccepted = function(req,cb) {
8282
// Only show the accepted items
83-
cb(null, B.find().where('accepted', true));
83+
cb(null, {accepted:true});
8484
};
8585

8686
BSchema.statics.prepareSave = function(doc, req, cb) {
8787
doc.ipAddress = req.ip;
8888
cb(null);
8989
};
9090

91+
BSchema.statics.report = function(report) {
92+
var reportSchema = '';
93+
switch (report) {
94+
case 'allVisible' :
95+
reportSchema = {
96+
pipeline: [{$group:{_id:"$accepted",count:{"$sum":1}}}],
97+
title: "Numbers of Applicants By Status"
98+
};
99+
break;
100+
}
101+
return reportSchema;
102+
};
103+
104+
91105
module.exports = {
92106
model : B // pass the model in an object if you want to add options
93107
, findFunc: BSchema.statics.findAccepted // this can be used to 'pre' filter selections.

test/api/report-api.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,14 @@ describe('Report API', function () {
7777
});
7878
});
7979

80+
it('honours findfunc',function(done) {
81+
exec('curl 0.0.0.0:3001/api/report/b_using_options/allVisible', function (error, stdout) {
82+
var data = JSON.parse(stdout).report;
83+
assert.equal(data.length, 1);
84+
assert.deepEqual(data[0],{_id:true,count:1});
85+
done();
86+
});
87+
})
88+
8089
});
8190

0 commit comments

Comments
 (0)