Skip to content

Commit 8f48231

Browse files
authored
Merge pull request #43 from icefoganalytics/caleb/dev
Generating PDF for Printing Site
2 parents a67d795 + 228d3fb commit 8f48231

File tree

9 files changed

+1335
-458
lines changed

9 files changed

+1335
-458
lines changed

api/index.ts

Lines changed: 13 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,7 @@ app.use(
109109
// very basic CORS setup
110110
app.use(
111111
cors({
112-
origin: [
113-
config.FRONTEND_URL,
114-
'http://inf-docker-tst:27640',
115-
'http://localhost:27640',
116-
],
112+
origin: [config.FRONTEND_URL, 'http://inf-docker-tst:27640', 'http://localhost:27640'],
117113
optionsSuccessStatus: 200,
118114
credentials: true,
119115
})
@@ -139,20 +135,12 @@ app.use('/api/interpretive-sites', intSitesRouter);
139135
app.use('/api/actions', actionRouter);
140136
app.use('/api/assets', assetRouter);
141137
app.use('/api/inspections', inspectionRouter);
142-
app.use(
143-
'/api/association-types',
144-
RequiresAuthentication,
145-
associationTypesRouter
146-
);
138+
app.use('/api/association-types', RequiresAuthentication, associationTypesRouter);
147139
app.use('/api/boats', RequiresAuthentication, boatsRouter);
148140
app.use('/api/category-types', RequiresAuthentication, categoryTypesRouter);
149141
app.use('/api/communities', RequiresAuthentication, communitiesRouter);
150142
app.use('/api/condition-types', RequiresAuthentication, conditionTypesRouter);
151-
app.use(
152-
'/api/construction-period-types',
153-
RequiresAuthentication,
154-
constructionPeriodTypesRouter
155-
);
143+
app.use('/api/construction-period-types', RequiresAuthentication, constructionPeriodTypesRouter);
156144
app.use('/api/contact-types', RequiresAuthentication, contactTypesRouter);
157145
app.use(
158146
'/api/contributing-resource-types',
@@ -165,44 +153,20 @@ app.use(
165153
coordinateDeterminationTypesRouter
166154
);
167155
app.use('/api/date-types', RequiresAuthentication, dateTypesRouter);
168-
app.use(
169-
'/api/description-types',
170-
RequiresAuthentication,
171-
descriptionTypesRouter
172-
);
173-
app.use(
174-
'/api/designation-types',
175-
RequiresAuthentication,
176-
designationTypesRouter
177-
);
156+
app.use('/api/description-types', RequiresAuthentication, descriptionTypesRouter);
157+
app.use('/api/designation-types', RequiresAuthentication, designationTypesRouter);
178158
app.use(
179159
'/api/first-nation-association-types',
180160
RequiresAuthentication,
181161
firstNationAssociationTypesRouter
182162
);
183163
app.use('/api/first-nations', RequiresAuthentication, firstNationsRouter);
184164
app.use('/api/functional-types', RequiresAuthentication, functionalTypesRouter);
185-
app.use(
186-
'/api/functional-use-types',
187-
RequiresAuthentication,
188-
functionalUseTypesRouter
189-
);
190-
app.use(
191-
'/api/historical-pattern-types',
192-
RequiresAuthentication,
193-
historicalPatternTypesRouter
194-
);
195-
app.use(
196-
'/api/jurisdiction-types',
197-
RequiresAuthentication,
198-
jurisdictionTypesRouter
199-
);
165+
app.use('/api/functional-use-types', RequiresAuthentication, functionalUseTypesRouter);
166+
app.use('/api/historical-pattern-types', RequiresAuthentication, historicalPatternTypesRouter);
167+
app.use('/api/jurisdiction-types', RequiresAuthentication, jurisdictionTypesRouter);
200168
app.use('/api/nts-map-sheets', RequiresAuthentication, ntsMapSheetsRouter);
201-
app.use(
202-
'/api/owner-consent-types',
203-
RequiresAuthentication,
204-
ownerConsentTypesRouter
205-
);
169+
app.use('/api/owner-consent-types', RequiresAuthentication, ownerConsentTypesRouter);
206170
app.use('/api/people', RequiresAuthentication, peopleRouter);
207171
app.use('/api/owners', RequiresAuthentication, ownerRouter);
208172
app.use('/api/ownership-types', RequiresAuthentication, ownershipTypesRouter);
@@ -212,24 +176,12 @@ app.use('/api/catalogs', RequiresAuthentication, catalogsRouter);
212176
app.use('/api/people', RequiresAuthentication, peopleRouter);
213177
app.use('/api/photo-owners', RequiresAuthentication, photoOwnersRouter);
214178
app.use('/api/photos', photosExtraRouter); //removed auth check for testing
215-
app.use(
216-
'/api/revision-log-types',
217-
RequiresAuthentication,
218-
revisionLogTypesRouter
219-
);
179+
app.use('/api/revision-log-types', RequiresAuthentication, revisionLogTypesRouter);
220180
app.use('/api/place-edits', RequiresAuthentication, placeEditsRouter);
221181
app.use('/api/place-themes', RequiresAuthentication, placeThemesRouter);
222182
app.use('/api/record-types', RequiresAuthentication, recordTypesRouter);
223-
app.use(
224-
'/api/site-category-types',
225-
RequiresAuthentication,
226-
siteCategoryTypesRouter
227-
);
228-
app.use(
229-
'/api/site-status-types',
230-
RequiresAuthentication,
231-
siteStatusTypesRouter
232-
);
183+
app.use('/api/site-category-types', RequiresAuthentication, siteCategoryTypesRouter);
184+
app.use('/api/site-status-types', RequiresAuthentication, siteStatusTypesRouter);
233185
app.use('/api/statutes', RequiresAuthentication, statutesRouter);
234186
app.use('/api/users', RequiresAuthentication, usersExtraRouter);
235187
app.use('/api/people', RequiresAuthentication, peopleRouter);
@@ -253,12 +205,7 @@ app.use(Sentry.Handlers.errorHandler());
253205
// Optional fallthrough error handler
254206

255207
// eslint-disable-next-line @typescript-eslint/no-unused-vars
256-
app.use(function onError(
257-
err: Error,
258-
req: Request,
259-
res: any,
260-
next: NextFunction
261-
) {
208+
app.use(function onError(err: Error, req: Request, res: any, next: NextFunction) {
262209
// The error id is attached to `res.sentry` to be returned
263210
// and optionally displayed to the user for support.
264211
res.statusCode = 500;

api/models/place.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,17 @@ import {
55
FirstNationAssociation,
66
Name,
77
PlainObject,
8+
Description,
9+
Association,
10+
ConstructionPeriod,
11+
Contact,
12+
Theme,
13+
FunctionalUse,
14+
Ownership,
15+
RevisionLog,
16+
WebLink,
817
} from '.';
18+
import { Date as DateModel } from '../models/date';
919

1020
export class Place {
1121
id?: number;
@@ -77,6 +87,21 @@ export class Place {
7787
historicalPatterns?: HistoricalPattern[];
7888
names?: Name[];
7989

90+
// Virtual fields
91+
communityName?: string;
92+
coordinateDeterminationName?: string;
93+
associations?: Association[];
94+
constructionPeriods?: ConstructionPeriod[];
95+
contacts?: Contact[];
96+
dates?: DateModel[];
97+
descriptions?: Description[];
98+
themes?: Theme[];
99+
functionalUses?: FunctionalUse[];
100+
ownerships?: Ownership[];
101+
previousOwnerships?: Ownership[];
102+
revisionLogs?: RevisionLog[];
103+
webLinks?: WebLink[];
104+
80105
static FIELDS: ReadonlyArray<string> = Object.freeze([
81106
'id',
82107
'block',
@@ -148,24 +173,14 @@ export class Place {
148173
'siteCategories',
149174
]);
150175

151-
static encodeCommaDelimitedArray(
152-
value: undefined | null | string | string[]
153-
): string | null {
154-
if (
155-
value === undefined ||
156-
value === null ||
157-
value === '' ||
158-
value.length === 0
159-
)
160-
return null;
176+
static encodeCommaDelimitedArray(value: undefined | null | string | string[]): string | null {
177+
if (value === undefined || value === null || value === '' || value.length === 0) return null;
161178
if (isString(value)) return value;
162179

163180
return value.join(',');
164181
}
165182

166-
static decodeCommaDelimitedArray(
167-
value: undefined | null | string | string[]
168-
): string[] {
183+
static decodeCommaDelimitedArray(value: undefined | null | string | string[]): string[] {
169184
if (value === undefined || value === null || value === '') return [];
170185
if (Array.isArray(value)) return value;
171186

api/routes/place-router.ts

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
11
import express, { Request, Response } from 'express';
2-
import {
3-
body,
4-
check,
5-
param,
6-
query,
7-
validationResult,
8-
matchedData,
9-
} from 'express-validator';
10-
import fs from 'fs';
2+
import { body, check, param, query, validationResult, matchedData } from 'express-validator';
113
import multer from 'multer';
12-
import { create } from 'handlebars';
13-
import handlebarsHelpers from '../utils/handlebars-helpers';
4+
import { isNil, isString } from 'lodash';
145

156
import { API_PORT, DB_CONFIG } from '../config';
167
import { PhotoService, PlaceService } from '../services';
8+
import PrintSiteService from '../services/place/print-site-service';
179
import { ReturnValidationErrors } from '../middleware';
1810
import { authorize } from '../middleware/authorization';
1911
import { Place, User, UserRoles } from '../models';
@@ -87,11 +79,7 @@ placeRouter.post(
8779

8880
placeRouter.post(
8981
'/generate-id',
90-
authorize([
91-
UserRoles.SITE_ADMIN,
92-
UserRoles.SITE_EDITOR,
93-
UserRoles.ADMINISTRATOR,
94-
]),
82+
authorize([UserRoles.SITE_ADMIN, UserRoles.SITE_EDITOR, UserRoles.ADMINISTRATOR]),
9583
[body('nTSMapSheet').isString().bail().notEmpty().trim()],
9684
async (req: Request, res: Response) => {
9785
const errors = validationResult(req);
@@ -137,39 +125,38 @@ placeRouter.get(
137125
const { format } = req.params;
138126
const currentUser = req.user as User;
139127

140-
const place = await placeService
128+
const placeData = await placeService
141129
.getById(id, currentUser)
142130
.then(({ place, relationships }) => {
143131
const policy = new PlacePolicy(currentUser, place);
144132
if (policy.show()) {
145133
return {
146-
...place,
134+
place,
147135
relationships,
148136
API_PORT,
149137
};
150138
}
151139
});
152140

153-
console.log('place');
141+
if (isNil(placeData)) {
142+
res.status(500).send('Failed to load place');
143+
return;
144+
}
145+
146+
const { place } = placeData;
147+
148+
const { sections } = req.query;
154149

155-
//(place as any).API_PORT = API_PORT;
156-
const PDF_TEMPLATE = fs.readFileSync(
157-
__dirname + '/../templates/places/placePrint.handlebars'
158-
);
159-
const h = create();
160-
h.registerHelper('joinArray', handlebarsHelpers.joinArray);
161-
h.registerHelper('joinArrayPick', handlebarsHelpers.joinArrayPick);
162-
const template = h.compile(PDF_TEMPLATE.toString(), {});
163-
const data = template(place);
150+
const selectedSections = isString(sections) ? sections.split(',') : [];
151+
152+
const data = await PrintSiteService.perform(place, selectedSections);
164153

165154
if (format == 'html') {
166155
res.send(data);
167156
} else {
168157
const pdf = await generatePDF(data, 'letter', false);
169-
res.setHeader(
170-
'Content-disposition',
171-
`filename="SitePrint-${(place as any).primaryName}.pdf"`
172-
);
158+
const primaryName = place.primaryName;
159+
res.setHeader('Content-disposition', `filename="SitePrint-${primaryName}.pdf"`);
173160
res.setHeader('Content-type', 'application/pdf');
174161
res.send(pdf);
175162
}
@@ -178,11 +165,7 @@ placeRouter.get(
178165

179166
placeRouter.post(
180167
'/',
181-
authorize([
182-
UserRoles.SITE_ADMIN,
183-
UserRoles.SITE_EDITOR,
184-
UserRoles.ADMINISTRATOR,
185-
]),
168+
authorize([UserRoles.SITE_ADMIN, UserRoles.SITE_EDITOR, UserRoles.ADMINISTRATOR]),
186169
[
187170
body('primaryName').isString().bail().notEmpty().trim(),
188171
//body('yHSIId').isString().bail().notEmpty().trim(),
@@ -225,11 +208,7 @@ placeRouter.post(
225208

226209
placeRouter.post(
227210
'/:id/photo',
228-
authorize([
229-
UserRoles.SITE_ADMIN,
230-
UserRoles.SITE_EDITOR,
231-
UserRoles.ADMINISTRATOR,
232-
]),
211+
authorize([UserRoles.SITE_ADMIN, UserRoles.SITE_EDITOR, UserRoles.ADMINISTRATOR]),
233212
multer().single('file'),
234213
async (req: Request, res: Response) => {
235214
try {

api/services/base-service.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
type HasNoArgsConstructor<T> = T extends { new (): any } ? true : false;
2+
3+
type CleanConstructorParameters<T extends typeof BaseService> =
4+
HasNoArgsConstructor<T> extends true ? [] : ConstructorParameters<T>;
5+
6+
export class BaseService {
7+
constructor(...args: any[]) {}
8+
9+
static perform<T extends typeof BaseService>(
10+
this: T,
11+
...args: CleanConstructorParameters<T>
12+
): ReturnType<InstanceType<T>['perform']> {
13+
const instance = new this(...args);
14+
return instance.perform();
15+
}
16+
17+
perform(): any {
18+
throw new Error('Not Implemented');
19+
}
20+
}
21+
22+
export default BaseService;
23+
24+
// Type Testing - keeping until I have real tests implemented
25+
// class AsyncService extends BaseService {
26+
// private param1: number
27+
28+
// constructor(param1: number) {
29+
// super()
30+
// this.param1 = param1
31+
// }
32+
33+
// async perform(): Promise<string[]> {
34+
// return ["async-string1", "async-string2"]
35+
// }
36+
// }
37+
38+
// class NonAsyncService extends BaseService {
39+
// perform(): string {
40+
// return "non-async-string"
41+
// }
42+
// }
43+
44+
// const param1 = 77
45+
// AsyncService.perform(param1).then((result: string[]) => {
46+
// logger.log(result)
47+
// })
48+
49+
// const result = NonAsyncService.perform()
50+
// logger.log(result)

0 commit comments

Comments
 (0)