Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial load testing code #829

Closed
wants to merge 13 commits into from
Closed
34 changes: 34 additions & 0 deletions backend/src/common/methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,37 @@ export const formatPhonenumber = (input: string): string => {
//-- return the phone number as its stored
return input;
};

export const getFileType = (input: string): string => {
const extension = getFileExtension(input);
return mapExtensiontoFileType(extension);
};

export const getFileExtension = (input: string): string => {
return input
.substring(input.lastIndexOf(".") + 1)
.toLowerCase()
.trim();
};

export const mapExtensiontoFileType = (input: string): string => {
if (["bmp", "gif", "heif", "heic", "jpg", "jpeg", "png", "psd", "svg", "tif", "tiff"].includes(input)) {
return "Image";
}
if (["doc", "docx", "md", "odt", "pdf", "ppt", "rtf", "txt", "xls", "xlsx"].includes(input)) {
return "Document";
}
if (["flac", "mp3", "aac", "ogg", "wma", "wav", "wave"].includes(input)) {
return "Audio";
}
if (["avi", "flv", "mov", "mp4"].includes(input)) {
return "Video";
}
if (["7z", "jar", "rar", "zip"].includes(input)) {
return "Archive";
}
if (["eml", "msg", "ost", "pst"].includes(input)) {
return "Email";
}
return "Unknown";
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { COMPLAINT_TYPE } from "./complaint-type";

export interface ExportComplaintParameters {
id: string;
type: COMPLAINT_TYPE;
tz: string;
attachments: any;
}
13 changes: 13 additions & 0 deletions backend/src/types/models/general/attachment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface Attachment {
type: AttachmentType;
date: Date;
name: string;
user: string;
sequenceId: number;
fileType: string;
}

export enum AttachmentType {
COMPLAINT_ATTACHMENT = "COMPLAINT_ATTACHMENT",
OUTCOME_ATTACHMENT = "OUTCOME_ATTACHMENT",
}
84 changes: 58 additions & 26 deletions backend/src/v1/complaint/complaint.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ import { CompMthdRecvCdAgcyCdXrefService } from "../comp_mthd_recv_cd_agcy_cd_xr
import { OfficerService } from "../officer/officer.service";
import { SpeciesCode } from "../species_code/entities/species_code.entity";
import { LinkedComplaintXrefService } from "../linked_complaint_xref/linked_complaint_xref.service";

import { Attachment, AttachmentType } from "../../types/models/general/attachment";
import { getFileType } from "../../common/methods";
const WorldBounds: Array<number> = [-180, -90, 180, 90];
type complaintAlias = HwcrComplaint | AllegationComplaint | GirComplaint;
@Injectable({ scope: Scope.REQUEST })
Expand Down Expand Up @@ -434,8 +435,28 @@ export class ComplaintService {
query: string,
token: string,
): Promise<SelectQueryBuilder<complaintAlias>> {
let caseSearchData = [];
if (complaintType === "ERS") {
// Search CM for any case files that may match based on authorization id
const { data, errors } = await get(token, {
query: `{getCasesFilesBySearchString (searchString: "${query}")
{
leadIdentifier,
caseIdentifier
}
}`,
});

if (errors) {
this.logger.error("GraphQL errors:", errors);
throw new Error("GraphQL errors occurred");
}

caseSearchData = data.getCasesFilesBySearchString;
}

builder.andWhere(
new Brackets(async (qb) => {
new Brackets((qb) => {
qb.orWhere("complaint.complaint_identifier ILIKE :query", {
query: `%${query}%`,
});
Expand Down Expand Up @@ -502,23 +523,6 @@ export class ComplaintService {

switch (complaintType) {
case "ERS": {
// Search CM for any case files that may match based on authorization id
const { data, errors } = await get(token, {
query: `{getCasesFilesBySearchString (searchString: "${query}")
{
leadIdentifier,
caseIdentifier
}
}`,
});

if (errors) {
this.logger.error("GraphQL errors:", errors);
throw new Error("GraphQL errors occurred");
}

const caseSearchData = data.getCasesFilesBySearchString;

if (caseSearchData.length > 0) {
qb.orWhere("complaint.complaint_identifier IN(:...complaint_identifiers)", {
complaint_identifiers: caseSearchData.map((caseData) => caseData.leadIdentifier),
Expand Down Expand Up @@ -996,7 +1000,6 @@ export class ComplaintService {
token?: string,
): Promise<SearchResults> => {
try {
this.logger.error("Searching for complaints");
let results: SearchResults = { totalCount: 0, complaints: [] };

const { orderBy, sortBy, page, pageSize, query, ...filters } = model;
Expand Down Expand Up @@ -1133,16 +1136,16 @@ export class ComplaintService {
const includeCosOrganization: boolean = Boolean(query || filters.community || filters.zone || filters.region);
let builder = this._generateMapQueryBuilder(complaintType, includeCosOrganization);

//-- apply search
if (query) {
builder = await this._applySearch(builder, complaintType, query, token);
}

//-- apply filters if used
if (Object.keys(filters).length !== 0) {
builder = this._applyFilters(builder, filters as ComplaintFilterParameters, complaintType);
}

//-- apply search
if (query) {
builder = await this._applySearch(builder, complaintType, query, token);
}

//-- only return complaints for the agency the user is associated with
const agency = hasCEEBRole ? "EPO" : (await this._getAgencyByUser()).agency_code;
agency && builder.andWhere("complaint.owned_by_agency_code.agency_code = :agency", { agency });
Expand Down Expand Up @@ -1714,7 +1717,13 @@ export class ComplaintService {
return results;
};

getReportData = async (id: string, complaintType: COMPLAINT_TYPE, tz: string, token: string) => {
getReportData = async (
id: string,
complaintType: COMPLAINT_TYPE,
tz: string,
token: string,
attachments: Attachment[],
) => {
let data;
mapWildlifeReport(this.mapper, tz);
mapAllegationReport(this.mapper, tz);
Expand Down Expand Up @@ -2181,6 +2190,29 @@ export class ComplaintService {
if (data.incidentDateTime) {
data.incidentDateTime = _applyTimezone(data.incidentDateTime, tz, "datetime");
}
// Using short names like "cAtts" and "oAtts" to fit them in CDOGS template table cells
data.cAtts = attachments
.filter((item) => item.type === AttachmentType.COMPLAINT_ATTACHMENT)
.map((item) => {
return {
name: item.name,
date: _applyTimezone(item.date, tz, "datetime"),
fileType: getFileType(item.name),
};
});
data.hasComplaintAttachments = data.cAtts?.length > 0;

data.oAtts = attachments
.filter((item) => item.type === AttachmentType.OUTCOME_ATTACHMENT)
.map((item) => {
return {
name: item.name,
date: _applyTimezone(item.date, tz, "datetime"),
fileType: getFileType(item.name),
};
});

data.hasOutcomeAttachments = data.oAtts?.length > 0;

return data;
} catch (error) {
Expand Down
44 changes: 33 additions & 11 deletions backend/src/v1/document/document.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Controller, Get, Logger, Param, Query, Res, UseGuards } from "@nestjs/common";
import { Body, Controller, Logger, Post, Res, UseGuards } from "@nestjs/common";
import { Response } from "express";
import { DocumentService } from "./document.service";
import { JwtRoleGuard } from "../../auth/jwtrole.guard";
Expand All @@ -9,6 +9,8 @@ import { Token } from "../../auth/decorators/token.decorator";
import { COMPLAINT_TYPE } from "../../types/models/complaints/complaint-type";
import { format } from "date-fns";
import { escape } from "escape-html";
import { ExportComplaintParameters } from "src/types/models/complaints/export-complaint-parameters";
import { Attachment, AttachmentType } from "../../types/models/general/attachment";

@UseGuards(JwtRoleGuard)
@ApiTags("document")
Expand All @@ -18,18 +20,38 @@ export class DocumentController {

constructor(private readonly service: DocumentService) {}

@Get("/export-complaint/:type")
@Post("/export-complaint")
@Roles(Role.COS_OFFICER, Role.CEEB)
async exportComplaint(
@Param("type") type: COMPLAINT_TYPE,
@Query("id") id: string,
@Query("tz") tz: string,
@Token() token,
@Res() res: Response,
): Promise<void> {
async exportComplaint(@Body() model: ExportComplaintParameters, @Token() token, @Res() res: Response): Promise<void> {
const id: string = model?.id ?? "unknown";

const complaintsAttachments = model?.attachments?.complaintsAttachments ?? [];
const outcomeAttachments = model?.attachments?.outcomeAttachments ?? [];

const attachments: Attachment[] = [
...complaintsAttachments.map((item, index) => {
return {
type: AttachmentType.COMPLAINT_ATTACHMENT,
user: item.createdBy,
name: decodeURIComponent(item.name),
date: item.createdAt,
sequenceId: index,
} as Attachment;
}),
...outcomeAttachments.map((item, index) => {
return {
type: AttachmentType.OUTCOME_ATTACHMENT,
date: item.createdAt,
name: decodeURIComponent(item.name),
user: item.createdBy,
sequenceId: index,
} as Attachment;
}),
];

try {
const fileName = `Complaint-${id}-${type}-${format(new Date(), "yyyy-MM-dd")}.pdf`;
const response = await this.service.exportComplaint(id, type, fileName, tz, token);
const fileName = `Complaint-${id}-${model.type}-${format(new Date(), "yyyy-MM-dd")}.pdf`;
const response = await this.service.exportComplaint(id, model.type, fileName, model.tz, token, attachments);

if (!response || !response.data) {
throw Error(`exception: unable to export document for complaint: ${id}`);
Expand Down
12 changes: 10 additions & 2 deletions backend/src/v1/document/document.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Inject, Injectable, Logger } from "@nestjs/common";
import { CdogsService } from "../../external_api/cdogs/cdogs.service";
import { ComplaintService } from "../complaint/complaint.service";
import { COMPLAINT_TYPE } from "../../types/models/complaints/complaint-type";
import { Attachment } from "src/types/models/general/attachment";

@Injectable()
export class DocumentService {
Expand All @@ -17,11 +18,18 @@ export class DocumentService {
//-- using the cdogs api generate a new document from the specified
//-- complaint-id and complaint type
//--
exportComplaint = async (id: string, type: COMPLAINT_TYPE, name: string, tz: string, token: string) => {
exportComplaint = async (
id: string,
type: COMPLAINT_TYPE,
name: string,
tz: string,
token: string,
attachments?: Attachment[],
) => {
try {
//-- get the complaint from the system, but do not include anything other
//-- than the base complaint. no maps, no attachments, no outcome data
const data = await this.ceds.getReportData(id, type, tz, token);
const data = await this.ceds.getReportData(id, type, tz, token, attachments);

//--
return await this.cdogs.generate(name, data, type);
Expand Down
4 changes: 2 additions & 2 deletions backend/src/v1/officer/officer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ export class OfficerService {
.leftJoinAndSelect("officer.office_guid", "office")
.leftJoinAndSelect("officer.person_guid", "person")
.leftJoinAndSelect("office.agency_code", "agency")
// This view is slow, no need to join and select for the properties that are mapped in this call
//.leftJoinAndSelect("office.cos_geo_org_unit", "cos_geo_org_unit")
// This view is slow :(
.leftJoinAndSelect("office.cos_geo_org_unit", "cos_geo_org_unit")
.leftJoinAndSelect("office.agency_code", "agency_code")
.orderBy("person.last_name", "ASC")
.getMany();
Expand Down
Binary file modified backend/templates/complaint/CDOGS-CEEB-COMPLAINT-TEMPLATE-v1.docx
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions charts/app/templates/secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
{{- $webeocUrl := (get $secretData "webeocUrl" | b64dec | default "") }}
{{- $webeocCronExpression := (get $secretData "webeocCronExpression" | b64dec | default "") }}
{{- $webeocLogPath := (get $secretData "webeocLogPath" | b64dec | default "") }}
{{- $webeocDateFilter := (get $secretData "webeocDateFilter" | b64dec | default "2025-01-01T08:00:00Z") }}
{{- $backupDir := (get $secretData "backupDir" | b64dec | default "") }}
{{- $backupStrategy := (get $secretData "backupStrategy" | b64dec | default "") }}
{{- $numBackups := (get $secretData "numBackups" | b64dec | default "") }}
Expand Down Expand Up @@ -139,6 +140,7 @@ data:
WEBEOC_URL: {{ $webeocUrl | b64enc | quote }}
WEBEOC_CRON_EXPRESSION: {{ $webeocCronExpression | b64enc | quote }}
WEBEOC_LOG_PATH: {{ $webeocLogPath | b64enc | quote }}
WEBEOC_DATE_FILTER: {{ $webeocDateFilter | b64enc | quote }}
COMPLAINTS_API_KEY: {{ $caseManagementApiKey | b64enc | quote }}
{{- end }}
{{- if not (lookup "v1" "Secret" .Release.Namespace (printf "%s-flyway" .Release.Name)) }}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/common/validation-checkbox-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const ValidationCheckboxGroup: FC<ValidationCheckboxGroupProps> = ({

useEffect(() => {
setCheckedItems(checkedValues);
}, [checkedValues.length]);
}, [checkedValues, checkedValues.length]);

return (
<div id="checkbox-div">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/common/validation-phone-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const ValidationPhoneInput: FC<ValidationPhoneInputProps> = ({
international,
}) => {
const errClass = errMsg === "" ? "" : "error-message";
const calulatedClass = errMsg === "" ? "comp-form-control" : "comp-form-control" + " error-border";
const calulatedClass = errMsg === "" ? "comp-form-control" : "comp-form-control error-border";
return (
<div>
<div className={className}>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/app/components/common/attachments-carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const AttachmentsCarousel: FC<Props> = ({
if (complaintIdentifier) {
dispatch(getAttachments(complaintIdentifier, attachmentType));
}
}, [complaintIdentifier, dispatch]);
}, [attachmentType, complaintIdentifier, dispatch]);

//-- when the component unmounts clear the attachments from redux
useEffect(() => {
Expand All @@ -82,15 +82,15 @@ export const AttachmentsCarousel: FC<Props> = ({
if (typeof onSlideCountChange === "function") {
onSlideCountChange(slides.length);
}
}, [slides.length]);
}, [onSlideCountChange, slides.length]);

// Clear all pending upload attachments
useEffect(() => {
if (cancelPendingUpload) {
setSlides([]);
if (setCancelPendingUpload) setCancelPendingUpload(false); //reset cancelPendingUpload
}
}, [cancelPendingUpload]);
}, [cancelPendingUpload, setCancelPendingUpload]);

function sortAttachmentsByName(comsObjects: COMSObject[]): COMSObject[] {
// Create a copy of the array using slice() or spread syntax
Expand Down
Loading