It's a meteor service to generate reports in PDF format by the provided data. Service returns link to generated report.
Programs installation (Ubuntu):
- apt update -y -q
Service receive data in JSON format. Received data validate by SimpleSchema which custom for each report. On validated data generates report in PDF format. Report dumps on storage. The link is returned to the user.
- Add
simpleSchemafor new report (dataSchema+filterSchemaif necessary) indefinitionsfolder.
Example (new-report-schema.js):
import SimpleSchema from 'simpl-schema';
export const filterSchema = new SimpleSchema({
'member': String,
'jobTitle': String,
});
export const dataSchema = new SimpleSchema({
'modules': {
type: Array,
optional: true,
},
'policies': {
type: Array,
optional: true,
},
});
- Add definition for new report which extends
ReportDocDefinitionclass.
Example (new-report.js):
import { ReportDocDefinition } from '../definition';
import { dataSchema, filterSchema } from './new-report-schema';
export class newReportDefinition extends ReportDocDefinition {
constructor(params) {
super(params, {
dataSchema,
filterSchema,
});
}
}
- Export definition to
definitions/index.js.
Example (index.js):
...
export * from './new-report';
...
...
- Add mapping route to
report-routes.js.
Example (report-routes.js):
...
const mappings = {
...
'newReport': defs.newReportDefinition,
...
}
...
- Add data to
test-datafolder in JSON format.
Example (new-report-definition.json):
{
"modules": [
{
"name": "Aseptic technique",
"completedOn": {
"$date": "2020-08-21T08:58:08.601Z"
},
"cpd": 120
},
{
"name": "Basic Wound Management",
"completedOn": {
"$date": "2020-08-29T18:30:00.000Z"
},
"cpd": 60
}
],
"policies": [
{
"name": "Emergency Contact Details",
"completedOn": {
"$date": "2021-03-04T01:17:28.956Z"
}
},
{
"name": "Employee Dependents Form",
"completedOn": {
"$date": "2021-03-09T01:45:38.792Z"
}
}
],
}
- Create test, which generate
newReportand dump it (if necessary).
Example (methods.tests.js):
// Use this beforeEach code block to prevent fs operations
beforeEach(function () {
sinon.stub(fs, 'createWriteStream').returns({
write: sinon.fake(),
once: sinon.fake(),
end: sinon.fake(),
on: sinon.fake(),
emit: sinon.fake(),
path: '',
});
sinon.replace(ReportDocDefinition, 'saveFile', sinon.fake());
sinon.replace(ImageFetcher, 'download', sinon.fake.returns({ text: '' }));
});
...
it('dump newReport', function () {
const filter = {
member: 'Member string',
jobTitle: 'JobTitle string',
};
const params = {
...EJSON.fromJSONValue(require('/test-data/common-data-definition.json')),
report: 'newReport',
filter,
data: EJSON.fromJSONValue(require('/test-data/new-report-definition.json')),
};
assert.match(Meteor.call('HSE.Reporting.generate', params), /^http/);
});
Example (new-report.js):
...
// Override ReportDocDefinition method to generate content
reportContent() {
return [
'Content string',
];
}
...
Example (new-report.js):
...
// Override ReportDocDefinition method to generate title
reportTitle() {
return 'Report Title';
}
// Override ReportDocDefinition method to generate subtitle
reportSubTitle() {
const from = new Date();
const till = new Date();
return this.formatDateRange(from, till); // ReportDocDefinition method to generate date range
}
...
Example (new-report.js):
...
filterSection(){
const {member, jobTitle} = this.filter;
// ReportDocDefinition method to generate filter section
return super.filterSection(
[ // 1-st row
{ // 1-st column
label: 'Member',
value: member,
width: '50%',
},
],
[ // 2-nd row
{ // 1-st column
label: 'Job Title',
value: jobTitle,
width: '*',
},
{ // 2-nd column
label: 'Test label',
value: 'Test value',
width: 150,
},
],
];
);
}
reportContent() {
return [
this.filterSection(),
];
}
...
For additional use cases look for examples in definition.spec.js
Example (new-report.js):
tableSection () {
const view = [
{ // Column 1
heading: 'Name', // table header
render: (item) => item.name ?? '\u2013', // table cell's render callback
},
{ // Column 2
heading: 'CPD',
render: (item) => item.cpd ?? '',
},
];
const data = [
{
'name': 'Name 1',
'cpd': 60,
},
{
'name': 'Name 2',
'cpd': 90,
},
{
'name': 'Name 3',
'cpd': 120,
},
];
// ReportDocDefinition method to generate tables
return super.tableSection(view, data);
}
reportContent() {
return [
this.tableSection();
];
}
For additional use cases look for examples in definition.spec.js
Example (new-report.js):
renderChart() {
const chartData = {
'type': 'doughnut',
'height': 200,
'data': {
'labels': [
'Not specified',
'Aboriginal and Torres Str...',
'Registered Nurse',
'Administration – Clinical',
'Aboriginal health worker',
'Administration/Clerical –...',
'Enrolled Nurse',
'Manager – Administration',
'Other',
],
'datasets': [
{
'data': [
19,
10,
6,
4,
3,
3,
3,
3,
37,
],
'backgroundColor': [
'#020E87',
'#387BE4',
'#A9060F',
'#CF4D38',
'#E6AC27',
'#669A45',
'#138887',
'#622F8C',
'#B661E4',
'#E560B6',
],
},
],
},
'options': {
'title': {
'display': true,
'text': 'Members by Job Title',
},
'legend': {
'position': 'right',
'align': 'left',
},
},
};
const width = 1800;
const height = 600;
const chartOptions = {
title: {
fontSize: 40,
padding: 20,
},
legend: {
labels: {
fontSize: 25,
boxWidth: 60,
padding: 10,
},
},
};
// ReportDocDefinition method to generate tables
return super.renderChart(chartData, width, height, chartOptions);
}
reportContent() {
return [
this.renderChart();
];
}
For additional use cases look for examples in definition.spec.js
Example (new-report.js):
...
reportContent() {
{ user, data, organisation, team, branding, filter, strings } = this;
return ...
}
...