Skip to content

Commit

Permalink
🎉 feat: ability to add officers/ems/fd to incidents (#593)
Browse files Browse the repository at this point in the history
  • Loading branch information
casperiv0 authored Apr 9, 2022
1 parent 2e5683b commit 9912337
Show file tree
Hide file tree
Showing 22 changed files with 386 additions and 227 deletions.
24 changes: 24 additions & 0 deletions packages/api/prisma/migrations/20220409085241_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- CreateTable
CREATE TABLE "IncidentInvolvedUnit" (
"id" TEXT NOT NULL,
"officerId" TEXT,
"emsFdDeputyId" TEXT,
"combinedLeoId" TEXT,
"incidentId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "IncidentInvolvedUnit_pkey" PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "IncidentInvolvedUnit" ADD CONSTRAINT "IncidentInvolvedUnit_officerId_fkey" FOREIGN KEY ("officerId") REFERENCES "Officer"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "IncidentInvolvedUnit" ADD CONSTRAINT "IncidentInvolvedUnit_incidentId_fkey" FOREIGN KEY ("incidentId") REFERENCES "LeoIncident"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "IncidentInvolvedUnit" ADD CONSTRAINT "IncidentInvolvedUnit_combinedLeoId_fkey" FOREIGN KEY ("combinedLeoId") REFERENCES "CombinedLeoUnit"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "IncidentInvolvedUnit" ADD CONSTRAINT "IncidentInvolvedUnit_emsFdDeputyId_fkey" FOREIGN KEY ("emsFdDeputyId") REFERENCES "EmsFdDeputy"("id") ON DELETE SET NULL ON UPDATE CASCADE;
144 changes: 81 additions & 63 deletions packages/api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -633,42 +633,43 @@ model EmployeeValue {

// leo
model Officer {
id String @id @default(cuid())
department DepartmentValue? @relation("officerDepartmentToDepartment", fields: [departmentId], references: [id])
id String @id @default(cuid())
department DepartmentValue? @relation("officerDepartmentToDepartment", fields: [departmentId], references: [id])
departmentId String?
callsign String @db.VarChar(255)
callsign2 String @db.VarChar(255)
callsign String @db.VarChar(255)
callsign2 String @db.VarChar(255)
// `division` is deprecated. Use `divisions` instead.
division DivisionValue? @relation("officerDivisionToDivision", fields: [divisionId], references: [id])
division DivisionValue? @relation("officerDivisionToDivision", fields: [divisionId], references: [id])
divisionId String?
divisions DivisionValue[] @relation("officerDivisionsToDivision")
rank Value? @relation("officerRankToValue", fields: [rankId], references: [id])
divisions DivisionValue[] @relation("officerDivisionsToDivision")
rank Value? @relation("officerRankToValue", fields: [rankId], references: [id])
rankId String?
status StatusValue? @relation("officerStatusToValue", fields: [statusId], references: [id])
status StatusValue? @relation("officerStatusToValue", fields: [statusId], references: [id])
statusId String?
suspended Boolean @default(false)
suspended Boolean @default(false)
badgeNumber Int?
imageId String? @db.VarChar(255)
citizen Citizen @relation(fields: [citizenId], references: [id], onDelete: Cascade)
imageId String? @db.VarChar(255)
citizen Citizen @relation(fields: [citizenId], references: [id], onDelete: Cascade)
citizenId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
whitelistStatus LeoWhitelistStatus? @relation(fields: [whitelistStatusId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
whitelistStatus LeoWhitelistStatus? @relation(fields: [whitelistStatusId], references: [id])
whitelistStatusId String?
radioChannelId String?
bolos Bolo[] @relation("bolosToOfficer")
bolos Bolo[] @relation("bolosToOfficer")
warrants Warrant[]
logs OfficerLog[]
Record Record[]
assignedUnit AssignedUnit[]
activeIncident LeoIncident? @relation("activeIncident", fields: [activeIncidentId], references: [id])
activeIncident LeoIncident? @relation("activeIncident", fields: [activeIncidentId], references: [id])
activeIncidentId String?
LeoIncident LeoIncident[]
LeoIncidentInvolvedOfficers LeoIncident[] @relation("involvedOfficers")
combinedLeoUnit CombinedLeoUnit? @relation(fields: [combinedLeoUnitId], references: [id])
LeoIncidentInvolvedOfficers LeoIncident[] @relation("involvedOfficers")
combinedLeoUnit CombinedLeoUnit? @relation(fields: [combinedLeoUnitId], references: [id])
combinedLeoUnitId String?
IncidentInvolvedUnit IncidentInvolvedUnit[]
}

model LeoWhitelistStatus {
Expand Down Expand Up @@ -719,26 +720,27 @@ model ImpoundedVehicle {
}

model LeoIncident {
id String @id @default(uuid())
caseNumber Int @default(autoincrement())
description String? @db.Text
id String @id @default(uuid())
caseNumber Int @default(autoincrement())
description String? @db.Text
descriptionData Json?
postal String?
// when null, it is the dispatcher
creator Officer? @relation(fields: [creatorId], references: [id])
creator Officer? @relation(fields: [creatorId], references: [id])
creatorId String?
officersInvolved Officer[] @relation("involvedOfficers")
firearmsInvolved Boolean @default(false)
injuriesOrFatalities Boolean @default(false)
arrestsMade Boolean @default(false)
isActive Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
situationCode StatusValue? @relation(fields: [situationCodeId], references: [id])
officersInvolved Officer[] @relation("involvedOfficers")
unitsInvolved IncidentInvolvedUnit[] @relation("unitsInvolved")
firearmsInvolved Boolean @default(false)
injuriesOrFatalities Boolean @default(false)
arrestsMade Boolean @default(false)
isActive Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
situationCode StatusValue? @relation(fields: [situationCodeId], references: [id])
situationCodeId String?
events IncidentEvent[]
calls Call911[]
Officer Officer[] @relation("activeIncident")
Officer Officer[] @relation("activeIncident")
}

model IncidentEvent {
Expand All @@ -751,15 +753,16 @@ model IncidentEvent {
}

model CombinedLeoUnit {
id String @id @default(uuid())
officers Officer[]
callsign String
callsign2 String?
incremental Int?
radioChannelId String?
status StatusValue? @relation("combinedUnitStatusToValue", fields: [statusId], references: [id])
statusId String?
AssignedUnit AssignedUnit[]
id String @id @default(uuid())
officers Officer[]
callsign String
callsign2 String?
incremental Int?
radioChannelId String?
status StatusValue? @relation("combinedUnitStatusToValue", fields: [statusId], references: [id])
statusId String?
AssignedUnit AssignedUnit[]
IncidentInvolvedUnit IncidentInvolvedUnit[]
}

// dispatching
Expand Down Expand Up @@ -817,6 +820,20 @@ model AssignedUnit {
updatedAt DateTime @default(now()) @updatedAt
}

model IncidentInvolvedUnit {
id String @id @default(uuid())
officer Officer? @relation(fields: [officerId], references: [id])
officerId String?
deputy EmsFdDeputy? @relation(fields: [emsFdDeputyId], references: [id])
emsFdDeputyId String?
combinedUnit CombinedLeoUnit? @relation(fields: [combinedLeoId], references: [id])
combinedLeoId String?
incident LeoIncident? @relation("unitsInvolved", fields: [incidentId], references: [id], onDelete: Cascade)
incidentId String?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
}

model Call911Event {
id String @id @default(uuid())
createdAt DateTime @default(now())
Expand Down Expand Up @@ -925,28 +942,29 @@ model NameChangeRequest {

// ems-fd
model EmsFdDeputy {
id String @id @default(cuid())
department DepartmentValue @relation("emsFdDepartmentToDepartment", fields: [departmentId], references: [id])
departmentId String
callsign String @db.VarChar(255)
callsign2 String @db.VarChar(255)
division DivisionValue @relation("emsFdDivisionToDivision", fields: [divisionId], references: [id])
divisionId String
rank Value? @relation("emsFdRankToValue", fields: [rankId], references: [id])
rankId String?
status StatusValue? @relation("emsFdStatusToValue", fields: [statusId], references: [id])
statusId String?
suspended Boolean @default(false)
badgeNumber Int?
imageId String? @db.VarChar(255)
citizen Citizen @relation(fields: [citizenId], references: [id], onDelete: Cascade)
citizenId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
radioChannelId String?
AssignedUnit AssignedUnit[]
id String @id @default(cuid())
department DepartmentValue @relation("emsFdDepartmentToDepartment", fields: [departmentId], references: [id])
departmentId String
callsign String @db.VarChar(255)
callsign2 String @db.VarChar(255)
division DivisionValue @relation("emsFdDivisionToDivision", fields: [divisionId], references: [id])
divisionId String
rank Value? @relation("emsFdRankToValue", fields: [rankId], references: [id])
rankId String?
status StatusValue? @relation("emsFdStatusToValue", fields: [statusId], references: [id])
statusId String?
suspended Boolean @default(false)
badgeNumber Int?
imageId String? @db.VarChar(255)
citizen Citizen @relation(fields: [citizenId], references: [id], onDelete: Cascade)
citizenId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
radioChannelId String?
AssignedUnit AssignedUnit[]
IncidentInvolvedUnit IncidentInvolvedUnit[]
}

// truck logs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { prisma } from "lib/prisma";
import { Socket } from "services/SocketService";
import { UseBeforeEach } from "@tsed/platform-middlewares";
import { IsAuth } from "middlewares/IsAuth";
import { unitProperties, leoProperties, combinedUnitProperties } from "lib/leo/activeOfficer";
import { unitProperties, combinedUnitProperties, _leoProperties } from "lib/leo/activeOfficer";
import { validateSchema } from "lib/validateSchema";
import {
ShouldDoType,
Expand All @@ -19,22 +19,24 @@ import {
User,
StatusValue,
MiscCadSettings,
Call911,
} from "@prisma/client";
import { sendDiscordWebhook } from "lib/discord/webhooks";
import type { cad, Call911 } from "@snailycad/types";
import type { cad } from "@snailycad/types";
import type { APIEmbed } from "discord-api-types/v10";
import { manyToManyHelper } from "utils/manyToMany";
import { Permissions, UsePermissions } from "middlewares/UsePermissions";
import { officerOrDeputyToUnit } from "lib/leo/officerOrDeputyToUnit";

const assignedUnitsInclude = {
export const assignedUnitsInclude = {
include: {
officer: { include: leoProperties },
officer: { include: _leoProperties },
deputy: { include: unitProperties },
combinedUnit: {
include: {
status: { include: { value: true } },
officers: {
include: leoProperties,
include: _leoProperties,
},
},
},
Expand All @@ -46,8 +48,8 @@ export const callInclude = {
assignedUnits: assignedUnitsInclude,
events: true,
incidents: true,
departments: { include: leoProperties.department.include },
divisions: { include: leoProperties.division.include },
departments: { include: _leoProperties.department.include },
divisions: { include: _leoProperties.division.include },
situationCode: { include: { value: true } },
};

Expand All @@ -70,7 +72,7 @@ export class Calls911Controller {
where: includeEnded ? undefined : { ended: false },
});

return calls.map(this.officerOrDeputyToUnit);
return calls.map(officerOrDeputyToUnit);
}

@Get("/:id")
Expand All @@ -85,7 +87,7 @@ export class Calls911Controller {
include: callInclude,
});

return this.officerOrDeputyToUnit(call);
return officerOrDeputyToUnit(call);
}

@Post("/")
Expand Down Expand Up @@ -129,7 +131,7 @@ export class Calls911Controller {
include: callInclude,
});

const returnData = this.officerOrDeputyToUnit(updated);
const returnData = officerOrDeputyToUnit(updated);

try {
const data = this.createWebhookData(returnData);
Expand Down Expand Up @@ -238,9 +240,9 @@ export class Calls911Controller {
include: callInclude,
});

this.socket.emitUpdate911Call(this.officerOrDeputyToUnit(updated));
this.socket.emitUpdate911Call(officerOrDeputyToUnit(updated));

return this.officerOrDeputyToUnit(updated);
return officerOrDeputyToUnit(updated);
}

@Delete("/purge")
Expand Down Expand Up @@ -392,24 +394,9 @@ export class Calls911Controller {
include: callInclude,
});

this.socket.emitUpdate911Call(this.officerOrDeputyToUnit(updated));
this.socket.emitUpdate911Call(officerOrDeputyToUnit(updated));

return this.officerOrDeputyToUnit(updated);
}

protected officerOrDeputyToUnit(call: any & { assignedUnits: any[] }) {
return {
...call,
assignedUnits: (call.assignedUnits ?? [])
?.map((v: any) => ({
...v,
officer: undefined,
deputy: undefined,

unit: v.officer ?? v.deputy ?? v.combinedUnit,
}))
.filter((v: any) => v.unit?.id),
};
return officerOrDeputyToUnit(updated);
}

protected async linkOrUnlinkCallDepartmentsAndDivisions({
Expand Down
5 changes: 4 additions & 1 deletion packages/api/src/controllers/dispatch/DispatchController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ExtendedNotFound } from "src/exceptions/ExtendedNotFound";
import { incidentInclude } from "controllers/leo/incidents/IncidentController";
import { UsePermissions, Permissions } from "middlewares/UsePermissions";
import { userProperties } from "lib/auth/user";
import { officerOrDeputyToUnit } from "lib/leo/officerOrDeputyToUnit";

@Controller("/dispatch")
@UseBeforeEach(IsAuth)
Expand Down Expand Up @@ -66,7 +67,9 @@ export class DispatchController {
include: incidentInclude,
});

return { deputies, officers, activeIncidents, activeDispatchers };
const correctedIncidents = activeIncidents.map(officerOrDeputyToUnit);

return { deputies, officers, activeIncidents: correctedIncidents, activeDispatchers };
}

@Post("/aop")
Expand Down
Loading

0 comments on commit 9912337

Please sign in to comment.