@measurequick/measurequick-report-generator 1.0.52 → 1.0.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,459 @@
1
+ import * as graphics from "../templates/js/graphics.js";
2
+ import * as templates from "../templates/js/templates.js";
3
+ import * as request from "../templates/js/request.js";
4
+
5
+ let jsPDF = require("jspdf");
6
+ let doc = new jsPDF(templates.docOptions);
7
+ let reportData;
8
+ let currentSnapshot;
9
+
10
+ export function generateReport(reportType, payload) {
11
+ console.log("reportType " + reportType);
12
+ console.log(payload);
13
+ try {
14
+ reportData = JSON.parse(JSON.stringify(payload));
15
+ let testNumber = 0;
16
+ for (currentSnapshot in reportData.snapshots) {
17
+ if (testNumber > 0) doc.addPage();
18
+ printBoilerPlateStandard();
19
+ printHeaderData();
20
+ printBodyData(currentSnapshot);
21
+ printFooterData();
22
+ resizeAndPrintStaticImages();
23
+ testNumber++;
24
+ }
25
+ printDiagnosticPage();
26
+ printInfoPage();
27
+ printPhotoPages();
28
+ return doc.output("datauristring");
29
+ } catch (e) {
30
+ return doc.output("datauristring");
31
+ }
32
+ }
33
+
34
+ function printBoilerPlateStandard() {
35
+ doc.addImage(graphics.headerMeasureQuick, "JPEG", 0, 0, 8.5, 1);
36
+ doc.addImage(graphics.footerMeasureQuick, "JPEG", 0, 7.75, 8.5, 4.5);
37
+ let mqLogo = graphics.companyLogoMeasureQuickLight;
38
+ if (reportData.meta.reportTheme == "light")
39
+ mqLogo = graphics.companyLogoMeasureQuickDark;
40
+ doc.addImage(mqLogo, "JPEG", .25, 10, 2.15625, .5);
41
+ doc.setLineWidth(0.025);
42
+ doc.setDrawColor(0);
43
+ doc.setFillColor(242, 242, 242);
44
+ doc.rect(.125, 1.125, 2.667, 3, "F");
45
+ doc.rect(2.917, 1.125, 2.667, 3, "F");
46
+ doc.rect(5.709, 1.125, 2.667, 3, "F");
47
+ doc.rect(.125, 4.25, 4.0625, 3.375, "F");
48
+ let bgIcons = templates[`${reportData.meta.reportType}BgIcons`];
49
+ doc.addImage(graphics[bgIcons.iconSection1], "JPEG", .4585, 1.625, 2, 2);
50
+ doc.addImage(graphics[bgIcons.iconSection2], "JPEG", 3.252, 1.625, 2, 2);
51
+ doc.addImage(graphics[bgIcons.iconSection3], "JPEG", 6.044, 1.625, 2, 2);
52
+ doc.addImage(graphics[bgIcons.iconSection4], "JPEG", 1.15625, 4.9375, 2, 2);
53
+ doc.setFontStyle("bold");
54
+ doc.setFontSize(9);
55
+ let headers = templates[`${reportData.meta.reportType}Headers`];
56
+ doc.text(headers.headerSection1, .25, 1.33);
57
+ doc.text(headers.headerSection2, 3.042, 1.33);
58
+ doc.text(headers.headerSection3, 5.834, 1.33);
59
+ doc.text(headers.headerSection4, .25, 4.435);
60
+ doc.setFontSize(8);
61
+ doc.text(headers.headerSection4Subsection1, .25, 4.655);
62
+ doc.text(headers.headerSection4Subsection2, 2.25, 4.655);
63
+ doc.text(headers.headerSection4Subsection3, 2.25, 5.655);
64
+ doc.setFontSize(6.5);
65
+ doc.setFontStyle("bolditalic");
66
+ doc.setTextColor(255, 255, 255);
67
+ let disclaimer = `Disclaimer: This report was prepared by your service technician who is solely responsible for its content. This report is provided “as-is” excluding all warranties expressed or implied including without limitation the warranty of merchantability. ©2017-${new Date().getFullYear()} Manifold Cloud Services Ltd.`;
68
+ disclaimer = doc.splitTextToSize(disclaimer.toUpperCase(), 8);
69
+ doc.text(disclaimer, .125, 10.66);
70
+ }
71
+
72
+ function printBoilerPlateDiagnostic() {
73
+ doc.addImage(graphics.headerMeasureQuick, "JPEG", 0, 0, 8.5, 1);
74
+ doc.setLineWidth(0.025);
75
+ doc.setDrawColor(0);
76
+ doc.setFillColor(242, 242, 242);
77
+ doc.setTextColor(0, 0, 0);
78
+ doc.setFontSize(9);
79
+ doc.setFontStyle('bold');
80
+ doc.rect(.125, 3.3, 8.25, 7.6, "F");
81
+ doc.rect(.125, .6, 4.0625, 2.6, "F");
82
+ doc.rect(4.3125, .6, 4.0625, 2.6, "F");
83
+ doc.text("System Diagnostics", 4.4375, .8);
84
+ doc.text("Subsystem Review", .25, .8);
85
+ doc.text('Corrective Actions', .25, 3.5);
86
+ doc.setFontSize(8);
87
+ doc.setFontStyle('normal');
88
+
89
+ }
90
+
91
+ function printHeaderData() {
92
+ let size = 13.5;
93
+ do { size -= .5; } while ((doc.getStringUnitWidth(reportData.meta.reportName) * size / 72) > 4.5);
94
+ doc.setFontSize(size);
95
+ doc.setFontStyle("bold");
96
+ doc.text(reportData.meta.reportName, 0.25, .75);
97
+ }
98
+
99
+ function printBodyData(currentSnapshot) {
100
+ doc.setTextColor(0, 0, 0);
101
+ doc.setFontType("normal");
102
+ doc.setFontSize(8);
103
+ if (reportData.meta.reportType == "mqCooling") {
104
+ printBodySection(currentSnapshot, "outdoorMeasurements", .25, 2.317, 1.55);
105
+ printBodySection(currentSnapshot, "indoorMeasurements", 3.042, 5.109, 1.55);
106
+ printBodySection(currentSnapshot, "systemProfileWeatherData", 5.834, 7.9, 1.55);
107
+ printBodySection(currentSnapshot, "performanceData", .25, 2, 4.855);
108
+ } else if (reportData.meta.reportType == "mqHeating") {
109
+ printBodySection(currentSnapshot, "combustion", .25, 2.317, 1.55);
110
+ printBodySection(currentSnapshot, "electricalMeasurements", 3.042, 5.109, 1.55);
111
+ printBodySection(currentSnapshot, "systemProfileWeatherDataGas", 5.834, 7.9, 1.55);
112
+ printBodySection(currentSnapshot, "performanceMetrics", .25, 2, 4.855);
113
+ }
114
+ printGeolocationMap();
115
+ }
116
+
117
+ function printBodySection(currentSnapshot, measurementSection, xLeft, xRight, y) {
118
+ console.log("David: " + measurementSection + ": " + JSON.stringify(reportData.snapshots[currentSnapshot]));
119
+ let dataPoints = JSON.parse(reportData.snapshots[currentSnapshot][measurementSection]);
120
+ let printRangeIcons = true;
121
+ let units = "";
122
+ let labelText = "";
123
+ let valueText = "";
124
+ let yTop = y;
125
+ let s = .18;
126
+ let sMultiplier = measurementSection == "performanceData" ? 2 : 1;
127
+
128
+ for (let key in dataPoints.values) {
129
+ let dataPoint = dataPoints.values[key];
130
+ let valueBeforeEditing = dataPoint;
131
+ let templateRef = templates[measurementSection][key];
132
+ if (templateRef) {
133
+ if (templateRef.hasOwnProperty("units"))
134
+ units = Array.isArray(templateRef.units) ? `(${templateRef.units[0]} / ${templateRef.units[1]})` : `(${templateRef.units})`;
135
+ else units = "";
136
+ labelText = `${templateRef.label} ${units}:`;
137
+ if (templateRef.hasOwnProperty("roundTo")) {
138
+ dataPoint *= 1;
139
+ dataPoint = dataPoint.toFixed(templateRef.roundTo);
140
+ }
141
+ if (templateRef.hasOwnProperty("secondaryValue")) {
142
+ let secondaryDataPoint = dataPoints.values[templateRef.secondaryValue];
143
+ if (templateRef.hasOwnProperty("roundTo")) {
144
+ secondaryDataPoint *= 1;
145
+ secondaryDataPoint = secondaryDataPoint.toFixed(templateRef.roundTo);
146
+ }
147
+ valueText = `${dataPoint} / ${secondaryDataPoint}`;
148
+ } else valueText = `${dataPoint}`;
149
+ if (templateRef.label) doc.text(labelText, xLeft, y);
150
+ doc.text(valueText, alignRight(valueText, xRight, 8), y);
151
+ if (printRangeIcons) {
152
+ let rangeIconRef = getRangeIcon(key, valueBeforeEditing, currentSnapshot);
153
+ if (rangeIconRef) doc.addImage(graphics[rangeIconRef], "JPEG", xRight + .05, y - .12, .16, .16);
154
+ }
155
+ let toolIconRef = getToolIcon(reportData.snapshots[currentSnapshot].toolIcons[key]);
156
+ if (toolIconRef) doc.addImage(graphics[toolIconRef], "JPEG", xRight + .25, y - .12, .16, .16);
157
+ y += (s * sMultiplier);
158
+ } else if (key == "newColumn") {
159
+ sMultiplier = 1;
160
+ xLeft = 2.25;
161
+ xRight = 3.9;
162
+ y = yTop;
163
+ } else if (key == "newRow") {
164
+ y += .35;
165
+ }
166
+ }
167
+ }
168
+
169
+ function printGeolocationMap() {
170
+ doc.addImage(reportData.staticPhotos.geolocationMap, "PNG", 4.3125, 4.25, 4.0625, 3.375);
171
+ }
172
+
173
+ function printProfilePicture() {
174
+ let coords = templates.profilePictureCoordinates[reportData.meta.profilePicturePlacement];
175
+ doc.addImage(reportData.staticPhotos.profilePicture, "JPEG", coords.x, coords.y, coords.w, coords.h);
176
+ doc.text(reportData.meta.techName, alignRight(reportData.meta.techName, 8.4, 8), coords.y + 1.2);
177
+ }
178
+
179
+ function printCompanyLogo() {
180
+ let coords = templates.companyLogoCoordinates[reportData.meta.companyLogoPlacement];
181
+ doc.addImage(reportData.staticPhotos.companyLogo, "JPEG", coords.x, coords.y, coords.w, coords.h);
182
+ }
183
+
184
+ function printFooterData() {
185
+ let s = .15;
186
+ let x = .2;
187
+ let y = 8;
188
+ let equipmentLabels = ["Make", "Model", "Serial"];
189
+ let i = -1;
190
+ doc.setTextColor(255, 255, 255);
191
+ doc.setFontType("bold");
192
+ doc.setFontSize(12);
193
+ doc.text("Customer", x, y);
194
+ doc.text("Equipment", x + 2.5, y);
195
+ doc.setFontSize(8);
196
+ y += .2;
197
+ doc.setFontSize(10);
198
+ y += .05;
199
+ let footer = JSON.parse(reportData.footerData);
200
+ for (let key in footer) {
201
+ doc.setFontType("normal");
202
+ if (key.includes("header")) {
203
+ i = 0;
204
+ doc.setFontStyle("bold");
205
+ y += .1;
206
+ } else if (key == "systemCoords") {
207
+ s = .12;
208
+ x += 2.5;
209
+ y = 8.2;
210
+ doc.setFontStyle("bold");
211
+ doc.setFontSize(8);
212
+ doc.text("ID", x, y);
213
+ doc.setFontStyle("normal");
214
+ y += s;
215
+ }
216
+ let prefix = "";
217
+ if (i > -1) {
218
+ prefix = `${equipmentLabels[i]}: `;
219
+ i++;
220
+ }
221
+ doc.text(`${prefix}${footer[key]}`, x, y);
222
+ y += s;
223
+ }
224
+ }
225
+
226
+ function printDiagnosticPage() {
227
+ doc.addPage();
228
+ printBoilerPlateDiagnostic();
229
+ printSubsystemReview();
230
+ printSystemDiagnostics();
231
+ printCorrectiveActions();
232
+ }
233
+
234
+ function printSubsystemReview() {
235
+ let y = 1;
236
+ let templateRef = templates[`${reportData.meta.reportType}SubsystemReview`];
237
+ console.log("templateRef is " + templateRef);
238
+ let ssr = JSON.parse(reportData.subsystemReview);
239
+ for (let i = 0; i < Object.keys(ssr).length; i++) {
240
+ let key = Object.keys(ssr)[i];
241
+ console.log("key is " + key);
242
+ doc.text(templateRef[key], .25, y);
243
+ doc.text(ssr[key], 3, y);
244
+ y += .15;
245
+ };
246
+ }
247
+
248
+ function printSystemDiagnostics() {
249
+ let y = 1;
250
+ console.log("DIAG: " + reportData.systemDiagnostics);
251
+ let diagnostics = JSON.parse(reportData.systemDiagnostics);
252
+ for (let i = 0; i < Object.keys(diagnostics).length; i++) {
253
+ let key = Object.keys(diagnostics)[i];
254
+ let score = diagnostics[key].score;
255
+ let diagnosticIcon = graphics.iconFlagGreen;
256
+ if (type === 'stability') icon = stability;
257
+ else if (score > 1 && score < 10) diagnosticIcon = graphics.iconFlagRed;
258
+ else if (score >= 10 && score < 15) diagnosticIcon = graphics.iconFlagYellow;
259
+ else if (score >= 15) diagnosticIcon = graphics.iconFlagBlack;
260
+ if (diagnosticIcon) doc.addImage(graphics[diagnosticIcon], "JPEG", 7.8, y, .25, .25);
261
+ y += .2;
262
+ doc.text(diagnostics[key].title, 4.4375, y);
263
+ doc.text(`${diagnostics[key].score}`, 7.5, y);
264
+ }
265
+ }
266
+
267
+ function printCorrectiveActions() {
268
+ let y = 3.8;
269
+ doc.setFontSize(9);
270
+ let ca = JSON.parse(reportData.correctiveActions);
271
+ for (let i = 0; i < Object.keys(ca).length; i++) {
272
+ let sectionKey = Object.keys(ca)[i];
273
+ for (let j = 0; j < Object.keys(ca[sectionKey]).length; j++) {
274
+ let actionKey = Object.keys(ca[sectionKey])[j];
275
+ let action = ca[sectionKey][actionKey];
276
+ if (actionKey == "header") {
277
+ doc.setFontSize(10);
278
+ doc.setFontStyle("bold");
279
+ doc.text(action, .25, y);
280
+ } else {
281
+ doc.setFontSize(8);
282
+ doc.setFontStyle("normal");
283
+ doc.text(actionKey, .25, y);
284
+ }
285
+ y += .18;
286
+ }
287
+ y += .1;
288
+ }
289
+ }
290
+
291
+ function printPhotoPages() {
292
+ let photoSectionMapping = ["project", "equipment", "site", "customer"];
293
+ let x, y, header, currentQuadrant, currentSection;
294
+ for (let i = 0; i < 4; i++) { // loops through the 4 main sections of photos, listed above (photoSectionMapping)
295
+ currentSection = reportData[`${photoSectionMapping[i]}Photos`];
296
+ if (Object.keys(currentSection).length > 0) doc.addPage();
297
+ else break;
298
+ currentQuadrant = 1;
299
+ x = templates.photoCoordinates[`quadrant${currentQuadrant}`].x;
300
+ y = templates.photoCoordinates[`quadrant${currentQuadrant}`].y;
301
+ doc.setFontStyle("bold");
302
+ doc.text(`${photoSectionMapping[i][0].toUpperCase()}${photoSectionMapping[i].slice(1)} Photos`, x, y - .2);
303
+ doc.setFontStyle("normal");
304
+ for (let key in currentSection) { // loops through the "subsections" of each main photo section
305
+ if (currentSection[key].photos) {
306
+ for (let j = 0; j < currentSection[key].photos.length; j++) { // loops through the photos[] array within each subsection, of each main photo section
307
+ let currentPhoto = currentSection[key].photos[j];
308
+ if (currentQuadrant === 3) {
309
+ doc.setLineWidth(0.025);
310
+ doc.setDrawColor(194, 197, 204);
311
+ doc.line(.25, 5.5, 8.25, 5.5);
312
+ } else if (currentQuadrant === 5) {
313
+ doc.addPage();
314
+ currentQuadrant = 1;
315
+ }
316
+ x = templates.photoCoordinates[`quadrant${currentQuadrant}`].x;
317
+ y = templates.photoCoordinates[`quadrant${currentQuadrant}`].y;
318
+ if (currentPhoto.base64) {
319
+ doc.text(getPhotoSectionNameFromTag(currentPhoto.tag), x, y);
320
+ let wPicMax = 3.75,
321
+ hPicMax = 4,
322
+ wPic = wPicMax,
323
+ hPic = hPicMax,
324
+ r = currentPhoto.aspect_ratio * 1;
325
+ if (r >= 1) { // logo is a landscape image
326
+ wPic = wPicMax;
327
+ hPic = wPic / r;
328
+ if (hPic > hPicMax) { // calculated h is too large for the available space
329
+ hPic = hPicMax;
330
+ wPic = hPic * r;
331
+ }
332
+ } else { // logo is a portrait image
333
+ hPic = hPicMax;
334
+ wPic = hPic * r;
335
+ if (wPic > wPicMax) { // calculated w is too large for the available space
336
+ wPic = wPicMax;
337
+ hPic = wPic / r;
338
+ }
339
+ }
340
+ doc.addImage(currentPhoto.base64, "JPEG", x, y + .1, wPic, hPic);
341
+ }
342
+ currentQuadrant++;
343
+ }
344
+ // if (currentSection[key].hasNotes && currentSection[key].notes != "") {
345
+ // let notes = doc.splitTextToSize(`${currentSection[key].notes}`, 7.5);
346
+ // doc.setFontStyle("bold");
347
+ // doc.text("Notes:", x, y + hPic + .3);
348
+ // doc.setFontStyle("normal");
349
+ // doc.text(notes, x, y + hPic + .4);
350
+ // }
351
+ }
352
+ }
353
+ }
354
+ }
355
+
356
+ function printInfoPage() {
357
+ let infoPage = templates[`${reportData.meta.reportType}InfoPage`];
358
+ doc.addPage();
359
+ doc.setTextColor(0, 0, 0);
360
+ doc.setFontStyle("bold");
361
+ doc.setFontSize(14);
362
+ doc.text(infoPage.header, .5, .6);
363
+ doc.setFontSize(12);
364
+ for (let i = 0; i < infoPage.subheader.length; i++) {
365
+ let y = infoPage.y[i];
366
+ if (y == .6) doc.addPage();
367
+ doc.setFontStyle("bold");
368
+ doc.text(infoPage.subheader[i], .5, y);
369
+ y += .2;
370
+ doc.setFontStyle("normal");
371
+ doc.text(doc.splitTextToSize(infoPage.body[i], 7.5), .5, y);
372
+ }
373
+ }
374
+
375
+ function getPhotoSectionNameFromTag(tag) {
376
+ let tagPrefixRemoved = tag.substr(tag.indexOf('_'));
377
+ let tagUnderscoresReplaced = tagPrefixRemoved.replaceAll('_', ' ');
378
+ let sectionName = `${tagUnderscoresReplaced.charAt(0).toUpperCase()}${tagUnderscoresReplaced.slice(1)}`;
379
+ return sectionName;
380
+ }
381
+
382
+ function resizeAndPrintStaticImages() {
383
+ let wMax = 2.25,
384
+ hMax = .7,
385
+ x_profile = 6.9,
386
+ y_profile = 8.13,
387
+ size_profile = 1.25,
388
+ x_name = 8.15,
389
+ y_name = 8.025,
390
+ r = reportData.meta.companyLogoAspectRatio,
391
+ xOffset = 0,
392
+ yOffset = 0,
393
+ w = 0,
394
+ h = 0,
395
+ x = 6,
396
+ y = .2;
397
+ if (reportData.meta.companyLogoPlacement !== "header") {
398
+ x = 6;
399
+ y = 8;
400
+ x_profile = 7.375;
401
+ y_profile = .15;
402
+ size_profile = reportData.meta.reportTheme === 'dark' ? .8 : .9;
403
+ x_name = 7.25;
404
+ y_name = .5;
405
+ wMax = 2.25;
406
+ hMax = .8
407
+ }
408
+ if (r >= 1) {
409
+ w = wMax;
410
+ h = w / r;
411
+ if (h > hMax) {
412
+ h = hMax;
413
+ w = h * r;
414
+ }
415
+ } else {
416
+ h = hMax;
417
+ w = h * r;
418
+ if (w > wMax) {
419
+ w = wMax;
420
+ h = w / r;
421
+ }
422
+ }
423
+ if (w < wMax) x = 8.5 - .25 - w;
424
+ doc.setFontSize(12);
425
+ doc.setFontType("bold");
426
+ doc.setTextColor(255, 255, 255);
427
+ if (reportData.meta.companyLogoPlacement == "Header" && h < hMax)
428
+ y = y + ((hMax - h) / 2);
429
+ doc.addImage(reportData.staticPhotos.companyLogo, "JPEG", x, y, w, h);
430
+ doc.addImage(reportData.staticPhotos.profilePicture, "JPEG", x_profile, y_profile, size_profile, size_profile);
431
+ }
432
+
433
+ function getToolIcon(measurementSource) {
434
+ switch (measurementSource) {
435
+ case "Fieldpiece": return "companyLogoFieldpiece";
436
+ default: return null;
437
+ }
438
+ }
439
+
440
+ function getRangeIcon(key, actualValue, currentSnapshot) {
441
+ let ranges = JSON.parse(reportData.snapshots[currentSnapshot].rangeIcons);
442
+ let target = null, low = null, high = null;
443
+ let rangeIconRef = null;
444
+ if (ranges[key]) {
445
+ target = parseFloat(ranges[key]);
446
+ if (ranges[`${key}_ideal_low`]) low = target - parseFloat(ranges[`${key}_ideal_low`]);
447
+ if (ranges[`${key}_ideal_high`]) high = target + parseFloat(ranges[`${key}_ideal_high`]);
448
+ if (target && low && high) {
449
+ if (actualValue < low) rangeIconRef = "iconRangeLow";
450
+ else if (actualValue > high) rangeIconRef = "iconRangeHigh";
451
+ else rangeIconRef = "iconRangeNormal";
452
+ }
453
+ }
454
+ return rangeIconRef;
455
+ }
456
+
457
+ function alignRight(text, rightAlign, fontSize) {
458
+ return rightAlign - doc.getStringUnitWidth(text) * fontSize / 72;
459
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@measurequick/measurequick-report-generator",
3
- "version": "1.0.52",
3
+ "version": "1.0.53",
4
4
  "description": "Generates PDF documents for various MeasureQuick applications.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,165 @@
1
+ import { PDFDocument } from 'pdf-lib';
2
+
3
+ export async function generateReport(payload) {
4
+ // fetch and load form
5
+ const formPdfBytes = await fetch("../../assets/pdf_templates/MQ-system-vitals-report.pdf").then(res => res.arrayBuffer());
6
+ const pdfDoc = await PDFDocument.load(formPdfBytes);
7
+
8
+ // fetch resources
9
+ const iconRangeGreenBytes = await fetch(`../../assets/img/icon-range-green.png`).then(res => res.arrayBuffer());
10
+ const iconRangeRedBytes = await fetch(`../../assets/img/icon-range-red.png`).then(res => res.arrayBuffer());
11
+ const iconFlagBlackBytes = await fetch(`../../assets/img/icon-flag-black.png`).then(res => res.arrayBuffer());
12
+ const iconFlagGreenBytes = await fetch(`../../assets/img/icon-flag-green.png`).then(res => res.arrayBuffer());
13
+ const iconFlagRedBytes = await fetch(`../../assets/img/icon-flag-red.png`).then(res => res.arrayBuffer());
14
+ const iconFlagYellowBytes = await fetch(`../../assets/img/icon-flag-yellow.png`).then(res => res.arrayBuffer());
15
+ const iconStabilityBytes = await fetch(`../../assets/img/icon-stability.png`).then(res => res.arrayBuffer());
16
+
17
+ // load resources
18
+ const iconRangeGreen = await pdfDoc.embedPng(iconRangeGreenBytes);
19
+ const iconRangeRed = await pdfDoc.embedPng(iconRangeRedBytes);
20
+ const iconFlagBlack = await pdfDoc.embedPng(iconFlagBlackBytes);
21
+ const iconFlagGreen = await pdfDoc.embedPng(iconFlagGreenBytes);
22
+ const iconFlagRed = await pdfDoc.embedPng(iconFlagRedBytes);
23
+ const iconFlagYellow = await pdfDoc.embedPng(iconFlagYellowBytes);
24
+ const iconStability = await pdfDoc.embedPng(iconStabilityBytes);
25
+
26
+ // get form for filling
27
+ const form = pdfDoc.getForm();
28
+
29
+ // print system diagnostics section
30
+ let scoreDeduction = 0;
31
+ for (let i = 0; i < 9; i++) {
32
+ let d = payload.test.diagnostics.multi_point_analysis[i];
33
+ if (d) {
34
+ let diagnosticIcon = iconFlagBlack;
35
+ if (d.type === 'stability')
36
+ diagnosticIcon = iconStability;
37
+ else if (d.score > 1 && d.score < 10) {
38
+ diagnosticIcon = iconFlagRed;
39
+ scoreDeduction += d.score;
40
+ } else if (d.score >= 10 && d.score < 15)
41
+ diagnosticIcon = iconFlagYellow;
42
+ form.getButton(`ImageDiagnostics${i+1}_af_image`).setImage(diagnosticIcon);
43
+ form.getTextField(`SystemDiagnostics${i+1}`).setText(`${d.title}`);
44
+ form.getTextField(`SystemDiagnostics${i+1}a`).setText(`${d.score}`);
45
+ }
46
+ }
47
+
48
+ let systemScorePercentage = 100 - scoreDeduction;
49
+ let systemScoreGrade = getGradeFromScore(systemScorePercentage);
50
+ let systemScoreColor = getColorFromGrade(systemScoreGrade);
51
+
52
+ // get text fields
53
+ let textFields = getTextFields(payload);
54
+
55
+ // print customer data
56
+ let dateTimePlacement = "Upper";
57
+ let e = "";
58
+ let d = "";
59
+
60
+ let date = new Date();
61
+ if (textFields.cName && textFields.address && textFields.city && textFields.state && textFields.zip) {
62
+ dateTimePlacement = "Lower";
63
+ form.getTextField('Name').setText(`${textFields.cName}`);
64
+ form.getTextField('Address1').setText(`${textFields.address}`);
65
+ if (textFields.address2) {
66
+ form.getTextField('Address2').setText(`${textFields.address2}`);
67
+ form.getTextField('CityStateZip').setText(`${textFields.city}${textFields.state}${textFields.zip}`);
68
+ } else form.getTextField('Address2').setText(`${textFields.city}${textFields.state}${textFields.zip}`);
69
+ form.getTextField('DateOfServiceLabelUpper').setText("");
70
+ form.getTextField('TimeOfServiceLabelUpper').setText("");
71
+ form.getTextField('DateOfServiceLabelLower').setText("Date of Service:");
72
+ form.getTextField('TimeOfServiceLabelLower').setText("Time of Service:");
73
+ form.getTextField('DateOfServiceLower').setText(date.toLocaleDateString("en-US"));
74
+ form.getTextField('TimeOfServiceLower').setText(date.toLocaleTimeString("en-US"));
75
+ } else {
76
+ form.getTextField('DateOfServiceLabelLower').setText("");
77
+ form.getTextField('TimeOfServiceLabelLower').setText("");
78
+ form.getTextField('DateOfServiceLabelUpper').setText("Date of Service:");
79
+ form.getTextField('TimeOfServiceLabelUpper').setText("Time of Service:");
80
+ form.getTextField('DateOfServiceUpper').setText(date.toLocaleDateString("en-US"));
81
+ form.getTextField('TimeOfServiceUpper').setText(date.toLocaleTimeString("en-US"));
82
+ }
83
+
84
+ // print measurement data
85
+ form.getTextField(`YourSystemScore${systemScoreColor}`).setText(`${systemScorePercentage}% ${systemScoreGrade}`);
86
+ form.getTextField('Superheat').setText(`${textFields.superheat} °${payload.units.temperature}`);
87
+ form.getTextField('Subcooling').setText(`${textFields.subcooling} °${payload.units.temperature}`);
88
+ form.getTextField('CondenserApproach').setText(`${textFields.approach} °${payload.units.temperature}`);
89
+ form.getTextField('TemperatureSplit').setText(`${textFields.tempSplit} °${payload.units.temperature}`);
90
+ form.getTextField('TotalExternalStaticPressure').setText(`${textFields.tesp} ${payload.units.pressurece_units}`);
91
+ form.getTextField('FilterFaceVelocity').setText(`${textFields.fltrFace}`);
92
+
93
+ // print targets and range icons
94
+ const measureLabels = ["Superheat", "Subcooling", "Condenser", "TempSplit", "Tesp", "FilterFace"];
95
+ const targetKeys = ["superheat", "subcooling", "approach", "temperature_split", "pressure_static_total_external", "velocity_face_filter1"];
96
+ for (let i = 0; i < measureLabels.length; i++) {
97
+ let iconPlacement = "Mid";
98
+ let icon = iconRangeRed;
99
+ let low = +payload.test.targets[`${targetKeys[i]}_ideal_low`];
100
+ let high = +payload.test.targets[`${targetKeys[i]}_ideal_high`];
101
+ let actual = +payload.test.data[`${targetKeys[i]}`];
102
+ if (actual < low) iconPlacement = "Low";
103
+ else if (actual > high) iconPlacement = "High";
104
+ else icon = iconRangeGreen;
105
+ form.getButton(`Image${measureLabels[i]}${iconPlacement}_af_image`).setImage(icon);
106
+ let targetZone = "";
107
+ if (isNaN(low) || low == "NaN" || isNaN(high) || high == "NaN" ) targetZone = `(${low.toFixed(1)} - ${high.toFixed(1)})`;
108
+ form.getTextField(`${measureLabels[i]}Target`).setText(targetZone);
109
+ }
110
+
111
+ // print pass fail measures
112
+ let passFails = ["electrical_system_pass_fail", "air_distribution_system_pass_fail", "air_filtration_system_pass_fail", "condensate_drain_system_pass_fail", "refrigerant_charge_pass_fail", "outdoor_equipment_pass_fail", "indoor_equipment_pass_fail", "cooling_capacity_pass_fail", "cooling_electrical_efficiency_pass_fail"];
113
+ for (let i = 1; i <= passFails.length; i++) {
114
+ let icon = payload.test[passFails[i - 1]] == "Pass" ? iconRangeGreen : iconRangeRed;
115
+ form.getButton(`ImageSubsystem${i}_af_image`).setImage(icon);
116
+ }
117
+
118
+ // flatten and return as base64
119
+ form.flatten();
120
+ const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true });
121
+ return pdfBase64;
122
+ }
123
+
124
+ function getGradeFromScore(score) {
125
+ let grade = "F";
126
+ if (score == 100) return "A+";
127
+ else if (score >= 94) return "A";
128
+ else if (score >= 90) return "A-";
129
+ else if (score >= 87) return "B+";
130
+ else if (score >= 84) return "B";
131
+ else if (score >= 80) return "B-";
132
+ else if (score >= 77) return "C+";
133
+ else if (score >= 74) return "C";
134
+ else if (score >= 70) return "C-";
135
+ else if (score >= 67) return "D+";
136
+ else if (score >= 64) return "D";
137
+ else if (score >= 60) return "D-";
138
+ else return "F";
139
+ }
140
+
141
+ function getColorFromGrade(grade) {
142
+ switch (grade.substr(0, 1)) {
143
+ case "A": return "Green";
144
+ case "B": return "Yellow";
145
+ case "C": return "Orange";
146
+ default: return "Red";
147
+ }
148
+ }
149
+
150
+ function getTextFields(payload) {
151
+ return {
152
+ "cName": payload.site.customer.first_name && payload.site.customer.last_name ? `${payload.site.customer.first_name} ${payload.site.customer.last_name}` : "",
153
+ "address": payload.site.location.address ? payload.site.location.address : "",
154
+ "address2": payload.site.location.address2 ? payload.site.location.address2 : "",
155
+ "city": payload.site.location.city ? payload.site.location.city : "",
156
+ "state": payload.site.location.state ? `, ${payload.site.location.state}` : "",
157
+ "zip": payload.site.location.zip ? ` ${payload.site.location.zip}` : "",
158
+ "superheat": payload.test.data.superheat ? payload.test.data.superheat.toFixed(1) : "--",
159
+ "subcooling": payload.test.data.subcooling ? payload.test.data.subcooling.toFixed(1) : "--",
160
+ "approach": payload.test.data.approach ? payload.test.data.approach.toFixed(1) : "--",
161
+ "tempSplit": payload.test.data.temperature_split ? payload.test.data.temperature_split.toFixed(1) : "--",
162
+ "tesp": payload.test.data.pressure_static_total_external ? payload.test.data.pressure_static_total_external.toFixed(1) : "--",
163
+ "fltrFace": payload.test.data.velocity_face_filter1 ? payload.test.data.velocity_face_filter1.toFixed(1) + " SCFM" : "--"
164
+ }
165
+ }