From 9172383f88987a7ab42171e3ed9d55b3c76e7fea Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Tue, 23 Apr 2024 17:26:34 -0700 Subject: [PATCH 01/17] feat: added sql scripts for new animal outcome codes --- migrations/sql/R__insert_animal_outcome.sql | 115 ++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 migrations/sql/R__insert_animal_outcome.sql diff --git a/migrations/sql/R__insert_animal_outcome.sql b/migrations/sql/R__insert_animal_outcome.sql new file mode 100644 index 00000000..4d472d8c --- /dev/null +++ b/migrations/sql/R__insert_animal_outcome.sql @@ -0,0 +1,115 @@ +-- +-- INSERT New animal outcome action type codes +-- +INSERT INTO + case_management.action_type_code ( + action_type_code, + short_description, + long_description, + active_ind, + create_user_id, + create_utc_timestamp, + update_user_id, + update_utc_timestamp + ) +VALUES + ( + 'WILDLIFE', + 'Wildlife', + 'For actions related to the wildlife section', + 'Y', + CURRENT_USER, + CURRENT_TIMESTAMP, + CURRENT_USER, + CURRENT_TIMESTAMP + ) ON CONFLICT DO NOTHING; + +-- +-- INSERT New animal outcome action codes +-- +INSERT INTO + case_management.action_code ( + action_code, + short_description, + long_description, + active_ind, + create_user_id, + create_utc_timestamp, + update_user_id, + update_utc_timestamp + ) +VALUES + ( + 'ADMNSTRDRG', + 'Drug administered by an officer', + 'Drug administered by an officer', + 'Y', + CURRENT_USER, + CURRENT_TIMESTAMP, + CURRENT_USER, + CURRENT_TIMESTAMP + ) ON CONFLICT DO NOTHING; + +INSERT INTO + case_management.action_code ( + action_code, + short_description, + long_description, + active_ind, + create_user_id, + create_utc_timestamp, + update_user_id, + update_utc_timestamp + ) +VALUES + ( + 'RECOUTCOME', + 'Outcome recorded by an officer', + 'Outcome recorded by an officer', + 'Y', + CURRENT_USER, + CURRENT_TIMESTAMP, + CURRENT_USER, + CURRENT_TIMESTAMP + ) ON CONFLICT DO NOTHING; + +-- +-- INSERT New animal outcome action xref +-- +INSERT INTO + case_management.action_type_action_xref ( + action_type_code, + action_code, + display_order, + active_ind, + create_user_id, + create_utc_timestamp + ) +VALUES + ( + 'WILDLIFE', + 'ADMNSTRDRG', + 1, + 'Y', + CURRENT_USER, + CURRENT_TIMESTAMP + ) ON CONFLICT DO NOTHING; + +INSERT INTO + case_management.action_type_action_xref ( + action_type_code, + action_code, + display_order, + active_ind, + create_user_id, + create_utc_timestamp + ) +VALUES + ( + 'WILDLIFE', + 'RECOUTCOME', + 2, + 'Y', + CURRENT_USER, + CURRENT_TIMESTAMP + ) ON CONFLICT DO NOTHING; \ No newline at end of file From 45418bbe0cd5e26c9e7da45c063b95a22fbf0ba5 Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Wed, 24 Apr 2024 14:42:21 -0700 Subject: [PATCH 02/17] feat: added wildlife table --- .../V1.18.0__CE-358-animal-outcome-tables.sql | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 migrations/sql/V1.18.0__CE-358-animal-outcome-tables.sql diff --git a/migrations/sql/V1.18.0__CE-358-animal-outcome-tables.sql b/migrations/sql/V1.18.0__CE-358-animal-outcome-tables.sql new file mode 100644 index 00000000..ddff1a90 --- /dev/null +++ b/migrations/sql/V1.18.0__CE-358-animal-outcome-tables.sql @@ -0,0 +1,71 @@ +-- +-- CREATE TABLE wildlife +-- +CREATE TABLE + case_management.wildlife ( + wildlife_guid uuid NOT NULL, + case_file_guid uuid NOT NULL, + threat_level_code varchar(10), + conflict_history_code varchar(10), + sex_code varchar(10), + age_code varchar(10), + hwcr_outcome_code varchar(10), + species_code varchar(10) NOT NULL, + create_user_id varchar(32) NOT NULL, + create_utc_timestamp timestamp NOT NULL, + update_user_id varchar(32) NOT NULL, + update_utc_timestamp timestamp NOT NULL, + CONSTRAINT "PK_wildlife_guid" PRIMARY KEY (wildlife_guid) + ) + -- set default PK value +ALTER TABLE case_management.wildlife +ALTER COLUMN wildlife +SET DEFAULT uuid_generate_v4 (); + +-- foreign keys +ALTER TABLE case_management.wildlife ADD CONSTRAINT FK_wildlife__case_file_guid FOREIGN KEY (case_file_guid) REFERENCES case_management.case_file (case_file_guid); + +ALTER TABLE case_management.wildlife ADD CONSTRAINT FK_wildlife__threat_level_code FOREIGN KEY (threat_level_code) REFERENCES case_management.threat_level_code (threat_level_code); + +ALTER TABLE case_management.wildlife ADD CONSTRAINT FK_wildlife__conflict_history_code FOREIGN KEY (conflict_history_code) REFERENCES case_management.conflict_history_code (conflict_history_code); + +ALTER TABLE case_management.wildlife ADD CONSTRAINT FK_wildlife__sex_code FOREIGN KEY (sex_code) REFERENCES case_management.sex_code (sex_code); + +ALTER TABLE case_management.wildlife ADD CONSTRAINT FK_wildlife__age_code FOREIGN KEY (age_code) REFERENCES case_management.age_code (age_code); + +ALTER TABLE case_management.wildlife ADD CONSTRAINT FK_wildlife__hwcr_outcome_code FOREIGN KEY (hwcr_outcome_code) REFERENCES case_management.hwcr_outcome_code (hwcr_outcome_code); + +ALTER TABLE case_management.wildlife ADD CONSTRAINT FK_wildlife__species_code FOREIGN KEY (species_code) REFERENCES case_management.species_code (species_code); + +-- create new action reference +ALTER TABLE action +ADD COLUMN wildlife_guid uuid; + +ALTER TABLE case_management.action ADD CONSTRAINT FK_action__wildlife_guid FOREIGN KEY (wildlife_guid) REFERENCES case_management.wildlife (wildlife_guid); + +-- table comments +comment on table case_management.wildlife is 'WILDLIFE can be a participant on a CASE_FILE, specifically for CASE_FILE with a CASE_CODE of ' HWCR ' (Human / Wildlife Conflict)'; + +comment on column case_management.wildlife.wildlife_guid is 'System generated unique key for an animal. This key should never be exposed to users via any system utilizing the tables.'; + +comment on column case_management.wildlife.case_file_guid is 'System generated unique key for a case. This key should never be exposed to users via any system utilizing the tables.'; + +comment on column case_management.wildlife.threat_level_code is 'A human readable code used to identify a threat level type.'; + +comment on column case_management.wildlife.conflict_history_code is 'A human readable code used to identify a conflict history type.'; + +comment on column case_management.wildlife.sex_code is 'A human readable code used to identify a sex type.'; + +comment on column case_management.wildlife.age_code is 'A human readable code used to identify an age type.'; + +comment on column case_management.wildlife.hwcr_outcome_code is 'A human readable code used to identify an HWCR outcome type.'; + +comment on column case_management.wildlife.species_code is 'A human readable code used to identify a species. The COS Complaint Management is the authorative source for the screen labels and descriptions of the codes.'; + +comment on column case_management.wildlife.create_user_id is 'The id of the user that created the wildlife record.'; + +comment on column case_management.wildlife.create_utc_timestamp is 'The timestamp when the wildlife record was created. The timestamp is stored in UTC with no Offset.'; + +comment on column case_management.wildlife.update_user_id is 'The id of the user that updated the wildlife record.'; + +comment on column case_management.wildlife.update_utc_timestamp is 'The timestamp when the wildlife record was updated. The timestamp is stored in UTC with no Offset.'; \ No newline at end of file From 655caa6bd3c017d0e98cf99b78d6e40cade25f20 Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Wed, 24 Apr 2024 16:50:39 -0700 Subject: [PATCH 03/17] feat: database tables generated --- .../V1.18.0__CE-358-animal-outcome-tables.sql | 218 +++++++++++++++++- 1 file changed, 208 insertions(+), 10 deletions(-) diff --git a/migrations/sql/V1.18.0__CE-358-animal-outcome-tables.sql b/migrations/sql/V1.18.0__CE-358-animal-outcome-tables.sql index ddff1a90..a5cf9425 100644 --- a/migrations/sql/V1.18.0__CE-358-animal-outcome-tables.sql +++ b/migrations/sql/V1.18.0__CE-358-animal-outcome-tables.sql @@ -3,7 +3,7 @@ -- CREATE TABLE case_management.wildlife ( - wildlife_guid uuid NOT NULL, + wildlife_guid uuid NOT NULL DEFAULT uuid_generate_v4 (), case_file_guid uuid NOT NULL, threat_level_code varchar(10), conflict_history_code varchar(10), @@ -16,11 +16,7 @@ CREATE TABLE update_user_id varchar(32) NOT NULL, update_utc_timestamp timestamp NOT NULL, CONSTRAINT "PK_wildlife_guid" PRIMARY KEY (wildlife_guid) - ) - -- set default PK value -ALTER TABLE case_management.wildlife -ALTER COLUMN wildlife -SET DEFAULT uuid_generate_v4 (); + ); -- foreign keys ALTER TABLE case_management.wildlife ADD CONSTRAINT FK_wildlife__case_file_guid FOREIGN KEY (case_file_guid) REFERENCES case_management.case_file (case_file_guid); @@ -35,8 +31,6 @@ ALTER TABLE case_management.wildlife ADD CONSTRAINT FK_wildlife__age_code FOREIG ALTER TABLE case_management.wildlife ADD CONSTRAINT FK_wildlife__hwcr_outcome_code FOREIGN KEY (hwcr_outcome_code) REFERENCES case_management.hwcr_outcome_code (hwcr_outcome_code); -ALTER TABLE case_management.wildlife ADD CONSTRAINT FK_wildlife__species_code FOREIGN KEY (species_code) REFERENCES case_management.species_code (species_code); - -- create new action reference ALTER TABLE action ADD COLUMN wildlife_guid uuid; @@ -44,7 +38,7 @@ ADD COLUMN wildlife_guid uuid; ALTER TABLE case_management.action ADD CONSTRAINT FK_action__wildlife_guid FOREIGN KEY (wildlife_guid) REFERENCES case_management.wildlife (wildlife_guid); -- table comments -comment on table case_management.wildlife is 'WILDLIFE can be a participant on a CASE_FILE, specifically for CASE_FILE with a CASE_CODE of ' HWCR ' (Human / Wildlife Conflict)'; +comment on table case_management.wildlife is 'WILDLIFE can be a participant on a CASE_FILE, specifically for CASE_FILE with a CASE_CODE of "HWCR" (Human / Wildlife Conflict)'; comment on column case_management.wildlife.wildlife_guid is 'System generated unique key for an animal. This key should never be exposed to users via any system utilizing the tables.'; @@ -68,4 +62,208 @@ comment on column case_management.wildlife.create_utc_timestamp is 'The timestam comment on column case_management.wildlife.update_user_id is 'The id of the user that updated the wildlife record.'; -comment on column case_management.wildlife.update_utc_timestamp is 'The timestamp when the wildlife record was updated. The timestamp is stored in UTC with no Offset.'; \ No newline at end of file +comment on column case_management.wildlife.update_utc_timestamp is 'The timestamp when the wildlife record was updated. The timestamp is stored in UTC with no Offset.'; + +-- +-- CREATE TABLE ear_tag +-- +CREATE TABLE + case_management.ear_tag ( + ear_tag_guid uuid NOT NULL DEFAULT uuid_generate_v4 (), + wildlife_guid uuid NOT NULL, + ear_code varchar(10) NOT NULL, + ear_tag_identifier varchar(10) NOT NULL, + create_user_id varchar(32) NOT NULL, + create_utc_timestamp timestamp NOT NULL, + update_user_id varchar(32) NOT NULL, + update_utc_timestamp timestamp NOT NULL, + CONSTRAINT "PK_ear_tag_guid" PRIMARY KEY (ear_tag_guid) + ); + +-- foreign keys +ALTER TABLE case_management.ear_tag ADD CONSTRAINT FK_ear_tag__wildlife_guid FOREIGN KEY (wildlife_guid) REFERENCES case_management.wildlife (wildlife_guid); + +ALTER TABLE case_management.ear_tag ADD CONSTRAINT FK_ear_tag__ear_code FOREIGN KEY (ear_code) REFERENCES case_management.ear_code (ear_code); + +-- table comments +comment on table case_management.ear_tag is 'an EAR_TAG is used to identify a specific WILDLIFE allowing the animal to be tracked over time.'; + +comment on column case_management.ear_tag.ear_tag_guid is 'System generated unique key for an ear tag. This key should never be exposed to users via any system utilizing the tables.'; + +comment on column case_management.ear_tag.wildlife_guid is 'System generated unique key for an animal. This key should never be exposed to users via any system utilizing the tables.'; + +comment on column case_management.ear_tag.ear_code is 'A human readable code used to identify an ear type.'; + +comment on column case_management.ear_tag.ear_tag_identifier is 'An identifier used to label an animal, not used as a natural key as they are not guaranteed to be unique'; + +comment on column case_management.ear_tag.create_user_id is 'The id of the user that created the ear_tag record.'; + +comment on column case_management.ear_tag.create_utc_timestamp is 'The timestamp when the ear_tag record was created. The timestamp is stored in UTC with no Offset.'; + +comment on column case_management.ear_tag.update_user_id is 'The id of the user that updated the ear_tag record.'; + +comment on column case_management.ear_tag.update_utc_timestamp is 'The timestamp when the ear_tag record was updated. The timestamp is stored in UTC with no Offset.'; + +-- +-- CREATE TABLE drug_administered +-- +CREATE TABLE + case_management.drug_administered ( + drug_administered_guid uuid NOT NULL DEFAULT uuid_generate_v4 (), + wildlife_guid uuid NOT NULL, + drug_code varchar(10) NOT NULL, + drug_method_code varchar(10) NOT NULL, + drug_remaining_outcome_code varchar(10) NOT NULL, + vial_number varchar(50) NOT NULL, + drug_used_amount varchar(50) NOT NULL, + drug_discarded_amount varchar(50), + discard_method_text varchar(250), + adverse_reaction_text varchar(250), + create_user_id varchar(32) NOT NULL, + create_utc_timestamp timestamp NOT NULL, + update_user_id varchar(32) NOT NULL, + update_utc_timestamp timestamp NOT NULL, + CONSTRAINT "PK_drug_administered_guid" PRIMARY KEY (drug_administered_guid) + ); + +-- foreign keys +ALTER TABLE case_management.drug_administered ADD CONSTRAINT FK_drug_administered__wildlife_guid FOREIGN KEY (wildlife_guid) REFERENCES case_management.wildlife (wildlife_guid); + +ALTER TABLE case_management.drug_administered ADD CONSTRAINT FK_drug_administered__drug_code FOREIGN KEY (drug_code) REFERENCES case_management.drug_code (drug_code); + +ALTER TABLE case_management.drug_administered ADD CONSTRAINT FK_drug_administered__drug_method_code FOREIGN KEY (drug_method_code) REFERENCES case_management.drug_method_code (drug_method_code); + +ALTER TABLE case_management.drug_administered ADD CONSTRAINT FK_drug_administered__drug_remaining_outcome_code FOREIGN KEY (drug_remaining_outcome_code) REFERENCES case_management.drug_remaining_outcome_code (drug_remaining_outcome_code); + +-- table comments +comment on table case_management.drug_administered is 'While an officer is responding to a LEAD they may need to record that a WILDLIFE had a DRUG_ADMINISTERED. This table keeps track of the type and amount of drugs administered, along with what happened to any remaining drug that was not used.'; + +comment on column case_management.drug_administered.drug_administered_guid is 'System generated unique key for an drug that was administered to an animal. This key should never be exposed to users via any system utilizing the tables.'; + +comment on column case_management.drug_administered.wildlife_guid is 'System generated unique key for an animal. This key should never be exposed to users via any system utilizing the tables.'; + +comment on column case_management.drug_administered.drug_code is 'A human readable code used to identify a drug type.'; + +comment on column case_management.drug_administered.drug_method_code is 'A human readable code used to identify a drug injection method type.'; + +comment on column case_management.drug_administered.drug_remaining_outcome_code is 'A human readable code used to identify a remaining drug outcome type.'; + +comment on column case_management.drug_administered.vial_number is 'A vial number that relates to a seperate drug inventory system.'; + +comment on column case_management.drug_administered.drug_used_amount is 'The amount of drug used'; + +comment on column case_management.drug_administered.drug_discarded_amount is 'The amount of drug that was discarded'; + +comment on column case_management.drug_administered.discard_method_text is 'Brief narrative explaining how the drug was discared'; + +comment on column case_management.drug_administered.adverse_reaction_text is 'Brief narrative documenting any adverse reactions observed'; + +comment on column case_management.drug_administered.create_user_id is 'The id of the user that created the drug_administered record.'; + +comment on column case_management.drug_administered.create_utc_timestamp is 'The timestamp when the drug_administered record was created. The timestamp is stored in UTC with no Offset.'; + +comment on column case_management.drug_administered.update_user_id is 'The id of the user that updated the drug_administered record.'; + +comment on column case_management.drug_administered.update_utc_timestamp is 'The timestamp when the drug_administered record was updated. The timestamp is stored in UTC with no Offset.'; + +-- +-- CREATE TABLE wildlife_h +-- +CREATE TABLE + case_management.wildlife_h ( + h_wildlife_guid uuid NOT NULL DEFAULT uuid_generate_v4 (), + target_row_id uuid NOT NULL, + operation_type char(1) NOT NULL, + operation_user_id varchar(32) NOT NULL DEFAULT current_user, + operation_executed_at timestamp NOT NULL DEFAULT now (), + data_after_executed_operation jsonb, + CONSTRAINT "PK_h_wildlife" PRIMARY KEY (h_wildlife_guid) + ); + +CREATE +or REPLACE TRIGGER wildlife_history_trigger BEFORE INSERT +OR DELETE +OR +UPDATE ON case_management.wildlife FOR EACH ROW EXECUTE PROCEDURE audit_history ('wildlife_h', 'wildlife_guid'); + +COMMENT on table case_management.wildlife_h is 'History table for case_file table'; + +COMMENT on column case_management.wildlife_h.h_wildlife_guid is 'System generated unique key for case history. This key should never be exposed to users via any system utilizing the tables.'; + +COMMENT on column case_management.wildlife_h.target_row_id is 'The unique key for the case that has been created or modified.'; + +COMMENT on column case_management.wildlife_h.operation_type is 'The operation performed: I = Insert, U = Update, D = Delete'; + +COMMENT on column case_management.wildlife_h.operation_user_id is 'The id of the user that created or modified the data in the case table. Defaults to the logged in user if not passed in by the application.'; + +COMMENT on column case_management.wildlife_h.operation_executed_at is 'The timestamp when the data in the case table was created or modified. The timestamp is stored in UTC with no Offset.'; + +COMMENT on column case_management.wildlife_h.data_after_executed_operation is 'A JSON representation of the row in the table after the operation was completed successfully. This implies that the latest row in the audit table will always match with the current row in the live table.'; + +-- +-- CREATE TABLE ear_tag_h +-- +CREATE TABLE + case_management.ear_tag_h ( + h_ear_tag_guid uuid NOT NULL DEFAULT uuid_generate_v4 (), + target_row_id uuid NOT NULL, + operation_type char(1) NOT NULL, + operation_user_id varchar(32) NOT NULL DEFAULT current_user, + operation_executed_at timestamp NOT NULL DEFAULT now (), + data_after_executed_operation jsonb, + CONSTRAINT "PK_h_ear_tag" PRIMARY KEY (h_ear_tag_guid) + ); + +CREATE +or REPLACE TRIGGER ear_tag_history_trigger BEFORE INSERT +OR DELETE +OR +UPDATE ON case_management.ear_tag FOR EACH ROW EXECUTE PROCEDURE audit_history ('ear_tag_h', 'ear_tag_guid'); + +COMMENT on table case_management.ear_tag_h is 'History table for case_file table'; + +COMMENT on column case_management.ear_tag_h.h_ear_tag_guid is 'System generated unique key for case history. This key should never be exposed to users via any system utilizing the tables.'; + +COMMENT on column case_management.ear_tag_h.target_row_id is 'The unique key for the case that has been created or modified.'; + +COMMENT on column case_management.ear_tag_h.operation_type is 'The operation performed: I = Insert, U = Update, D = Delete'; + +COMMENT on column case_management.ear_tag_h.operation_user_id is 'The id of the user that created or modified the data in the case table. Defaults to the logged in user if not passed in by the application.'; + +COMMENT on column case_management.ear_tag_h.operation_executed_at is 'The timestamp when the data in the case table was created or modified. The timestamp is stored in UTC with no Offset.'; + +COMMENT on column case_management.ear_tag_h.data_after_executed_operation is 'A JSON representation of the row in the table after the operation was completed successfully. This implies that the latest row in the audit table will always match with the current row in the live table.'; + +-- +-- CREATE TABLE drug_administered_h +-- +CREATE TABLE + case_management.drug_administered_h ( + h_drug_administered_guid uuid NOT NULL DEFAULT uuid_generate_v4 (), + target_row_id uuid NOT NULL, + operation_type char(1) NOT NULL, + operation_user_id varchar(32) NOT NULL DEFAULT current_user, + operation_executed_at timestamp NOT NULL DEFAULT now (), + data_after_executed_operation jsonb, + CONSTRAINT "PK_h_drug_administered" PRIMARY KEY (h_drug_administered_guid) + ); + +CREATE +or REPLACE TRIGGER drug_administered_history_trigger BEFORE INSERT +OR DELETE +OR +UPDATE ON case_management.drug_administered FOR EACH ROW EXECUTE PROCEDURE audit_history ('drug_administered_h', 'drug_administered_guid'); + +COMMENT on table case_management.drug_administered_h is 'History table for case_file table'; + +COMMENT on column case_management.drug_administered_h.h_drug_administered_guid is 'System generated unique key for case history. This key should never be exposed to users via any system utilizing the tables.'; + +COMMENT on column case_management.drug_administered_h.target_row_id is 'The unique key for the case that has been created or modified.'; + +COMMENT on column case_management.drug_administered_h.operation_type is 'The operation performed: I = Insert, U = Update, D = Delete'; + +COMMENT on column case_management.drug_administered_h.operation_user_id is 'The id of the user that created or modified the data in the case table. Defaults to the logged in user if not passed in by the application.'; + +COMMENT on column case_management.drug_administered_h.operation_executed_at is 'The timestamp when the data in the case table was created or modified. The timestamp is stored in UTC with no Offset.'; + +COMMENT on column case_management.drug_administered_h.data_after_executed_operation is 'A JSON representation of the row in the table after the operation was completed successfully. This implies that the latest row in the audit table will always match with the current row in the live table.'; \ No newline at end of file From 152a934e4929c7e7dad8b0c4862d93f473af094c Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Wed, 24 Apr 2024 18:21:43 -0700 Subject: [PATCH 04/17] feat: added create animal outcome graph types --- backend/src/case_file/case_file.graphql | 50 ++ backend/src/case_file/case_file.resolver.ts | 31 +- backend/src/case_file/case_file.service.ts | 613 ++++++------------ .../dto/wildlife/create-wildlife-input.ts | 15 + .../src/case_file/dto/wildlife/drug-input.ts | 11 + .../case_file/dto/wildlife/ear-tag-input.ts | 5 + .../case_file/dto/wildlife/wildlife-action.ts | 9 + 7 files changed, 321 insertions(+), 413 deletions(-) create mode 100644 backend/src/case_file/dto/wildlife/create-wildlife-input.ts create mode 100644 backend/src/case_file/dto/wildlife/drug-input.ts create mode 100644 backend/src/case_file/dto/wildlife/ear-tag-input.ts create mode 100644 backend/src/case_file/dto/wildlife/wildlife-action.ts diff --git a/backend/src/case_file/case_file.graphql b/backend/src/case_file/case_file.graphql index 5f72261b..96add2bb 100644 --- a/backend/src/case_file/case_file.graphql +++ b/backend/src/case_file/case_file.graphql @@ -195,6 +195,54 @@ input EquipmentActionInput { actionCode: String } +input CreateAnimalOutcomeInput { + leadIdentifier: String! + actor: String! + agencyCode: String! + caseCode: String! + createUserId: String! + wildlife: [WildlifeInput]! +} + +input WildlifeInput { + id: String + categoryLevel: String + conflictHistory: String + sex: String + age: String + outcome: String + species: String! + earTags: [EarTagInput] + drugs: [DrugInput] + actions: [WildlifeActionInput] +} + +input EarTagInput { + id: String + ear: String + value: String +} + +input DrugInput { + id: String + drug: String + administrationMethod: String + fateOfRemaining: String + vialNumber: String + used: String + discarded: String + discardMethod: String + reactions: String +} + +input WildlifeActionInput { + id: String + actor: String + date: Date + actionCode: String + activeIndicator: Boolean +} + type Query { getCaseFile(caseIdentifier: String!): CaseFile getCaseFileByLeadId(leadIdentifier: String!): CaseFile @@ -217,4 +265,6 @@ type Mutation { createEquipment(createEquipmentInput: CreateEquipmentInput!): CaseFile! updateEquipment(updateEquipmentInput: UpdateEquipmentInput!): CaseFile! deleteEquipment(deleteEquipmentInput: DeleteEquipmentInput!): Boolean! + + createAnimal(input: CreateAnimalOutcomeInput!): CaseFile! } diff --git a/backend/src/case_file/case_file.resolver.ts b/backend/src/case_file/case_file.resolver.ts index 08775cad..ed9c1d14 100644 --- a/backend/src/case_file/case_file.resolver.ts +++ b/backend/src/case_file/case_file.resolver.ts @@ -1,7 +1,7 @@ -import { Resolver, Query, Mutation, Args } from '@nestjs/graphql'; -import { CaseFileService } from './case_file.service'; -import { CreateAssessmentInput, CreateEquipmentInput, CreatePreventionInput } from './dto/create-case_file.input'; -import { UpdateAssessmentInput, UpdateEquipmentInput, UpdatePreventionInput } from './dto/update-case_file.input'; +import { Resolver, Query, Mutation, Args } from "@nestjs/graphql"; +import { CaseFileService } from "./case_file.service"; +import { CreateAssessmentInput, CreateEquipmentInput, CreatePreventionInput } from "./dto/create-case_file.input"; +import { UpdateAssessmentInput, UpdateEquipmentInput, UpdatePreventionInput } from "./dto/update-case_file.input"; import { JwtRoleGuard } from "../auth/jwtrole.guard"; import { UseGuards } from "@nestjs/common"; import { Role } from "../enum/role.enum"; @@ -10,7 +10,8 @@ import { ReviewInput } from "./dto/review-input"; import { CreateSupplementalNoteInput } from "./dto/supplemental-note/create-supplemental-note.input"; import { UpdateSupplementalNoteInput } from "./dto/supplemental-note/update-supplemental-note.input"; import { DeleteSupplementalNoteInput } from "./dto/supplemental-note/delete-supplemental-note.input"; -import { DeleteEquipmentInput } from './dto/equipment/delete-equipment.input'; +import { DeleteEquipmentInput } from "./dto/equipment/delete-equipment.input"; +import { CreateWildlifeInput } from "./dto/wildlife/create-wildlife-input"; @UseGuards(JwtRoleGuard) @Resolver("CaseFile") @@ -59,25 +60,25 @@ export class CaseFileResolver { return this.caseFileService.updatePrevention(updatePreventionInput.caseIdentifier, updatePreventionInput); } - @Mutation('createEquipment') + @Mutation("createEquipment") @Roles(Role.COS_OFFICER) - createEquipment(@Args('createEquipmentInput') createEquipmentInput: CreateEquipmentInput) { + createEquipment(@Args("createEquipmentInput") createEquipmentInput: CreateEquipmentInput) { return this.caseFileService.createEquipment(createEquipmentInput); } - @Mutation('updateEquipment') + @Mutation("updateEquipment") @Roles(Role.COS_OFFICER) - updateEquipment(@Args('updateEquipmentInput') updateEquipmentInput: UpdateEquipmentInput) { + updateEquipment(@Args("updateEquipmentInput") updateEquipmentInput: UpdateEquipmentInput) { return this.caseFileService.updateEquipment(updateEquipmentInput); } - @Mutation('deleteEquipment') + @Mutation("deleteEquipment") @Roles(Role.COS_OFFICER) - deleteEquipment(@Args('deleteEquipmentInput') deleteEquipmentInput: DeleteEquipmentInput) { + deleteEquipment(@Args("deleteEquipmentInput") deleteEquipmentInput: DeleteEquipmentInput) { return this.caseFileService.deleteEquipment(deleteEquipmentInput); } - @Mutation('updateReview') + @Mutation("updateReview") @Roles(Role.COS_OFFICER) updateReview(@Args("reviewInput") reviewInput: ReviewInput) { return this.caseFileService.updateReview(reviewInput); @@ -100,4 +101,10 @@ export class CaseFileResolver { deleteNote(@Args("input") input: DeleteSupplementalNoteInput) { return this.caseFileService.deleteNote(input); } + + @Mutation("createAnimal") + @Roles(Role.COS_OFFICER) + createAnimal(@Args("input") input: CreateWildlifeInput) { + return this.caseFileService.createAnimal(input); + } } diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index 8960472d..271d13df 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -1,14 +1,6 @@ import { Injectable, Logger } from "@nestjs/common"; -import { - CreateAssessmentInput, - CreateCaseInput, - CreatePreventionInput, -} from "./dto/create-case_file.input"; -import { - UpdateAssessmentInput, - UpdateEquipmentInput, - UpdatePreventionInput, -} from "./dto/update-case_file.input"; +import { CreateAssessmentInput, CreateCaseInput, CreatePreventionInput } from "./dto/create-case_file.input"; +import { UpdateAssessmentInput, UpdateEquipmentInput, UpdatePreventionInput } from "./dto/update-case_file.input"; import { PrismaService } from "nestjs-prisma"; import { CaseFile } from "./entities/case_file.entity"; import { GraphQLError } from "graphql"; @@ -24,6 +16,7 @@ import { DeleteEquipmentInput } from "./dto/equipment/delete-equipment.input"; import { Prisma, PrismaClient } from "@prisma/client"; import { DefaultArgs } from "@prisma/client/runtime/library"; import { DeleteSupplementalNoteInput } from "./dto/supplemental-note/delete-supplemental-note.input"; +import { CreateWildlifeInput } from "./dto/wildlife/create-wildlife-input"; @Injectable() export class CaseFileService { @@ -40,7 +33,7 @@ export class CaseFileService { PrismaClient, "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" >, - input: CreateCaseInput + input: CreateCaseInput, ): Promise { let caseFileGuid: string; @@ -73,10 +66,7 @@ export class CaseFileService { }, }); } catch (exception) { - throw new GraphQLError( - "Exception occurred. See server log for details", - exception - ); + throw new GraphQLError("Exception occurred. See server log for details", exception); } return caseFileGuid; } @@ -84,20 +74,13 @@ export class CaseFileService { //------------------ //-- assessments //------------------ - async createAssessment( - createAssessmentInput: CreateAssessmentInput - ): Promise { + async createAssessment(createAssessmentInput: CreateAssessmentInput): Promise { const _createAssessmentCase = async ( db: Omit< PrismaClient, - | "$connect" - | "$disconnect" - | "$on" - | "$transaction" - | "$use" - | "$extends" + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" >, - createAssessmentInput: CreateAssessmentInput + createAssessmentInput: CreateAssessmentInput, ): Promise => { let caseFileGuid: string; @@ -109,20 +92,17 @@ export class CaseFileService { agency_code: createAssessmentInput.agencyCode, }, }, - inaction_reason_code_case_file_inaction_reason_codeToinaction_reason_code: - createAssessmentInput.assessmentDetails.actionJustificationCode - ? { - connect: { - inaction_reason_code: - createAssessmentInput.assessmentDetails - .actionJustificationCode, - }, - } - : undefined, + inaction_reason_code_case_file_inaction_reason_codeToinaction_reason_code: createAssessmentInput + .assessmentDetails.actionJustificationCode + ? { + connect: { + inaction_reason_code: createAssessmentInput.assessmentDetails.actionJustificationCode, + }, + } + : undefined, create_user_id: createAssessmentInput.createUserId, create_utc_timestamp: new Date(), - action_not_required_ind: - createAssessmentInput.assessmentDetails.actionNotRequired, + action_not_required_ind: createAssessmentInput.assessmentDetails.actionNotRequired, case_code_case_file_case_codeTocase_code: { connect: { case_code: createAssessmentInput.caseCode, @@ -142,10 +122,7 @@ export class CaseFileService { }, }); } catch (exception) { - throw new GraphQLError( - "Exception occurred. See server log for details", - {} - ); + throw new GraphQLError("Exception occurred. See server log for details", {}); } return caseFileGuid; }; @@ -173,21 +150,19 @@ export class CaseFileService { } for (const action of createAssessmentInput.assessmentDetails.actions) { - let actionTypeActionXref = - await db.action_type_action_xref.findFirstOrThrow({ - where: { - action_type_code: ACTION_TYPE_CODES.COMPASSESS, - action_code: action.actionCode, - }, - select: { - action_type_action_xref_guid: true, - }, - }); + let actionTypeActionXref = await db.action_type_action_xref.findFirstOrThrow({ + where: { + action_type_code: ACTION_TYPE_CODES.COMPASSESS, + action_code: action.actionCode, + }, + select: { + action_type_action_xref_guid: true, + }, + }); await db.action.create({ data: { case_guid: caseFileGuid, - action_type_action_xref_guid: - actionTypeActionXref.action_type_action_xref_guid, + action_type_action_xref_guid: actionTypeActionXref.action_type_action_xref_guid, actor_guid: action.actor, action_date: action.date, active_ind: action.activeIndicator, @@ -199,18 +174,12 @@ export class CaseFileService { }); caseFileOutput = await this.findOne(caseFileGuid); } catch (exception) { - throw new GraphQLError( - "Exception occurred. See server log for details", - {} - ); + throw new GraphQLError("Exception occurred. See server log for details", {}); } return caseFileOutput; } - async updateAssessment( - caseIdentifier: string, - updateAssessmentInput: UpdateAssessmentInput - ) { + async updateAssessment(caseIdentifier: string, updateAssessmentInput: UpdateAssessmentInput) { let caseFileOutput: CaseFile; try { @@ -218,18 +187,15 @@ export class CaseFileService { await db.case_file.update({ where: { case_file_guid: caseIdentifier }, data: { - inaction_reason_code_case_file_inaction_reason_codeToinaction_reason_code: - updateAssessmentInput.assessmentDetails.actionJustificationCode - ? { - connect: { - inaction_reason_code: - updateAssessmentInput.assessmentDetails - .actionJustificationCode, - }, - } - : undefined, - action_not_required_ind: - updateAssessmentInput.assessmentDetails.actionNotRequired, + inaction_reason_code_case_file_inaction_reason_codeToinaction_reason_code: updateAssessmentInput + .assessmentDetails.actionJustificationCode + ? { + connect: { + inaction_reason_code: updateAssessmentInput.assessmentDetails.actionJustificationCode, + }, + } + : undefined, + action_not_required_ind: updateAssessmentInput.assessmentDetails.actionNotRequired, update_user_id: updateAssessmentInput.updateUserId, update_utc_timestamp: new Date(), }, @@ -245,23 +211,21 @@ export class CaseFileService { }); for (const action of updateAssessmentInput.assessmentDetails.actions) { - let actionTypeActionXref = - await this.prisma.action_type_action_xref.findFirstOrThrow({ - where: { - action_type_code: ACTION_TYPE_CODES.COMPASSESS, - action_code: action.actionCode, - }, - select: { - action_type_action_xref_guid: true, - action_code: true, - action_type_code: true, - }, - }); + let actionTypeActionXref = await this.prisma.action_type_action_xref.findFirstOrThrow({ + where: { + action_type_code: ACTION_TYPE_CODES.COMPASSESS, + action_code: action.actionCode, + }, + select: { + action_type_action_xref_guid: true, + action_code: true, + action_type_code: true, + }, + }); let actionXref = await this.prisma.action.findFirst({ where: { - action_type_action_xref_guid: - actionTypeActionXref.action_type_action_xref_guid, + action_type_action_xref_guid: actionTypeActionXref.action_type_action_xref_guid, case_guid: caseIdentifier, }, select: { @@ -273,8 +237,7 @@ export class CaseFileService { await db.action.updateMany({ where: { case_guid: caseIdentifier, - action_type_action_xref_guid: - actionTypeActionXref.action_type_action_xref_guid, + action_type_action_xref_guid: actionTypeActionXref.action_type_action_xref_guid, }, data: { actor_guid: action.actor, @@ -288,8 +251,7 @@ export class CaseFileService { await db.action.create({ data: { case_guid: caseIdentifier, - action_type_action_xref_guid: - actionTypeActionXref.action_type_action_xref_guid, + action_type_action_xref_guid: actionTypeActionXref.action_type_action_xref_guid, actor_guid: action.actor, action_date: action.date, active_ind: action.activeIndicator, @@ -300,8 +262,7 @@ export class CaseFileService { } } - let assessmentCount: number = - updateAssessmentInput.assessmentDetails.actions.length; + let assessmentCount: number = updateAssessmentInput.assessmentDetails.actions.length; if (assessmentCount === 0) { await db.action.updateMany({ where: { case_guid: caseIdentifier }, @@ -312,10 +273,7 @@ export class CaseFileService { caseFileOutput = await this.findOne(caseIdentifier); } catch (exception) { - throw new GraphQLError( - "Exception occurred. See server log for details", - {} - ); + throw new GraphQLError("Exception occurred. See server log for details", {}); } return caseFileOutput; } @@ -323,20 +281,13 @@ export class CaseFileService { //-------------------------- //-- prevention & education //-------------------------- - async createPrevention( - createPreventionInput: CreatePreventionInput - ): Promise { + async createPrevention(createPreventionInput: CreatePreventionInput): Promise { const _createPreventionCase = async ( db: Omit< PrismaClient, - | "$connect" - | "$disconnect" - | "$on" - | "$transaction" - | "$use" - | "$extends" + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" >, - createInput: CreatePreventionInput + createInput: CreatePreventionInput, ): Promise => { let caseFileGuid: string; @@ -369,10 +320,7 @@ export class CaseFileService { }, }); } catch (exception) { - throw new GraphQLError( - "Exception occurred. See server log for details", - {} - ); + throw new GraphQLError("Exception occurred. See server log for details", {}); } return caseFileGuid; }; @@ -384,11 +332,10 @@ export class CaseFileService { await this.prisma.$transaction(async (db) => { caseFileGuid = await _createPreventionCase(db, createPreventionInput); - let action_codes_objects = - await this.prisma.action_type_action_xref.findMany({ - where: { action_type_code: ACTION_TYPE_CODES.COSPRVANDEDU }, - select: { action_code: true }, - }); + let action_codes_objects = await this.prisma.action_type_action_xref.findMany({ + where: { action_type_code: ACTION_TYPE_CODES.COSPRVANDEDU }, + select: { action_code: true }, + }); let action_codes: Array = []; for (const action_code_object of action_codes_objects) { action_codes.push(action_code_object.action_code); @@ -400,21 +347,19 @@ export class CaseFileService { } for (const action of createPreventionInput.preventionDetails.actions) { - let actionTypeActionXref = - await this.prisma.action_type_action_xref.findFirstOrThrow({ - where: { - action_type_code: ACTION_TYPE_CODES.COSPRVANDEDU, - action_code: action.actionCode, - }, - select: { - action_type_action_xref_guid: true, - }, - }); + let actionTypeActionXref = await this.prisma.action_type_action_xref.findFirstOrThrow({ + where: { + action_type_code: ACTION_TYPE_CODES.COSPRVANDEDU, + action_code: action.actionCode, + }, + select: { + action_type_action_xref_guid: true, + }, + }); await db.action.create({ data: { case_guid: caseFileGuid, - action_type_action_xref_guid: - actionTypeActionXref.action_type_action_xref_guid, + action_type_action_xref_guid: actionTypeActionXref.action_type_action_xref_guid, actor_guid: action.actor, action_date: action.date, active_ind: action.activeIndicator, @@ -426,40 +371,32 @@ export class CaseFileService { }); caseFileOutput = await this.findOne(caseFileGuid); } catch (exception) { - throw new GraphQLError( - "Exception occurred. See server log for details", - {} - ); + throw new GraphQLError("Exception occurred. See server log for details", {}); } return caseFileOutput; } - async updatePrevention( - caseIdentifier: string, - updatePreventionInput: UpdatePreventionInput - ) { + async updatePrevention(caseIdentifier: string, updatePreventionInput: UpdatePreventionInput) { let caseFileOutput: CaseFile; try { await this.prisma.$transaction(async (db) => { for (const action of updatePreventionInput.preventionDetails.actions) { - let actionTypeActionXref = - await this.prisma.action_type_action_xref.findFirstOrThrow({ - where: { - action_type_code: ACTION_TYPE_CODES.COSPRVANDEDU, - action_code: action.actionCode, - }, - select: { - action_type_action_xref_guid: true, - action_code: true, - action_type_code: true, - }, - }); + let actionTypeActionXref = await this.prisma.action_type_action_xref.findFirstOrThrow({ + where: { + action_type_code: ACTION_TYPE_CODES.COSPRVANDEDU, + action_code: action.actionCode, + }, + select: { + action_type_action_xref_guid: true, + action_code: true, + action_type_code: true, + }, + }); let actionXref = await this.prisma.action.findFirst({ where: { - action_type_action_xref_guid: - actionTypeActionXref.action_type_action_xref_guid, + action_type_action_xref_guid: actionTypeActionXref.action_type_action_xref_guid, case_guid: caseIdentifier, }, select: { @@ -471,8 +408,7 @@ export class CaseFileService { await db.action.updateMany({ where: { case_guid: caseIdentifier, - action_type_action_xref_guid: - actionTypeActionXref.action_type_action_xref_guid, + action_type_action_xref_guid: actionTypeActionXref.action_type_action_xref_guid, }, data: { actor_guid: action.actor, @@ -486,8 +422,7 @@ export class CaseFileService { await db.action.create({ data: { case_guid: caseIdentifier, - action_type_action_xref_guid: - actionTypeActionXref.action_type_action_xref_guid, + action_type_action_xref_guid: actionTypeActionXref.action_type_action_xref_guid, actor_guid: action.actor, action_date: action.date, active_ind: action.activeIndicator, @@ -497,8 +432,7 @@ export class CaseFileService { }); } } - let preventionCount: number = - updatePreventionInput.preventionDetails.actions.length; + let preventionCount: number = updatePreventionInput.preventionDetails.actions.length; if (preventionCount === 0) { await db.action.updateMany({ where: { case_guid: caseIdentifier }, @@ -508,10 +442,7 @@ export class CaseFileService { }); caseFileOutput = await this.findOne(caseIdentifier); } catch (exception) { - throw new GraphQLError( - "Exception occurred. See server log for details", - {} - ); + throw new GraphQLError("Exception occurred. See server log for details", {}); } return caseFileOutput; } @@ -523,14 +454,9 @@ export class CaseFileService { const _createReviewCase = async ( db: Omit< PrismaClient, - | "$connect" - | "$disconnect" - | "$on" - | "$transaction" - | "$use" - | "$extends" + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" >, - reviewInput: ReviewInput + reviewInput: ReviewInput, ): Promise => { try { let caseFileId: string; @@ -574,33 +500,26 @@ export class CaseFileService { const _createReviewComplete = async ( db: Omit< PrismaClient, - | "$connect" - | "$disconnect" - | "$on" - | "$transaction" - | "$use" - | "$extends" + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" >, - reviewInput: ReviewInput + reviewInput: ReviewInput, ): Promise => { try { let actionId: string; - let actionTypeActionXref = - await this.prisma.action_type_action_xref.findFirstOrThrow({ - where: { - action_type_code: ACTION_TYPE_CODES.CASEACTION, - action_code: ACTION_CODES.COMPLTREVW, - }, - select: { - action_type_action_xref_guid: true, - }, - }); + let actionTypeActionXref = await this.prisma.action_type_action_xref.findFirstOrThrow({ + where: { + action_type_code: ACTION_TYPE_CODES.CASEACTION, + action_code: ACTION_CODES.COMPLTREVW, + }, + select: { + action_type_action_xref_guid: true, + }, + }); const reviewAction = await db.action.create({ data: { case_guid: reviewInput.caseIdentifier, - action_type_action_xref_guid: - actionTypeActionXref.action_type_action_xref_guid, + action_type_action_xref_guid: actionTypeActionXref.action_type_action_xref_guid, actor_guid: reviewInput.reviewComplete.actor, action_date: reviewInput.reviewComplete.date, active_ind: true, //True: review complete, false: review not complete @@ -640,11 +559,7 @@ export class CaseFileService { result.isReviewRequired = caseFile.review_required_ind; //if isReviewRequired && reviewComplete, create reviewComplete action - if ( - reviewInput.isReviewRequired && - reviewInput.reviewComplete && - !reviewInput.reviewComplete.actionId - ) { + if (reviewInput.isReviewRequired && reviewInput.reviewComplete && !reviewInput.reviewComplete.actionId) { const actionId = await _createReviewComplete(db, reviewInput); reviewInput.reviewComplete.actionId = actionId; } @@ -681,9 +596,7 @@ export class CaseFileService { //---------------------- //-- supplemental notes //---------------------- - createNote = async ( - model: CreateSupplementalNoteInput - ): Promise => { + createNote = async (model: CreateSupplementalNoteInput): Promise => { let caseFileId = ""; try { @@ -701,7 +614,6 @@ export class CaseFileService { } await this._upsertNote(db, caseFileId, note, actor, createUserId); - }); result = await this.findOne(caseFileId); @@ -709,14 +621,10 @@ export class CaseFileService { return result; } catch (error) { console.log("exception: unable to create supplemental note", error); - throw new GraphQLError( - "Exception occurred. See server log for details", - {} - ); + throw new GraphQLError("Exception occurred. See server log for details", {}); } }; - updateNote = async (model: UpdateSupplementalNoteInput): Promise => { const { caseIdentifier: caseFileId, actor, note, updateUserId, actionId } = model; @@ -724,20 +632,15 @@ export class CaseFileService { let result: CaseFile; await this.prisma.$transaction(async (db) => { - - const caseId = await this._upsertNote(db, caseFileId, note, actor, updateUserId, actionId); + const caseId = await this._upsertNote(db, caseFileId, note, actor, updateUserId, actionId); - result = await this.findOne(caseId); - + result = await this.findOne(caseId); }); return result; } catch (error) { console.log("exception: unable to update supplemental note", error); - throw new GraphQLError( - "Exception occurred. See server log for details", - {} - ); + throw new GraphQLError("Exception occurred. See server log for details", {}); } }; @@ -856,10 +759,7 @@ export class CaseFileService { return caseId; } catch (error) { console.log("exception: unable to create supplemental note", error); - throw new GraphQLError( - "Exception occurred. See server log for details", - {} - ); + throw new GraphQLError("Exception occurred. See server log for details", {}); } }; @@ -908,16 +808,12 @@ export class CaseFileService { //------------------ //-- equipment //------------------ - async createEquipment( - createEquipmentInput: CreateCaseInput - ): Promise { + async createEquipment(createEquipmentInput: CreateCaseInput): Promise { let caseFileOutput: CaseFile; let caseFileGuid; try { await this.prisma.$transaction(async (db) => { - let caseFile = await this.findOneByLeadId( - createEquipmentInput.leadIdentifier - ); + let caseFile = await this.findOneByLeadId(createEquipmentInput.leadIdentifier); if (caseFile?.caseIdentifier) { caseFileGuid = caseFile.caseIdentifier; @@ -938,9 +834,7 @@ export class CaseFileService { // exclude equipment_geometry_point because prisma can't handle this =gracefully }; - this.logger.debug( - `Creating equipment: ${JSON.stringify(newEquipmentJSON)}` - ); + this.logger.debug(`Creating equipment: ${JSON.stringify(newEquipmentJSON)}`); // create the equipment record const newEquipment = await db.equipment.create({ @@ -957,8 +851,7 @@ export class CaseFileService { // update the equipment record to set the coordinates // using raw query because prisma can't handle the awesomeness - await this.prisma - .$executeRaw`SET search_path TO public, case_management`; + await this.prisma.$executeRaw`SET search_path TO public, case_management`; const geometryUpdateQuery = ` UPDATE case_management.equipment SET equipment_geometry_point = public.ST_GeomFromText($1, 4326) @@ -970,19 +863,12 @@ export class CaseFileService { await db.$executeRawUnsafe( geometryUpdateQuery, pointWKT, // WKT string for the POINT - newEquipment.equipment_guid // UUID of the equipment - ); - this.logger.debug( - `Updated geometry for equipment GUID: ${newEquipment.equipment_guid}` + newEquipment.equipment_guid, // UUID of the equipment ); + this.logger.debug(`Updated geometry for equipment GUID: ${newEquipment.equipment_guid}`); } catch (error) { - this.logger.error( - "An error occurred during the geometry update:", - error - ); - throw new Error( - "Failed to update equipment geometry due to a database error." - ); + this.logger.error("An error occurred during the geometry update:", error); + throw new Error("Failed to update equipment geometry due to a database error."); } } @@ -994,22 +880,20 @@ export class CaseFileService { // get the actions associated with the creation of the equipment. We may be setting an equipment, or setting and removing an equipment for (const action of actions) { - let actionTypeActionXref = - await db.action_type_action_xref.findFirstOrThrow({ - where: { - action_type_code: ACTION_TYPE_CODES.EQUIPMENT, - action_code: action.actionCode, - }, - select: { - action_type_action_xref_guid: true, - }, - }); + let actionTypeActionXref = await db.action_type_action_xref.findFirstOrThrow({ + where: { + action_type_code: ACTION_TYPE_CODES.EQUIPMENT, + action_code: action.actionCode, + }, + select: { + action_type_action_xref_guid: true, + }, + }); // create the action records (this may either be setting an equipment or removing an equipment) const data = { case_guid: caseFileGuid, - action_type_action_xref_guid: - actionTypeActionXref.action_type_action_xref_guid, + action_type_action_xref_guid: actionTypeActionXref.action_type_action_xref_guid, actor_guid: action.actor, action_date: action.date, active_ind: action.activeIndicator, @@ -1018,9 +902,7 @@ export class CaseFileService { equipment_guid: newEquipment.equipment_guid, }; - this.logger.debug( - `Creating new action record for equipment: ${JSON.stringify(data)}` - ); + this.logger.debug(`Creating new action record for equipment: ${JSON.stringify(data)}`); await db.action.create({ data: data, }); @@ -1028,32 +910,21 @@ export class CaseFileService { }); caseFileOutput = await this.findOne(caseFileGuid); } catch (exception) { - this.logger.error( - "An error occurred during equipment creation:", - exception - ); - throw new GraphQLError( - "An error occurred during equipment creation. See server log for details" - ); + this.logger.error("An error occurred during equipment creation:", exception); + throw new GraphQLError("An error occurred during equipment creation. See server log for details"); } return caseFileOutput; } - async updateEquipment( - updateEquipmentInput: UpdateEquipmentInput - ): Promise { + async updateEquipment(updateEquipmentInput: UpdateEquipmentInput): Promise { let caseFileOutput: CaseFile; - let caseFile = await this.findOneByLeadId( - updateEquipmentInput.leadIdentifier - ); + let caseFile = await this.findOneByLeadId(updateEquipmentInput.leadIdentifier); try { await this.prisma.$transaction(async (db) => { // Find the existing equipment record - this.logger.debug( - `Updating equipment ${JSON.stringify(updateEquipmentInput)}` - ); + this.logger.debug(`Updating equipment ${JSON.stringify(updateEquipmentInput)}`); // we're updating a single equipment record, so only one equipment was provided. const equipmentRecord = updateEquipmentInput.equipment[0]; @@ -1073,9 +944,7 @@ export class CaseFileService { active_ind: equipmentRecord.actionEquipmentTypeActiveIndicator, }; - this.logger.debug( - `Equipment record being updated: ${JSON.stringify(data)}` - ); + this.logger.debug(`Equipment record being updated: ${JSON.stringify(data)}`); // Update the equipment record await db.equipment.update({ @@ -1089,15 +958,11 @@ export class CaseFileService { // prisma doesn't handle geometry types, so we have to create this as a string and insert it const xCoordinate = updateEquipmentInput.equipment[0].xCoordinate; const yCoordinate = updateEquipmentInput.equipment[0].yCoordinate; - const pointWKT = - xCoordinate && yCoordinate - ? `POINT(${xCoordinate} ${yCoordinate})` - : null; + const pointWKT = xCoordinate && yCoordinate ? `POINT(${xCoordinate} ${yCoordinate})` : null; // update the equipment record to set the coordinates // using raw query because prisma can't handle the awesomeness - await this.prisma - .$executeRaw`SET search_path TO public, case_management`; + await this.prisma.$executeRaw`SET search_path TO public, case_management`; const geometryUpdateQuery = ` UPDATE case_management.equipment SET equipment_geometry_point = public.ST_GeomFromText($1, 4326) @@ -1109,28 +974,19 @@ export class CaseFileService { await db.$executeRawUnsafe( geometryUpdateQuery, pointWKT, // WKT string for the POINT - equipmentGuid // UUID of the equipment - ); - this.logger.debug( - `Updated geometry for equipment GUID: ${equipmentGuid}` + equipmentGuid, // UUID of the equipment ); + this.logger.debug(`Updated geometry for equipment GUID: ${equipmentGuid}`); } catch (error) { - this.logger.error( - "An error occurred during the geometry update:", - error - ); - throw new Error( - "Failed to update equipment geometry due to a database error." - ); + this.logger.error("An error occurred during the geometry update:", error); + throw new Error("Failed to update equipment geometry due to a database error."); } // Check for updated or added actions const actions = equipmentRecord.actions; for (const action of actions) { if (action.actionGuid) { - this.logger.debug( - `Updating equipment action: ${JSON.stringify(action)}` - ); + this.logger.debug(`Updating equipment action: ${JSON.stringify(action)}`); // If actionGuid exists, it means the action already exists and needs to be updated await db.action.update({ where: { action_guid: action.actionGuid }, @@ -1142,27 +998,23 @@ export class CaseFileService { }); } else { // we're adding a new action, so find the action type action xref needed for this - this.logger.debug( - `Creating new equipment action: ${JSON.stringify(action)}` - ); - let actionTypeActionXref = - await db.action_type_action_xref.findFirstOrThrow({ - where: { - action_type_code: ACTION_TYPE_CODES.EQUIPMENT, - action_code: action.actionCode, - }, - select: { - action_type_action_xref_guid: true, - }, - }); + this.logger.debug(`Creating new equipment action: ${JSON.stringify(action)}`); + let actionTypeActionXref = await db.action_type_action_xref.findFirstOrThrow({ + where: { + action_type_code: ACTION_TYPE_CODES.EQUIPMENT, + action_code: action.actionCode, + }, + select: { + action_type_action_xref_guid: true, + }, + }); this.logger.debug(`Found action xref`); const caseFileGuid = caseFile.caseIdentifier; // create the action records (this may either be setting an equipment or removing an equipment) const data = { case_guid: caseFileGuid, - action_type_action_xref_guid: - actionTypeActionXref.action_type_action_xref_guid, + action_type_action_xref_guid: actionTypeActionXref.action_type_action_xref_guid, actor_guid: action.actor, action_date: action.date, active_ind: action.activeIndicator, @@ -1171,11 +1023,7 @@ export class CaseFileService { equipment_guid: equipmentGuid, }; - this.logger.debug( - `Adding new equipment action as part of an update: ${JSON.stringify( - data - )}` - ); + this.logger.debug(`Adding new equipment action as part of an update: ${JSON.stringify(data)}`); await db.action.create({ data: data, @@ -1188,17 +1036,12 @@ export class CaseFileService { caseFileOutput = await this.findOne(caseFileGuid); } catch (error) { this.logger.error("An error occurred during equipment update:", error); - throw new GraphQLError( - "An error occurred during equipment update. See server log for details", - error - ); + throw new GraphQLError("An error occurred during equipment update. See server log for details", error); } return caseFileOutput; } - async deleteEquipment( - deleteEquipmentInput: DeleteEquipmentInput - ): Promise { + async deleteEquipment(deleteEquipmentInput: DeleteEquipmentInput): Promise { try { // Find the equipment record by its ID const equipment = await this.prisma.equipment.findUnique({ @@ -1208,9 +1051,7 @@ export class CaseFileService { }); if (!equipment) { - throw new Error( - `Equipment with ID ${deleteEquipmentInput.id} not found.` - ); + throw new Error(`Equipment with ID ${deleteEquipmentInput.id} not found.`); } // Update the active_ind field to false @@ -1225,9 +1066,7 @@ export class CaseFileService { }, }); - this.logger.debug( - `Equipment with ID ${deleteEquipmentInput.id} has been updated successfully.` - ); + this.logger.debug(`Equipment with ID ${deleteEquipmentInput.id} has been updated successfully.`); return true; } catch (error) { this.logger.error("Error deleting equipment:", error); @@ -1253,14 +1092,13 @@ export class CaseFileService { action_not_required_ind: true, inaction_reason_code: true, note_text: true, - inaction_reason_code_case_file_inaction_reason_codeToinaction_reason_code: - { - select: { - short_description: true, - long_description: true, - active_ind: true, - }, + inaction_reason_code_case_file_inaction_reason_codeToinaction_reason_code: { + select: { + short_description: true, + long_description: true, + active_ind: true, }, + }, lead: { select: { lead_identifier: true, @@ -1289,15 +1127,14 @@ export class CaseFileService { active_ind: true, }, }, - action_type_code_action_type_action_xref_action_type_codeToaction_type_code: - { - select: { - action_type_code: true, - short_description: true, - long_description: true, - active_ind: true, - }, + action_type_code_action_type_action_xref_action_type_codeToaction_type_code: { + select: { + action_type_code: true, + short_description: true, + long_description: true, + active_ind: true, }, + }, }, }, }, @@ -1313,8 +1150,7 @@ export class CaseFileService { lead, action_not_required_ind: actionNotRequired, inaction_reason_code: inactionReasonCode, - inaction_reason_code_case_file_inaction_reason_codeToinaction_reason_code: - reason, + inaction_reason_code_case_file_inaction_reason_codeToinaction_reason_code: reason, review_required_ind: isReviewRequired, } = queryResult; @@ -1323,7 +1159,7 @@ export class CaseFileService { const reviewCompleteAction = await this.getCaseAction( queryResult.action, ACTION_TYPE_CODES.CASEACTION, - ACTION_CODES.COMPLTREVW + ACTION_CODES.COMPLTREVW, ); const caseFile: CaseFile = { @@ -1332,33 +1168,19 @@ export class CaseFileService { assessmentDetails: { actionNotRequired: actionNotRequired, actionJustificationCode: inactionReasonCode, - actionJustificationShortDescription: !reason - ? "" - : reason.short_description, - actionJustificationLongDescription: !reason - ? "" - : reason.long_description, + actionJustificationShortDescription: !reason ? "" : reason.short_description, + actionJustificationLongDescription: !reason ? "" : reason.long_description, actionJustificationActiveIndicator: !reason ? false : reason.active_ind, - actions: await this.getCaseActions( - queryResult.action, - ACTION_TYPE_CODES.COMPASSESS - ), + actions: await this.getCaseActions(queryResult.action, ACTION_TYPE_CODES.COMPASSESS), }, preventionDetails: { - actions: await this.getCaseActions( - queryResult.action, - ACTION_TYPE_CODES.COSPRVANDEDU - ), + actions: await this.getCaseActions(queryResult.action, ACTION_TYPE_CODES.COSPRVANDEDU), }, isReviewRequired: isReviewRequired, reviewComplete: reviewCompleteAction ?? null, note: { note: queryResult.note_text, - action: await this.getCaseAction( - queryResult.action, - ACTION_TYPE_CODES.CASEACTION, - ACTION_CODES.UPDATENOTE - ), + action: await this.getCaseAction(queryResult.action, ACTION_TYPE_CODES.CASEACTION, ACTION_CODES.UPDATENOTE), }, equipment: equipmentDetails, }; @@ -1392,7 +1214,7 @@ export class CaseFileService { private getCaseActions = async ( actions: Array, actionTypeCode: string, - actionCode: string = "" + actionCode: string = "", ): Promise> => { let items = []; @@ -1400,8 +1222,7 @@ export class CaseFileService { items = actions.filter((action) => { const { action_type_action_xref: { - action_type_code_action_type_action_xref_action_type_codeToaction_type_code: - _actionTypeCode, + action_type_code_action_type_action_xref_action_type_codeToaction_type_code: _actionTypeCode, }, } = action; @@ -1411,17 +1232,12 @@ export class CaseFileService { items = actions.filter((action) => { const { action_type_action_xref: { - action_code_action_type_action_xref_action_codeToaction_code: - _actionCode, - action_type_code_action_type_action_xref_action_type_codeToaction_type_code: - _actionTypeCode, + action_code_action_type_action_xref_action_codeToaction_code: _actionCode, + action_type_code_action_type_action_xref_action_type_codeToaction_type_code: _actionTypeCode, }, } = action; - return ( - _actionTypeCode.action_type_code === actionTypeCode && - _actionCode.action_code === actionCode - ); + return _actionTypeCode.action_type_code === actionTypeCode && _actionCode.action_code === actionCode; }); } @@ -1448,7 +1264,7 @@ export class CaseFileService { longDescription, activeIndicator, } as Action; - } + }, ); return result; }; @@ -1458,7 +1274,7 @@ export class CaseFileService { private getCaseAction = async ( actions: Array, actionTypeCode: string, - actionCode: string = "" + actionCode: string = "", ): Promise => { let item: CaseFileActionItem; @@ -1466,8 +1282,7 @@ export class CaseFileService { item = actions.find((action) => { const { action_type_action_xref: { - action_type_code_action_type_action_xref_action_type_codeToaction_type_code: - _actionTypeCode, + action_type_code_action_type_action_xref_action_type_codeToaction_type_code: _actionTypeCode, }, } = action; @@ -1477,17 +1292,12 @@ export class CaseFileService { item = actions.find((action) => { const { action_type_action_xref: { - action_code_action_type_action_xref_action_codeToaction_code: - _actionCode, - action_type_code_action_type_action_xref_action_type_codeToaction_type_code: - _actionTypeCode, + action_code_action_type_action_xref_action_codeToaction_code: _actionCode, + action_type_code_action_type_action_xref_action_type_codeToaction_type_code: _actionTypeCode, }, } = action; - return ( - _actionTypeCode.action_type_code === actionTypeCode && - _actionCode.action_code === actionCode - ); + return _actionTypeCode.action_type_code === actionTypeCode && _actionCode.action_code === actionCode; }); } @@ -1521,9 +1331,7 @@ export class CaseFileService { // find all equipment records, and their respective actions, for a given case // Since we want to list the equipment related to a case, rather than the actions for a case, which may contain equipment, let's // transform the actions with equipment to equipment with actions. - private findEquipmentDetails = async ( - caseIdentifier: string - ): Promise => { + private findEquipmentDetails = async (caseIdentifier: string): Promise => { const actions = await this.prisma.action.findMany({ where: { case_guid: caseIdentifier }, include: { @@ -1560,9 +1368,7 @@ export class CaseFileService { // get the action xref for the action let actionData = actionCodes.find( - (element) => - element.action_type_action_xref_guid === - action.action_type_action_xref_guid + (element) => element.action_type_action_xref_guid === action.action_type_action_xref_guid, ); if (equipment && equipment.active_ind) { @@ -1570,22 +1376,17 @@ export class CaseFileService { // Parse the geometry string into a GeoJSON object // Correctly setting the search path using Prisma - await this.prisma - .$executeRaw`SET search_path TO public, case_management`; + await this.prisma.$executeRaw`SET search_path TO public, case_management`; // get the latitude and longitude using a raw query - const result = await this.prisma.$queryRaw< - { longitude: number; latitude: number }[] - >` + const result = await this.prisma.$queryRaw<{ longitude: number; latitude: number }[]>` SELECT public.st_x(equipment_geometry_point::geometry) AS longitude, public.st_y(equipment_geometry_point::geometry) AS latitude FROM ${Prisma.raw("case_management.equipment")} WHERE - equipment_guid = ${Prisma.raw( - `'${equipment.equipment_guid}'::uuid` - )} + equipment_guid = ${Prisma.raw(`'${equipment.equipment_guid}'::uuid`)} `; const { longitude, latitude } = result[0]; @@ -1621,20 +1422,30 @@ export class CaseFileService { equipmentDetailsMap.set(equipment.equipment_guid, equipmentDetail); } } - const equipmentDetails = Array.from( - equipmentDetailsMap.values() - ) as Equipment[]; + const equipmentDetails = Array.from(equipmentDetailsMap.values()) as Equipment[]; // Sort the equipmentDetails by createDate in ascending order equipmentDetails.sort((a, b) => { - return ( - new Date(a.createDate).getTime() - new Date(b.createDate).getTime() - ); + return new Date(a.createDate).getTime() - new Date(b.createDate).getTime(); }); return equipmentDetails; }; + //---------------------- + //-- animal outcomes + //---------------------- + createAnimal = async (model: CreateWildlifeInput): Promise => { + let caseFileId = ""; + + try { + throw new Error("Method not implemented."); + } catch (error) { + console.log("exception: unable to create supplemental note", error); + throw new GraphQLError("Exception occurred. See server log for details", {}); + } + }; + //-- //-- not implemented //-- diff --git a/backend/src/case_file/dto/wildlife/create-wildlife-input.ts b/backend/src/case_file/dto/wildlife/create-wildlife-input.ts new file mode 100644 index 00000000..2a32ac4d --- /dev/null +++ b/backend/src/case_file/dto/wildlife/create-wildlife-input.ts @@ -0,0 +1,15 @@ +import { DrugInput } from "./drug-input"; +import { EarTagInput } from "./ear-tag-input"; +import { WildlifeAction } from "./wildlife-action"; + +export interface CreateWildlifeInput { + categoryLevel?: string; + conflictHistory?: string; + sex?: string; + age?: string; + outcome?: string; + species: string; + earTags?: Array; + drugs?: Array; + actions?: Array; //-- this should be refactored +} diff --git a/backend/src/case_file/dto/wildlife/drug-input.ts b/backend/src/case_file/dto/wildlife/drug-input.ts new file mode 100644 index 00000000..85440fb8 --- /dev/null +++ b/backend/src/case_file/dto/wildlife/drug-input.ts @@ -0,0 +1,11 @@ +export interface DrugInput { + id: string; + drug: string; + administrationMethod: string; + fateOfRemaining: string; + vialNumber: string; + used: string; + discarded: string; + discardMethod: string; + reactions: string; +} diff --git a/backend/src/case_file/dto/wildlife/ear-tag-input.ts b/backend/src/case_file/dto/wildlife/ear-tag-input.ts new file mode 100644 index 00000000..c7dc15c3 --- /dev/null +++ b/backend/src/case_file/dto/wildlife/ear-tag-input.ts @@ -0,0 +1,5 @@ +export interface EarTagInput { + id: string; + ear: string; + value: string; +} diff --git a/backend/src/case_file/dto/wildlife/wildlife-action.ts b/backend/src/case_file/dto/wildlife/wildlife-action.ts new file mode 100644 index 00000000..13739370 --- /dev/null +++ b/backend/src/case_file/dto/wildlife/wildlife-action.ts @@ -0,0 +1,9 @@ +import { UUID } from "crypto"; + +export interface WildlifeAction { + id: UUID; + actor: string; + date: Date; + actionCode: string; + activeIndicator: boolean; +} From f8c43fdbe7c5a402577dadb16a62093bad766745 Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Wed, 24 Apr 2024 18:35:32 -0700 Subject: [PATCH 05/17] feat: creating initial create mutation --- backend/src/case_file/case_file.service.ts | 10 ++++++++- .../dto/wildlife/create-wildlife-input.ts | 21 +++++++------------ .../case_file/dto/wildlife/wildlife-input.ts | 15 +++++++++++++ 3 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 backend/src/case_file/dto/wildlife/wildlife-input.ts diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index 271d13df..cc60dbdd 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -1439,7 +1439,15 @@ export class CaseFileService { let caseFileId = ""; try { - throw new Error("Method not implemented."); + let result: CaseFile; + + await this.prisma.$transaction(async (db) => { + const { leadIdentifier, actor, agencyCode, caseCode, createUserId, wildlife } = model; + }); + + result = await this.findOne(caseFileId); + + return result; } catch (error) { console.log("exception: unable to create supplemental note", error); throw new GraphQLError("Exception occurred. See server log for details", {}); diff --git a/backend/src/case_file/dto/wildlife/create-wildlife-input.ts b/backend/src/case_file/dto/wildlife/create-wildlife-input.ts index 2a32ac4d..33afdaa6 100644 --- a/backend/src/case_file/dto/wildlife/create-wildlife-input.ts +++ b/backend/src/case_file/dto/wildlife/create-wildlife-input.ts @@ -1,15 +1,10 @@ -import { DrugInput } from "./drug-input"; -import { EarTagInput } from "./ear-tag-input"; -import { WildlifeAction } from "./wildlife-action"; +import { WildlifeInput } from "./wildlife-input"; -export interface CreateWildlifeInput { - categoryLevel?: string; - conflictHistory?: string; - sex?: string; - age?: string; - outcome?: string; - species: string; - earTags?: Array; - drugs?: Array; - actions?: Array; //-- this should be refactored +export class CreateWildlifeInput { + leadIdentifier: string; + agencyCode: string; + caseCode: string; + createUserId: string; + actor: string; + wildlife: Array; } diff --git a/backend/src/case_file/dto/wildlife/wildlife-input.ts b/backend/src/case_file/dto/wildlife/wildlife-input.ts new file mode 100644 index 00000000..8429da53 --- /dev/null +++ b/backend/src/case_file/dto/wildlife/wildlife-input.ts @@ -0,0 +1,15 @@ +import { DrugInput } from "./drug-input"; +import { EarTagInput } from "./ear-tag-input"; +import { WildlifeAction } from "./wildlife-action"; + +export interface WildlifeInput { + categoryLevel?: string; + conflictHistory?: string; + sex?: string; + age?: string; + outcome?: string; + species: string; + earTags?: Array; + drugs?: Array; + actions?: Array; //-- this should be refactored +} From 6b4697470c1a9fb2fd933d4a3e3324e71c29c64d Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Fri, 3 May 2024 17:13:04 -0700 Subject: [PATCH 06/17] feat: ability to create wildlife outcomes added --- backend/prisma/schema.prisma | 265 +++++++++----- backend/src/case_file/case_file.graphql | 69 +++- backend/src/case_file/case_file.resolver.ts | 8 +- backend/src/case_file/case_file.service.ts | 340 +++++++++++++++++- .../dto/wildlife/create-wildlife-input.ts | 3 +- .../src/case_file/dto/wildlife/drug-input.ts | 16 +- .../case_file/dto/wildlife/ear-tag-input.ts | 2 +- .../case_file/dto/wildlife/wildlife-action.ts | 6 +- .../case_file/dto/wildlife/wildlife-input.ts | 11 +- .../case_file/entities/case_file.entity.ts | 2 + .../src/case_file/entities/wildlife-entity.ts | 32 ++ backend/src/common/action_type_codes.ts | 1 + 12 files changed, 630 insertions(+), 125 deletions(-) create mode 100644 backend/src/case_file/entities/wildlife-entity.ts diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 2f6db25b..d96c6c0e 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -9,75 +9,81 @@ datasource db { } model age_code { - age_code String @id(map: "PK_agecode") @db.VarChar(10) - short_description String @db.VarChar(50) - long_description String? @db.VarChar(250) - display_order Int - active_ind Boolean - create_user_id String @db.VarChar(32) - create_utc_timestamp DateTime @db.Timestamp(6) - update_user_id String @db.VarChar(32) - update_utc_timestamp DateTime @db.Timestamp(6) + age_code String @id(map: "PK_agecode") @db.VarChar(10) + short_description String @db.VarChar(50) + long_description String? @db.VarChar(250) + display_order Int + active_ind Boolean + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + wildlife_wildlife_age_codeToage_code wildlife[] @relation("wildlife_age_codeToage_code") } model conflict_history_code { - conflict_history_code String @id(map: "PK_cnfthistcd") @db.VarChar(10) - short_description String @db.VarChar(50) - long_description String? @db.VarChar(250) - display_order Int - active_ind Boolean - create_user_id String @db.VarChar(32) - create_utc_timestamp DateTime @db.Timestamp(6) - update_user_id String @db.VarChar(32) - update_utc_timestamp DateTime @db.Timestamp(6) + conflict_history_code String @id(map: "PK_cnfthistcd") @db.VarChar(10) + short_description String @db.VarChar(50) + long_description String? @db.VarChar(250) + display_order Int + active_ind Boolean + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + wildlife_wildlife_conflict_history_codeToconflict_history_code wildlife[] @relation("wildlife_conflict_history_codeToconflict_history_code") } model drug_code { - drug_code String @id(map: "PK_drugcode") @db.VarChar(10) - short_description String @db.VarChar(50) - long_description String? @db.VarChar(250) - display_order Int - active_ind Boolean - create_user_id String @db.VarChar(32) - create_utc_timestamp DateTime @db.Timestamp(6) - update_user_id String @db.VarChar(32) - update_utc_timestamp DateTime @db.Timestamp(6) + drug_code String @id(map: "PK_drugcode") @db.VarChar(10) + short_description String @db.VarChar(50) + long_description String? @db.VarChar(250) + display_order Int + active_ind Boolean + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + drug_administered_drug_administered_drug_codeTodrug_code drug_administered[] @relation("drug_administered_drug_codeTodrug_code") } model drug_method_code { - drug_method_code String @id(map: "PK_drgmethdcd") @db.VarChar(10) - short_description String @db.VarChar(50) - long_description String? @db.VarChar(250) - display_order Int - active_ind Boolean - create_user_id String @db.VarChar(32) - create_utc_timestamp DateTime @db.Timestamp(6) - update_user_id String @db.VarChar(32) - update_utc_timestamp DateTime @db.Timestamp(6) + drug_method_code String @id(map: "PK_drgmethdcd") @db.VarChar(10) + short_description String @db.VarChar(50) + long_description String? @db.VarChar(250) + display_order Int + active_ind Boolean + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + drug_administered_drug_administered_drug_method_codeTodrug_method_code drug_administered[] @relation("drug_administered_drug_method_codeTodrug_method_code") } model drug_remaining_outcome_code { - drug_remaining_outcome_code String @id(map: "PK_drgrmotccd") @db.VarChar(10) - short_description String @db.VarChar(50) - long_description String? @db.VarChar(250) - display_order Int - active_ind Boolean - create_user_id String @db.VarChar(32) - create_utc_timestamp DateTime @db.Timestamp(6) - update_user_id String @db.VarChar(32) - update_utc_timestamp DateTime @db.Timestamp(6) + drug_remaining_outcome_code String @id(map: "PK_drgrmotccd") @db.VarChar(10) + short_description String @db.VarChar(50) + long_description String? @db.VarChar(250) + display_order Int + active_ind Boolean + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + drug_administered_drug_administered_drug_remaining_outcome_codeTodrug_remaining_outcome_code drug_administered[] @relation("drug_administered_drug_remaining_outcome_codeTodrug_remaining_outcome_code") } model ear_code { - ear_code String @id(map: "PK_earcode") @db.VarChar(10) - short_description String @db.VarChar(50) - long_description String? @db.VarChar(250) - display_order Int - active_ind Boolean - create_user_id String @db.VarChar(32) - create_utc_timestamp DateTime @db.Timestamp(6) - update_user_id String @db.VarChar(32) - update_utc_timestamp DateTime @db.Timestamp(6) + ear_code String @id(map: "PK_earcode") @db.VarChar(10) + short_description String @db.VarChar(50) + long_description String? @db.VarChar(250) + display_order Int + active_ind Boolean + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + ear_tag_ear_tag_ear_codeToear_code ear_tag[] @relation("ear_tag_ear_codeToear_code") } model flyway_schema_history { @@ -96,27 +102,29 @@ model flyway_schema_history { } model hwcr_outcome_code { - hwcr_outcome_code String @id(map: "PK_hwcrotcmcd") @db.VarChar(10) - short_description String @db.VarChar(50) - long_description String? @db.VarChar(250) - display_order Int - active_ind Boolean - create_user_id String @db.VarChar(32) - create_utc_timestamp DateTime @db.Timestamp(6) - update_user_id String @db.VarChar(32) - update_utc_timestamp DateTime @db.Timestamp(6) + hwcr_outcome_code String @id(map: "PK_hwcrotcmcd") @db.VarChar(10) + short_description String @db.VarChar(50) + long_description String? @db.VarChar(250) + display_order Int + active_ind Boolean + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + wildlife_wildlife_hwcr_outcome_codeTohwcr_outcome_code wildlife[] @relation("wildlife_hwcr_outcome_codeTohwcr_outcome_code") } model sex_code { - sex_code String @id(map: "PK_sexcode") @db.VarChar(10) - short_description String @db.VarChar(50) - long_description String? @db.VarChar(250) - display_order Int - active_ind Boolean - create_user_id String @db.VarChar(32) - create_utc_timestamp DateTime @db.Timestamp(6) - update_user_id String @db.VarChar(32) - update_utc_timestamp DateTime @db.Timestamp(6) + sex_code String @id(map: "PK_sexcode") @db.VarChar(10) + short_description String @db.VarChar(50) + long_description String? @db.VarChar(250) + display_order Int + active_ind Boolean + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + wildlife_wildlife_sex_codeTosex_code wildlife[] @relation("wildlife_sex_codeTosex_code") } model equipment_code { @@ -133,15 +141,16 @@ model equipment_code { } model threat_level_code { - threat_level_code String @id(map: "PK_thrtlvlcd") @db.VarChar(10) - short_description String @db.VarChar(50) - long_description String? @db.VarChar(250) - display_order Int - active_ind Boolean - create_user_id String @db.VarChar(32) - create_utc_timestamp DateTime @db.Timestamp(6) - update_user_id String @db.VarChar(32) - update_utc_timestamp DateTime @db.Timestamp(6) + threat_level_code String @id(map: "PK_thrtlvlcd") @db.VarChar(10) + short_description String @db.VarChar(50) + long_description String? @db.VarChar(250) + display_order Int + active_ind Boolean + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + wildlife_wildlife_threat_level_codeTothreat_level_code wildlife[] @relation("wildlife_threat_level_codeTothreat_level_code") } model configuration { @@ -168,9 +177,11 @@ model action { update_user_id String? @db.VarChar(32) update_utc_timestamp DateTime? @db.Timestamp(6) equipment_guid String? @db.Uuid + wildlife_guid String? @db.Uuid case_file case_file @relation(fields: [case_guid], references: [case_file_guid], onDelete: NoAction, onUpdate: NoAction, map: "FK_action__case_guid") action_type_action_xref action_type_action_xref @relation(fields: [action_type_action_xref_guid], references: [action_type_action_xref_guid], onDelete: NoAction, onUpdate: NoAction, map: "FK_action_action_type_action_xref") equipment equipment? @relation(fields: [equipment_guid], references: [equipment_guid], onDelete: NoAction, onUpdate: NoAction, map: "fk_action__equipment_guid") + wildlife wildlife? @relation(fields: [wildlife_guid], references: [wildlife_guid], onDelete: NoAction, onUpdate: NoAction, map: "fk_action__wildlife_guid") } /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments @@ -272,6 +283,7 @@ model case_file { inaction_reason_code_case_file_inaction_reason_codeToinaction_reason_code inaction_reason_code? @relation("case_file_inaction_reason_codeToinaction_reason_code", fields: [inaction_reason_code], references: [inaction_reason_code], onDelete: NoAction, onUpdate: NoAction, map: "FK_case_file__inaction_reason_code") agency_code agency_code @relation(fields: [owned_by_agency_code], references: [agency_code], onDelete: NoAction, onUpdate: NoAction, map: "FK_case_file__owned_by_agency_code") lead lead[] + wildlife wildlife[] } /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments @@ -345,3 +357,94 @@ model equipment { action action[] equipment_code_equipment_equipment_codeToequipment_code equipment_code? @relation("equipment_equipment_codeToequipment_code", fields: [equipment_code], references: [equipment_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_equipment__equipment_code") } + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model drug_administered { + drug_administered_guid String @id(map: "PK_drug_administered_guid") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + wildlife_guid String @db.Uuid + drug_code String @db.VarChar(10) + drug_method_code String @db.VarChar(10) + drug_remaining_outcome_code String @db.VarChar(10) + vial_number String @db.VarChar(50) + drug_used_amount String @db.VarChar(50) + drug_discarded_amount String? @db.VarChar(50) + discard_method_text String? @db.VarChar(250) + adverse_reaction_text String? @db.VarChar(250) + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + drug_code_drug_administered_drug_codeTodrug_code drug_code @relation("drug_administered_drug_codeTodrug_code", fields: [drug_code], references: [drug_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_drug_administered__drug_code") + drug_method_code_drug_administered_drug_method_codeTodrug_method_code drug_method_code @relation("drug_administered_drug_method_codeTodrug_method_code", fields: [drug_method_code], references: [drug_method_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_drug_administered__drug_method_code") + drug_remaining_outcome_code_drug_administered_drug_remaining_outcome_codeTodrug_remaining_outcome_code drug_remaining_outcome_code @relation("drug_administered_drug_remaining_outcome_codeTodrug_remaining_outcome_code", fields: [drug_remaining_outcome_code], references: [drug_remaining_outcome_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_drug_administered__drug_remaining_outcome_code") + wildlife wildlife @relation(fields: [wildlife_guid], references: [wildlife_guid], onDelete: NoAction, onUpdate: NoAction, map: "fk_drug_administered__wildlife_guid") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model drug_administered_h { + h_drug_administered_guid String @id(map: "PK_h_drug_administered") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + target_row_id String @db.Uuid + operation_type String @db.Char(1) + operation_user_id String @default(dbgenerated("CURRENT_USER")) @db.VarChar(32) + operation_executed_at DateTime @default(now()) @db.Timestamp(6) + data_after_executed_operation Json? +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model ear_tag { + ear_tag_guid String @id(map: "PK_ear_tag_guid") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + wildlife_guid String @db.Uuid + ear_code String @db.VarChar(10) + ear_tag_identifier String @db.VarChar(10) + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + ear_code_ear_tag_ear_codeToear_code ear_code @relation("ear_tag_ear_codeToear_code", fields: [ear_code], references: [ear_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_ear_tag__ear_code") + wildlife wildlife @relation(fields: [wildlife_guid], references: [wildlife_guid], onDelete: NoAction, onUpdate: NoAction, map: "fk_ear_tag__wildlife_guid") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model ear_tag_h { + h_ear_tag_guid String @id(map: "PK_h_ear_tag") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + target_row_id String @db.Uuid + operation_type String @db.Char(1) + operation_user_id String @default(dbgenerated("CURRENT_USER")) @db.VarChar(32) + operation_executed_at DateTime @default(now()) @db.Timestamp(6) + data_after_executed_operation Json? +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model wildlife { + wildlife_guid String @id(map: "PK_wildlife_guid") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + case_file_guid String @db.Uuid + threat_level_code String? @db.VarChar(10) + conflict_history_code String? @db.VarChar(10) + sex_code String? @db.VarChar(10) + age_code String? @db.VarChar(10) + hwcr_outcome_code String? @db.VarChar(10) + species_code String @db.VarChar(10) + create_user_id String @db.VarChar(32) + create_utc_timestamp DateTime @db.Timestamp(6) + update_user_id String @db.VarChar(32) + update_utc_timestamp DateTime @db.Timestamp(6) + action action[] + drug_administered drug_administered[] + ear_tag ear_tag[] + age_code_wildlife_age_codeToage_code age_code? @relation("wildlife_age_codeToage_code", fields: [age_code], references: [age_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_wildlife__age_code") + case_file case_file @relation(fields: [case_file_guid], references: [case_file_guid], onDelete: NoAction, onUpdate: NoAction, map: "fk_wildlife__case_file_guid") + conflict_history_code_wildlife_conflict_history_codeToconflict_history_code conflict_history_code? @relation("wildlife_conflict_history_codeToconflict_history_code", fields: [conflict_history_code], references: [conflict_history_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_wildlife__conflict_history_code") + hwcr_outcome_code_wildlife_hwcr_outcome_codeTohwcr_outcome_code hwcr_outcome_code? @relation("wildlife_hwcr_outcome_codeTohwcr_outcome_code", fields: [hwcr_outcome_code], references: [hwcr_outcome_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_wildlife__hwcr_outcome_code") + sex_code_wildlife_sex_codeTosex_code sex_code? @relation("wildlife_sex_codeTosex_code", fields: [sex_code], references: [sex_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_wildlife__sex_code") + threat_level_code_wildlife_threat_level_codeTothreat_level_code threat_level_code? @relation("wildlife_threat_level_codeTothreat_level_code", fields: [threat_level_code], references: [threat_level_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_wildlife__threat_level_code") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model wildlife_h { + h_wildlife_guid String @id(map: "PK_h_wildlife") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + target_row_id String @db.Uuid + operation_type String @db.Char(1) + operation_user_id String @default(dbgenerated("CURRENT_USER")) @db.VarChar(32) + operation_executed_at DateTime @default(now()) @db.Timestamp(6) + data_after_executed_operation Json? +} diff --git a/backend/src/case_file/case_file.graphql b/backend/src/case_file/case_file.graphql index 96add2bb..6fcfb901 100644 --- a/backend/src/case_file/case_file.graphql +++ b/backend/src/case_file/case_file.graphql @@ -9,6 +9,7 @@ type CaseFile { equipment: [EquipmentDetails] isReviewRequired: Boolean reviewComplete: ReviewCompleteAction + subject: [Wildlife] } type ReviewCompleteAction { @@ -195,54 +196,86 @@ input EquipmentActionInput { actionCode: String } -input CreateAnimalOutcomeInput { +input CreateWildlifeInput { leadIdentifier: String! - actor: String! agencyCode: String! caseCode: String! createUserId: String! - wildlife: [WildlifeInput]! + wildlife: WildlifeInput! } input WildlifeInput { id: String - categoryLevel: String - conflictHistory: String + species: String! sex: String age: String + categoryLevel: String + conflictHistory: String outcome: String - species: String! - earTags: [EarTagInput] + tags: [EarTagInput] drugs: [DrugInput] actions: [WildlifeActionInput] } input EarTagInput { id: String - ear: String - value: String + ear: String! + identifier: String! } input DrugInput { id: String - drug: String - administrationMethod: String - fateOfRemaining: String - vialNumber: String - used: String - discarded: String - discardMethod: String + + vial: String! + drug: String! + amountUsed: String! + injectionMethod: String! reactions: String + + remainingUse: String + amountDiscarded: String + discardMethod: String } input WildlifeActionInput { id: String actor: String date: Date - actionCode: String + action: String activeIndicator: Boolean } +type Wildlife { + id: String + species: String! + sex: String + age: String + categoryLevel: String + conflictHistory: String + outcome: String + tags: [EarTag] + drugs: [Drug] + actions: [Action] +} + +type EarTag { + id: String + ear: String + identifier: String +} + +type Drug { + id: String + vial: String + drug: String + amountUsed: String + injectionMethod: String + reactions: String + remainingUse: String + amountDiscarded: String + discardMethod: String +} + type Query { getCaseFile(caseIdentifier: String!): CaseFile getCaseFileByLeadId(leadIdentifier: String!): CaseFile @@ -266,5 +299,5 @@ type Mutation { updateEquipment(updateEquipmentInput: UpdateEquipmentInput!): CaseFile! deleteEquipment(deleteEquipmentInput: DeleteEquipmentInput!): Boolean! - createAnimal(input: CreateAnimalOutcomeInput!): CaseFile! + createWildlife(input: CreateWildlifeInput!): CaseFile! } diff --git a/backend/src/case_file/case_file.resolver.ts b/backend/src/case_file/case_file.resolver.ts index 45f96a0d..0f8ed533 100644 --- a/backend/src/case_file/case_file.resolver.ts +++ b/backend/src/case_file/case_file.resolver.ts @@ -13,7 +13,6 @@ import { DeleteSupplementalNoteInput } from "./dto/supplemental-note/delete-supp import { DeleteEquipmentInput } from "./dto/equipment/delete-equipment.input"; import { CreateWildlifeInput } from "./dto/wildlife/create-wildlife-input"; - @UseGuards(JwtRoleGuard) @Resolver("CaseFile") export class CaseFileResolver { @@ -103,9 +102,10 @@ export class CaseFileResolver { return this.caseFileService.deleteNote(input); } - @Mutation("createAnimal") + @Mutation("createWildlife") @Roles(Role.COS_OFFICER) - createAnimal(@Args("input") input: CreateWildlifeInput) { - return this.caseFileService.createAnimal(input); + createWildlife(@Args("input") input: CreateWildlifeInput) { + console.log("CREATE_WILDLIEF_INPUT: ", input); + return this.caseFileService.createWildlife(input); } } diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index cc60dbdd..173a4598 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -17,6 +17,11 @@ import { Prisma, PrismaClient } from "@prisma/client"; import { DefaultArgs } from "@prisma/client/runtime/library"; import { DeleteSupplementalNoteInput } from "./dto/supplemental-note/delete-supplemental-note.input"; import { CreateWildlifeInput } from "./dto/wildlife/create-wildlife-input"; +import { WildlifeInput } from "./dto/wildlife/wildlife-input"; +import { EarTagInput } from "./dto/wildlife/ear-tag-input"; +import { DrugInput } from "./dto/wildlife/drug-input"; +import { WildlifeAction } from "./dto/wildlife/wildlife-action"; +import { DrugUsed, EarTag, Wildlife } from "./entities/wildlife-entity"; @Injectable() export class CaseFileService { @@ -1142,6 +1147,20 @@ export class CaseFileService { active_ind: true, }, }, + wildlife: { + select: { + wildlife_guid: true, + threat_level_code: true, + conflict_history_code: true, + sex_code: true, + age_code: true, + hwcr_outcome_code: true, + species_code: true, + action: true, + drug_administered: true, + ear_tag: true, + }, + }, }, }); @@ -1162,7 +1181,7 @@ export class CaseFileService { ACTION_CODES.COMPLTREVW, ); - const caseFile: CaseFile = { + let caseFile: CaseFile = { caseIdentifier: caseFileId, leadIdentifier: lead[0].lead_identifier, //this is okay because there will only be one lead for a case... for now. assessmentDetails: { @@ -1183,8 +1202,104 @@ export class CaseFileService { action: await this.getCaseAction(queryResult.action, ACTION_TYPE_CODES.CASEACTION, ACTION_CODES.UPDATENOTE), }, equipment: equipmentDetails, + //-- for now wildlife will populate the subject property + //-- though this may not be the case at a later date }; + const _mapWildlifeToCaseFile = (wildlife: any[]): Wildlife[] => { + return wildlife.map((item) => { + const { + wildlife_guid: id, + species_code: species, + sex_code: sex, + age_code: age, + threat_level_code: categoryLevel, + conflict_history_code: conflictHistory, + hwcr_outcome_code: outcome, + ear_tag, + drug_administered, + action, + } = item; + const record: Wildlife = { + id, + species, + sex, + age, + categoryLevel, + conflictHistory, + outcome, + tags: + ear_tag && ear_tag.length !== 0 + ? ear_tag.map(({ ear_tag_guid: id, ear_code: ear, ear_tag_identifier: identifier }) => { + return { + id, + ear, + identifier, + }; + }) + : [], + drugs: + drug_administered && drug_administered.length !== 0 + ? drug_administered.map( + ({ + drug_administered_guid: id, + vial_number: vial, + drug_code: drug, + drug_used_amount: amountUsed, + drug_method_code: injectionMethod, + adverse_reaction_text: reactions, + drug_remaining_outcome_code: remainingUse, + drug_discarded_amount: amountDiscarded, + discard_method_text: discardMethod, + }) => { + return { + id, + vial, + drug, + amountUsed, + injectionMethod, + reactions, + remainingUse, + amountDiscarded, + discardMethod, + }; + }, + ) + : [], + actions: + action && action.length !== 0 + ? action.map( + ({ + action_guid: actionGuid, + actor_guid: actor, + action_date: date, + action_type_action_xref: xref, + }) => { + //-- the xref contains the action code + const { + action_code_action_type_action_xref_action_codeToaction_code: { action_code: actionCode }, + } = xref; + return { + actionGuid, + actor, + actionCode, + date, + }; + }, + ) + : [], + }; + + return record; + }); + }; + + //-- add the wildlife items to the subject if there + //-- are results to add to the casefile + if (queryResult.wildlife) { + caseFile.subject = _mapWildlifeToCaseFile(queryResult.wildlife); + } + return caseFile; }; @@ -1435,21 +1550,238 @@ export class CaseFileService { //---------------------- //-- animal outcomes //---------------------- - createAnimal = async (model: CreateWildlifeInput): Promise => { + createWildlife = async (model: CreateWildlifeInput): Promise => { let caseFileId = ""; + //-- + //-- creates a new wildlife record and returns the wildlife_guid + //-- + const _addWildlife = async ( + db: Omit< + PrismaClient, + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" + >, + caseId: string, + wildlife: WildlifeInput, + userId: string, + ): Promise => { + try { + const { species } = wildlife; + + let record: any = { + case_file_guid: caseId, + species_code: species, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: new Date(), + update_utc_timestamp: new Date(), + }; + + if (wildlife.sex) { + record = { ...record, sex_code: wildlife.sex }; + } + + if (wildlife.age) { + record = { ...record, age_code: wildlife.age }; + } + + if (wildlife.categoryLevel) { + record = { ...record, threat_level_code: wildlife.categoryLevel }; + } + + if (wildlife.conflictHistory) { + record = { ...record, conflict_history_code: wildlife.conflictHistory }; + } + + if (wildlife.outcome) { + record = { ...record, hwcr_outcome_code: wildlife.outcome }; + } + + const result = await db.wildlife.create({ + data: record, + }); + + return result?.wildlife_guid; + } catch (exception) { + throw new GraphQLError("Exception occurred. See server log for details", exception); + } + }; + + //-- + //-- creates a new ear-tag record for each item tags collection + //-- + const _addEarTags = async ( + db: Omit< + PrismaClient, + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" + >, + wildlifeId: string, + tags: Array, + userId: string, + ) => { + if (tags && tags.length !== 0) { + try { + const records = tags.map(({ ear, identifier }) => { + return { + wildlife_guid: wildlifeId, + ear_code: ear, + ear_tag_identifier: identifier, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: new Date(), + update_utc_timestamp: new Date(), + }; + }); + let result = await db.ear_tag.createMany({ + data: records, + }); + } catch (exception) { + throw new GraphQLError("Exception occurred. See server log for details", exception); + } + } + }; + + //-- + //-- creates a new drug-used record for each item in drugs collection + //-- + const _addDrugsUsed = async ( + db: Omit< + PrismaClient, + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" + >, + wildlifeId: string, + drugs: Array, + userId: string, + ) => { + if (drugs && drugs.length !== 0) { + try { + const records = drugs.map( + ({ + vial: vial_number, + drug: drug_code, + amountUsed: drug_used_amount, + injectionMethod: drug_method_code, + reactions: adverse_reaction_text, + + remainingUse: drug_remaining_outcome_code, + amountDiscarded: drug_discarded_amount, + discardMethod: discard_method_text, + }) => { + return { + wildlife_guid: wildlifeId, + drug_code, + drug_method_code, + drug_remaining_outcome_code, + vial_number, + drug_used_amount, + drug_discarded_amount, + discard_method_text, + adverse_reaction_text, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: new Date(), + update_utc_timestamp: new Date(), + }; + }, + ); + let result = await db.drug_administered.createMany({ + data: records, + }); + } catch (exception) { + throw new GraphQLError("Exception occurred. See server log for details", exception); + } + } + }; + + //-- + //-- adds new actions for the wildlife record + //-- + const _applyActions = async ( + db: Omit< + PrismaClient, + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" + >, + caseId: string, + wildlifeId: string, + actions: Array, + userId: string, + ) => { + if (actions && actions.length !== 0) { + try { + const xrefs = await db.action_type_action_xref.findMany({ + where: { + action_type_code: ACTION_TYPE_CODES.WILDLIFE, + }, + select: { + action_type_action_xref_guid: true, + action_code: true, + }, + }); + + console.log("XREFS: ", xrefs); + console.log("ACTIONS: ", actions); + const records = actions.map(({ actor: actor_guid, date: action_date, action }) => { + const xref = xrefs.find((item) => item.action_code === action); + console.log("XREF: ", xref); + + return { + case_guid: caseId, + wildlife_guid: wildlifeId, + action_type_action_xref_guid: xref.action_type_action_xref_guid, + actor_guid, + action_date, + active_ind: true, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: new Date(), + update_utc_timestamp: new Date(), + }; + }); + let result = await db.action.createMany({ + data: records, + }); + } catch (exception) { + throw new GraphQLError("Exception occurred. See server log for details", exception); + } + } + }; + try { let result: CaseFile; await this.prisma.$transaction(async (db) => { - const { leadIdentifier, actor, agencyCode, caseCode, createUserId, wildlife } = model; + const { leadIdentifier, agencyCode, caseCode, createUserId, wildlife } = model; + const { tags, drugs, actions } = wildlife; + + const caseFile = await this.findOneByLeadId(leadIdentifier); + + if (caseFile && caseFile?.caseIdentifier) { + caseFileId = caseFile.caseIdentifier; + } else { + const caseInput: CreateCaseInput = { ...model }; + caseFileId = await this.createCase(db, caseInput); + } + + //-- add wildlife items + console.log("DERP"); + const wildlifeId = await _addWildlife(db, caseFileId, wildlife, createUserId); + console.log("W_RESULT: ", wildlifeId); + + if (wildlifeId) { + //-- create ear-tags, dru-used and action records + await _addEarTags(db, wildlifeId, tags, createUserId); + await _addDrugsUsed(db, wildlifeId, drugs, createUserId); + await _applyActions(db, caseFileId, wildlifeId, actions, createUserId); + } + + console.log("CASE_FILE_ID: ", caseFileId); }); result = await this.findOne(caseFileId); return result; } catch (error) { - console.log("exception: unable to create supplemental note", error); + console.log("exception: unable to wildlife ", error); throw new GraphQLError("Exception occurred. See server log for details", {}); } }; diff --git a/backend/src/case_file/dto/wildlife/create-wildlife-input.ts b/backend/src/case_file/dto/wildlife/create-wildlife-input.ts index 33afdaa6..3663da0f 100644 --- a/backend/src/case_file/dto/wildlife/create-wildlife-input.ts +++ b/backend/src/case_file/dto/wildlife/create-wildlife-input.ts @@ -5,6 +5,5 @@ export class CreateWildlifeInput { agencyCode: string; caseCode: string; createUserId: string; - actor: string; - wildlife: Array; + wildlife: WildlifeInput; } diff --git a/backend/src/case_file/dto/wildlife/drug-input.ts b/backend/src/case_file/dto/wildlife/drug-input.ts index 85440fb8..9eb968ef 100644 --- a/backend/src/case_file/dto/wildlife/drug-input.ts +++ b/backend/src/case_file/dto/wildlife/drug-input.ts @@ -1,11 +1,13 @@ export interface DrugInput { - id: string; + id?: number; + + vial: string; drug: string; - administrationMethod: string; - fateOfRemaining: string; - vialNumber: string; - used: string; - discarded: string; - discardMethod: string; + amountUsed: string; + injectionMethod: string; reactions: string; + + remainingUse: string; + amountDiscarded: string; + discardMethod: string; } diff --git a/backend/src/case_file/dto/wildlife/ear-tag-input.ts b/backend/src/case_file/dto/wildlife/ear-tag-input.ts index c7dc15c3..7f1f24fb 100644 --- a/backend/src/case_file/dto/wildlife/ear-tag-input.ts +++ b/backend/src/case_file/dto/wildlife/ear-tag-input.ts @@ -1,5 +1,5 @@ export interface EarTagInput { id: string; ear: string; - value: string; + identifier: string; } diff --git a/backend/src/case_file/dto/wildlife/wildlife-action.ts b/backend/src/case_file/dto/wildlife/wildlife-action.ts index 13739370..1916403f 100644 --- a/backend/src/case_file/dto/wildlife/wildlife-action.ts +++ b/backend/src/case_file/dto/wildlife/wildlife-action.ts @@ -1,9 +1,9 @@ import { UUID } from "crypto"; export interface WildlifeAction { - id: UUID; + id?: UUID; actor: string; - date: Date; - actionCode: string; + date?: Date; + action: string; activeIndicator: boolean; } diff --git a/backend/src/case_file/dto/wildlife/wildlife-input.ts b/backend/src/case_file/dto/wildlife/wildlife-input.ts index 8429da53..1e8035d3 100644 --- a/backend/src/case_file/dto/wildlife/wildlife-input.ts +++ b/backend/src/case_file/dto/wildlife/wildlife-input.ts @@ -3,13 +3,14 @@ import { EarTagInput } from "./ear-tag-input"; import { WildlifeAction } from "./wildlife-action"; export interface WildlifeInput { - categoryLevel?: string; - conflictHistory?: string; + id?: string; + species: string; sex?: string; age?: string; + categoryLevel?: string; + conflictHistory?: string; outcome?: string; - species: string; - earTags?: Array; + tags?: Array; drugs?: Array; - actions?: Array; //-- this should be refactored + actions?: Array; } diff --git a/backend/src/case_file/entities/case_file.entity.ts b/backend/src/case_file/entities/case_file.entity.ts index 7a4de509..e0c3496f 100644 --- a/backend/src/case_file/entities/case_file.entity.ts +++ b/backend/src/case_file/entities/case_file.entity.ts @@ -3,6 +3,7 @@ import { Equipment } from "./equipment.entity"; import { Prevention } from "./prevention.entity"; import { ReviewComplete } from "./review_complete"; import { Note } from "./supplemental-note.entity"; +import { Wildlife } from "./wildlife-entity"; export class CaseFile { caseIdentifier?: string; @@ -13,4 +14,5 @@ export class CaseFile { note?: Note; isReviewRequired?: boolean; reviewComplete?: ReviewComplete; + subject?: Array; } diff --git a/backend/src/case_file/entities/wildlife-entity.ts b/backend/src/case_file/entities/wildlife-entity.ts new file mode 100644 index 00000000..82d1fca3 --- /dev/null +++ b/backend/src/case_file/entities/wildlife-entity.ts @@ -0,0 +1,32 @@ +export interface Wildlife { + id: string; + species: string; + sex?: string; + age?: string; + categoryLevel?: string; + conflictHistory?: string; + outcome?: string; + tags: []; + drugs: []; + actions: []; +} + +export interface EarTag { + id: string; + ear: string; + identifier: string; +} + +export interface DrugUsed { + id: string; + + vial: string; + drug: string; + amountUsed: string; + injectionMethod: string; + reactions?: string; + + remainingUse?: string; + amountDiscarded?: string; + discardMethod?: string; +} diff --git a/backend/src/common/action_type_codes.ts b/backend/src/common/action_type_codes.ts index 991c672a..812bb89b 100644 --- a/backend/src/common/action_type_codes.ts +++ b/backend/src/common/action_type_codes.ts @@ -3,4 +3,5 @@ export const ACTION_TYPE_CODES = { COSPRVANDEDU: "COSPRV&EDU", EQUIPMENT: "EQUIPMENT", CASEACTION: "CASEACTION", + WILDLIFE: "WILDLIFE", }; From 6160f8354ff2395f83228ec5ddd108238d3ceb9e Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Tue, 7 May 2024 17:08:46 -0700 Subject: [PATCH 07/17] feat: updates to the backend to return animal outcomes --- backend/src/case_file/case_file.service.ts | 228 ++++++++++-------- .../src/case_file/dto/subject-query-result.ts | 51 ++++ .../src/case_file/entities/wildlife-entity.ts | 8 +- 3 files changed, 190 insertions(+), 97 deletions(-) create mode 100644 backend/src/case_file/dto/subject-query-result.ts diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index 173a4598..083e43d8 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -22,6 +22,7 @@ import { EarTagInput } from "./dto/wildlife/ear-tag-input"; import { DrugInput } from "./dto/wildlife/drug-input"; import { WildlifeAction } from "./dto/wildlife/wildlife-action"; import { DrugUsed, EarTag, Wildlife } from "./entities/wildlife-entity"; +import { SubjectQueryResult } from "./dto/subject-query-result"; @Injectable() export class CaseFileService { @@ -1150,15 +1151,34 @@ export class CaseFileService { wildlife: { select: { wildlife_guid: true, - threat_level_code: true, - conflict_history_code: true, - sex_code: true, + species_code: true, age_code: true, + sex_code: true, + conflict_history_code: true, + threat_level_code: true, hwcr_outcome_code: true, - species_code: true, - action: true, drug_administered: true, ear_tag: true, + action: { + select: { + action_guid: true, + actor_guid: true, + action_date: true, + active_ind: true, + action_type_action_xref: { + select: { + action_code_action_type_action_xref_action_codeToaction_code: { + select: { + action_code: true, + short_description: true, + long_description: true, + active_ind: true, + }, + }, + }, + }, + }, + }, }, }, }, @@ -1206,98 +1226,10 @@ export class CaseFileService { //-- though this may not be the case at a later date }; - const _mapWildlifeToCaseFile = (wildlife: any[]): Wildlife[] => { - return wildlife.map((item) => { - const { - wildlife_guid: id, - species_code: species, - sex_code: sex, - age_code: age, - threat_level_code: categoryLevel, - conflict_history_code: conflictHistory, - hwcr_outcome_code: outcome, - ear_tag, - drug_administered, - action, - } = item; - const record: Wildlife = { - id, - species, - sex, - age, - categoryLevel, - conflictHistory, - outcome, - tags: - ear_tag && ear_tag.length !== 0 - ? ear_tag.map(({ ear_tag_guid: id, ear_code: ear, ear_tag_identifier: identifier }) => { - return { - id, - ear, - identifier, - }; - }) - : [], - drugs: - drug_administered && drug_administered.length !== 0 - ? drug_administered.map( - ({ - drug_administered_guid: id, - vial_number: vial, - drug_code: drug, - drug_used_amount: amountUsed, - drug_method_code: injectionMethod, - adverse_reaction_text: reactions, - drug_remaining_outcome_code: remainingUse, - drug_discarded_amount: amountDiscarded, - discard_method_text: discardMethod, - }) => { - return { - id, - vial, - drug, - amountUsed, - injectionMethod, - reactions, - remainingUse, - amountDiscarded, - discardMethod, - }; - }, - ) - : [], - actions: - action && action.length !== 0 - ? action.map( - ({ - action_guid: actionGuid, - actor_guid: actor, - action_date: date, - action_type_action_xref: xref, - }) => { - //-- the xref contains the action code - const { - action_code_action_type_action_xref_action_codeToaction_code: { action_code: actionCode }, - } = xref; - return { - actionGuid, - actor, - actionCode, - date, - }; - }, - ) - : [], - }; - - return record; - }); - }; - //-- add the wildlife items to the subject if there //-- are results to add to the casefile if (queryResult.wildlife) { - caseFile.subject = _mapWildlifeToCaseFile(queryResult.wildlife); + caseFile.subject = await this.getCaseFileSubjects(queryResult); } return caseFile; @@ -1547,6 +1479,114 @@ export class CaseFileService { return equipmentDetails; }; + //-- get all of the subjects for the case files, this can be wildlife as well + //-- as people + private getCaseFileSubjects = async (query: SubjectQueryResult): Promise => { + let result: Array; + + if (query?.wildlife) { + const { wildlife } = query; + + result = wildlife.map((item) => { + const { + wildlife_guid: id, + species_code: species, + sex_code: sex, + age_code: age, + threat_level_code: categoryLevel, + conflict_history_code: conflictHistory, + hwcr_outcome_code: outcome, + ear_tag, + drug_administered, + action, + } = item; + + const tags = ear_tag.map(({ ear_tag_guid: id, ear_code: ear, ear_tag_identifier: identifier }) => { + return { + id, + ear, + identifier, + }; + }); + + const drugs = drug_administered.map( + ({ + drug_administered_guid: id, + vial_number: vial, + drug_code: drug, + drug_used_amount: amountUsed, + drug_method_code: injectionMethod, + adverse_reaction_text: reactions, + drug_remaining_outcome_code: remainingUse, + drug_discarded_amount: amountDiscarded, + discard_method_text: discardMethod, + }) => { + return { + id, + vial, + drug, + amountUsed, + injectionMethod, + reactions, + remainingUse, + amountDiscarded, + discardMethod, + }; + }, + ); + + const actions = action.map( + ({ action_guid: actionGuid, actor_guid: actor, action_date: date, action_type_action_xref: xref }) => { + //-- the xref contains the action code + const { + action_code_action_type_action_xref_action_codeToaction_code: { + short_description: shortDescription, + long_description: longDescription, + active_ind: activeIndicator, + action_code: actionCode, + }, + } = xref; + return { + actionGuid, + actor, + actionCode, + date, + shortDescription, + longDescription, + activeIndicator, + }; + }, + ); + + let record: Wildlife = { + id, + species, + sex, + age, + categoryLevel, + conflictHistory, + outcome, + }; + + if (tags && tags.length !== 0) { + record = { ...record, tags }; + } + + if (drugs && drugs.length !== 0) { + record = { ...record, drugs }; + } + + if (actions && actions.length !== 0) { + record = { ...record, actions }; + } + + return record; + }); + } + + return result; + }; + //---------------------- //-- animal outcomes //---------------------- diff --git a/backend/src/case_file/dto/subject-query-result.ts b/backend/src/case_file/dto/subject-query-result.ts new file mode 100644 index 00000000..affac297 --- /dev/null +++ b/backend/src/case_file/dto/subject-query-result.ts @@ -0,0 +1,51 @@ +export interface SubjectQueryResult { + wildlife: { + action: { + action_type_action_xref: { + action_code_action_type_action_xref_action_codeToaction_code: { + action_code: string; + short_description: string; + long_description: string; + active_ind: boolean; + }; + }; + active_ind: boolean; + action_guid: string; + actor_guid: string; + action_date: Date; + }[]; + wildlife_guid: string; + threat_level_code: string; + conflict_history_code: string; + sex_code: string; + age_code: string; + hwcr_outcome_code: string; + species_code: string; + drug_administered: { + drug_administered_guid: string; + wildlife_guid: string; + drug_code: string; + drug_method_code: string; + drug_remaining_outcome_code: string; + vial_number: string; + drug_used_amount: string; + drug_discarded_amount: string; + discard_method_text: string; + adverse_reaction_text: string; + create_user_id: string; + create_utc_timestamp: Date; + update_user_id: string; + update_utc_timestamp: Date; + }[]; + ear_tag: { + ear_tag_guid: string; + wildlife_guid: string; + ear_code: string; + ear_tag_identifier: string; + create_user_id: string; + create_utc_timestamp: Date; + update_user_id: string; + update_utc_timestamp: Date; + }[]; + }[]; +} diff --git a/backend/src/case_file/entities/wildlife-entity.ts b/backend/src/case_file/entities/wildlife-entity.ts index 82d1fca3..842a818f 100644 --- a/backend/src/case_file/entities/wildlife-entity.ts +++ b/backend/src/case_file/entities/wildlife-entity.ts @@ -1,3 +1,5 @@ +import { Action } from "./case-action.entity"; + export interface Wildlife { id: string; species: string; @@ -6,9 +8,9 @@ export interface Wildlife { categoryLevel?: string; conflictHistory?: string; outcome?: string; - tags: []; - drugs: []; - actions: []; + tags?: Array; + drugs?: Array; + actions?: Array; } export interface EarTag { From 4d831e1bdb3f6cc960ee11d06fb0f52b2724d536 Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Thu, 9 May 2024 16:16:43 -0700 Subject: [PATCH 08/17] feat: delete wildlife enabled, updated tables delete wildlife functionality added, updated wildlife, ear_tag and drug_administered tables to include missing active_ind column --- backend/package-lock.json | 2 +- backend/prisma/schema.prisma | 3 + backend/src/case_file/case_file.graphql | 8 ++ backend/src/case_file/case_file.resolver.ts | 8 +- backend/src/case_file/case_file.service.ts | 75 ++++++++++++++++++- .../dto/wildlife/delete-wildlife-input.ts | 6 ++ ...V1.18.1__CE-358-animal-outcome-updates.sql | 29 +++++++ 7 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 backend/src/case_file/dto/wildlife/delete-wildlife-input.ts create mode 100644 migrations/sql/V1.18.1__CE-358-animal-outcome-updates.sql diff --git a/backend/package-lock.json b/backend/package-lock.json index b6bcc773..8f80fac7 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,5 +1,5 @@ { - "name": "backend", + "name": "app", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index d96c6c0e..91c5504a 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -374,6 +374,7 @@ model drug_administered { create_utc_timestamp DateTime @db.Timestamp(6) update_user_id String @db.VarChar(32) update_utc_timestamp DateTime @db.Timestamp(6) + active_ind Boolean? drug_code_drug_administered_drug_codeTodrug_code drug_code @relation("drug_administered_drug_codeTodrug_code", fields: [drug_code], references: [drug_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_drug_administered__drug_code") drug_method_code_drug_administered_drug_method_codeTodrug_method_code drug_method_code @relation("drug_administered_drug_method_codeTodrug_method_code", fields: [drug_method_code], references: [drug_method_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_drug_administered__drug_method_code") drug_remaining_outcome_code_drug_administered_drug_remaining_outcome_codeTodrug_remaining_outcome_code drug_remaining_outcome_code @relation("drug_administered_drug_remaining_outcome_codeTodrug_remaining_outcome_code", fields: [drug_remaining_outcome_code], references: [drug_remaining_outcome_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_drug_administered__drug_remaining_outcome_code") @@ -400,6 +401,7 @@ model ear_tag { create_utc_timestamp DateTime @db.Timestamp(6) update_user_id String @db.VarChar(32) update_utc_timestamp DateTime @db.Timestamp(6) + active_ind Boolean? ear_code_ear_tag_ear_codeToear_code ear_code @relation("ear_tag_ear_codeToear_code", fields: [ear_code], references: [ear_code], onDelete: NoAction, onUpdate: NoAction, map: "fk_ear_tag__ear_code") wildlife wildlife @relation(fields: [wildlife_guid], references: [wildlife_guid], onDelete: NoAction, onUpdate: NoAction, map: "fk_ear_tag__wildlife_guid") } @@ -428,6 +430,7 @@ model wildlife { create_utc_timestamp DateTime @db.Timestamp(6) update_user_id String @db.VarChar(32) update_utc_timestamp DateTime @db.Timestamp(6) + active_ind Boolean? action action[] drug_administered drug_administered[] ear_tag ear_tag[] diff --git a/backend/src/case_file/case_file.graphql b/backend/src/case_file/case_file.graphql index 6fcfb901..4a51a1e3 100644 --- a/backend/src/case_file/case_file.graphql +++ b/backend/src/case_file/case_file.graphql @@ -146,6 +146,13 @@ input DeleteSupplementalNoteInput { actionId: String! } +input DeleteWildlifeInput { + caseIdentifier: String! + wildlifeId: String! + actor: String! + updateUserId: String! +} + type EquipmentDetails { id: String typeCode: String @@ -300,4 +307,5 @@ type Mutation { deleteEquipment(deleteEquipmentInput: DeleteEquipmentInput!): Boolean! createWildlife(input: CreateWildlifeInput!): CaseFile! + deleteWildlife(input: DeleteWildlifeInput!): CaseFile! } diff --git a/backend/src/case_file/case_file.resolver.ts b/backend/src/case_file/case_file.resolver.ts index 0f8ed533..205fe403 100644 --- a/backend/src/case_file/case_file.resolver.ts +++ b/backend/src/case_file/case_file.resolver.ts @@ -12,6 +12,7 @@ import { UpdateSupplementalNoteInput } from "./dto/supplemental-note/update-supp import { DeleteSupplementalNoteInput } from "./dto/supplemental-note/delete-supplemental-note.input"; import { DeleteEquipmentInput } from "./dto/equipment/delete-equipment.input"; import { CreateWildlifeInput } from "./dto/wildlife/create-wildlife-input"; +import { DeleteWildlifeInput } from "./dto/wildlife/delete-wildlife-input"; @UseGuards(JwtRoleGuard) @Resolver("CaseFile") @@ -105,7 +106,12 @@ export class CaseFileResolver { @Mutation("createWildlife") @Roles(Role.COS_OFFICER) createWildlife(@Args("input") input: CreateWildlifeInput) { - console.log("CREATE_WILDLIEF_INPUT: ", input); return this.caseFileService.createWildlife(input); } + + @Mutation("deleteWildlife") + @Roles(Role.COS_OFFICER) + deleteWildlife(@Args("input") input: DeleteWildlifeInput) { + return this.caseFileService.deleteWildlife(input); + } } diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index 083e43d8..368ba958 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -23,6 +23,7 @@ import { DrugInput } from "./dto/wildlife/drug-input"; import { WildlifeAction } from "./dto/wildlife/wildlife-action"; import { DrugUsed, EarTag, Wildlife } from "./entities/wildlife-entity"; import { SubjectQueryResult } from "./dto/subject-query-result"; +import { DeleteWildlifeInput } from "./dto/wildlife/delete-wildlife-input"; @Injectable() export class CaseFileService { @@ -1149,6 +1150,9 @@ export class CaseFileService { }, }, wildlife: { + where: { + OR: [{ active_ind: true }, { active_ind: null }], + }, select: { wildlife_guid: true, species_code: true, @@ -1611,6 +1615,7 @@ export class CaseFileService { let record: any = { case_file_guid: caseId, species_code: species, + active_ind: true, create_user_id: userId, update_user_id: userId, create_utc_timestamp: new Date(), @@ -1803,9 +1808,7 @@ export class CaseFileService { } //-- add wildlife items - console.log("DERP"); const wildlifeId = await _addWildlife(db, caseFileId, wildlife, createUserId); - console.log("W_RESULT: ", wildlifeId); if (wildlifeId) { //-- create ear-tags, dru-used and action records @@ -1813,8 +1816,6 @@ export class CaseFileService { await _addDrugsUsed(db, wildlifeId, drugs, createUserId); await _applyActions(db, caseFileId, wildlifeId, actions, createUserId); } - - console.log("CASE_FILE_ID: ", caseFileId); }); result = await this.findOne(caseFileId); @@ -1826,6 +1827,72 @@ export class CaseFileService { } }; + deleteWildlife = async (input: DeleteWildlifeInput): Promise => { + const { caseIdentifier, wildlifeId, actor, updateUserId: userId } = input; + const current = new Date(); + + const softDeleteFragment = { active_ind: false, update_user_id: userId, update_utc_timestamp: current }; + console.log("DELETE WILDLIFE"); + try { + await this.prisma.$transaction(async (db) => { + //-- find the wildlife entry to delete + const wildlife = await db.wildlife.findUnique({ + where: { + case_file_guid: caseIdentifier, + wildlife_guid: wildlifeId, + }, + }); + + if (!wildlife) { + throw new Error(`Wildlife with ID ${wildlifeId} not found.`); + } else { + console.log("DELETE WILDLIFE"); + } + + //-- soft delete ear_tags + const tags = await db.ear_tag.findMany({ where: { wildlife_guid: wildlifeId } }); + if (tags && tags.length !== 0) { + await db.ear_tag.updateMany({ + where: { wildlife_guid: wildlifeId }, + data: softDeleteFragment, + }); + } else { + console.log("NO EAR_TAGS"); + } + + //-- soft delete drugs_administered + const drugs = await db.drug_administered.findMany({ where: { wildlife_guid: wildlifeId } }); + if (drugs && drugs.length !== 0) { + await db.drug_administered.updateMany({ + where: { wildlife_guid: wildlifeId }, + data: softDeleteFragment, + }); + } else { + console.log("NO DRUGS"); + } + + //-- soft delete wildlife record + await db.wildlife.update({ where: { wildlife_guid: wildlifeId }, data: softDeleteFragment }); + + //-- if there are actions perform soft delete + const actions = await db.action.findMany({ where: { case_guid: caseIdentifier, wildlife_guid: wildlifeId } }); + if (actions && actions.length !== 0) { + await db.wildlife.updateMany({ + where: { wildlife_guid: wildlifeId }, + data: softDeleteFragment, + }); + } else { + console.log("NO ACTIONS"); + } + }); + + return await this.findOne(caseIdentifier); + } catch (error) { + console.log("exception: unable to delete supplemental note", error); + throw new GraphQLError("Exception occurred. See server log for details", {}); + } + }; + //-- //-- not implemented //-- diff --git a/backend/src/case_file/dto/wildlife/delete-wildlife-input.ts b/backend/src/case_file/dto/wildlife/delete-wildlife-input.ts new file mode 100644 index 00000000..d430d486 --- /dev/null +++ b/backend/src/case_file/dto/wildlife/delete-wildlife-input.ts @@ -0,0 +1,6 @@ +export interface DeleteWildlifeInput { + caseIdentifier: string; + wildlifeId: string; + actor?: string; + updateUserId: string; +} diff --git a/migrations/sql/V1.18.1__CE-358-animal-outcome-updates.sql b/migrations/sql/V1.18.1__CE-358-animal-outcome-updates.sql new file mode 100644 index 00000000..a5519c54 --- /dev/null +++ b/migrations/sql/V1.18.1__CE-358-animal-outcome-updates.sql @@ -0,0 +1,29 @@ +-- +-- ALTER TABLE wildlife +-- add missing active_ind column +-- +ALTER TABLE IF EXISTS case_management.wildlife +ADD COLUMN IF NOT EXISTS active_ind boolean; + +--comment +COMMENT ON COLUMN case_management.wildlife.active_ind is 'A boolean indicator to determine if the wildlife record is active.'; + +-- +-- ALTER TABLE ear_tag +-- add missing active_ind column +-- +ALTER TABLE IF EXISTS case_management.ear_tag +ADD COLUMN IF NOT EXISTS active_ind boolean; + +--comment +COMMENT ON COLUMN case_management.ear_tag.active_ind is 'A boolean indicator to determine if the ear_tag record is active.'; + +-- +-- ALTER TABLE drug_administered +-- add missing active_ind column +-- +ALTER TABLE IF EXISTS case_management.drug_administered +ADD COLUMN IF NOT EXISTS active_ind boolean; + +--comment +COMMENT ON COLUMN case_management.drug_administered.active_ind is 'A boolean indicator to determine if the drug_administered record is active.'; \ No newline at end of file From b459fdb37ea75d75a9272f0ef89a6b75d9c6f5ac Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Thu, 9 May 2024 19:13:19 -0700 Subject: [PATCH 09/17] feat: added update mutation --- backend/src/case_file/case_file.graphql | 9 +++++++++ backend/src/case_file/case_file.service.ts | 9 +++++++-- .../src/case_file/dto/wildlife/update-wildlife-input.ts | 9 +++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 backend/src/case_file/dto/wildlife/update-wildlife-input.ts diff --git a/backend/src/case_file/case_file.graphql b/backend/src/case_file/case_file.graphql index 4a51a1e3..98037c50 100644 --- a/backend/src/case_file/case_file.graphql +++ b/backend/src/case_file/case_file.graphql @@ -211,6 +211,14 @@ input CreateWildlifeInput { wildlife: WildlifeInput! } +input UpdateWildlifeInput { + caseIdentifier: String! + agencyCode: String! + caseCode: String! + updateUserId: String! + wildlife: WildlifeInput! +} + input WildlifeInput { id: String species: String! @@ -307,5 +315,6 @@ type Mutation { deleteEquipment(deleteEquipmentInput: DeleteEquipmentInput!): Boolean! createWildlife(input: CreateWildlifeInput!): CaseFile! + updateWildlife(input: UpdateWildlifeInput!): CaseFile! deleteWildlife(input: DeleteWildlifeInput!): CaseFile! } diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index 368ba958..001a7957 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -24,6 +24,7 @@ import { WildlifeAction } from "./dto/wildlife/wildlife-action"; import { DrugUsed, EarTag, Wildlife } from "./entities/wildlife-entity"; import { SubjectQueryResult } from "./dto/subject-query-result"; import { DeleteWildlifeInput } from "./dto/wildlife/delete-wildlife-input"; +import { UpdateWildlifeInput } from "./dto/wildlife/update-wildlife-input"; @Injectable() export class CaseFileService { @@ -1827,8 +1828,12 @@ export class CaseFileService { } }; - deleteWildlife = async (input: DeleteWildlifeInput): Promise => { - const { caseIdentifier, wildlifeId, actor, updateUserId: userId } = input; + updateWildlife = async (model: UpdateWildlifeInput): Promise => { + throw new Error("Method not implemented."); + }; + + deleteWildlife = async (model: DeleteWildlifeInput): Promise => { + const { caseIdentifier, wildlifeId, actor, updateUserId: userId } = model; const current = new Date(); const softDeleteFragment = { active_ind: false, update_user_id: userId, update_utc_timestamp: current }; diff --git a/backend/src/case_file/dto/wildlife/update-wildlife-input.ts b/backend/src/case_file/dto/wildlife/update-wildlife-input.ts new file mode 100644 index 00000000..590e65ad --- /dev/null +++ b/backend/src/case_file/dto/wildlife/update-wildlife-input.ts @@ -0,0 +1,9 @@ +import { WildlifeInput } from "./wildlife-input"; + +export class UpdateWildlifeInput { + leadIdentifier: string; + agencyCode: string; + caseCode: string; + updateUserId: string; + wildlife: WildlifeInput; +} From b3db558941a8460dc7162fb4bcbbaa698dd89944 Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Wed, 15 May 2024 15:01:07 -0700 Subject: [PATCH 10/17] feat: edit wildlife functionality added --- backend/src/case_file/case_file.graphql | 2 - backend/src/case_file/case_file.resolver.ts | 7 + backend/src/case_file/case_file.service.ts | 551 +++++++++++++++++- .../src/case_file/dto/wildlife/drug-input.ts | 2 +- .../dto/wildlife/update-wildlife-input.ts | 4 +- 5 files changed, 541 insertions(+), 25 deletions(-) diff --git a/backend/src/case_file/case_file.graphql b/backend/src/case_file/case_file.graphql index 98037c50..09774158 100644 --- a/backend/src/case_file/case_file.graphql +++ b/backend/src/case_file/case_file.graphql @@ -213,8 +213,6 @@ input CreateWildlifeInput { input UpdateWildlifeInput { caseIdentifier: String! - agencyCode: String! - caseCode: String! updateUserId: String! wildlife: WildlifeInput! } diff --git a/backend/src/case_file/case_file.resolver.ts b/backend/src/case_file/case_file.resolver.ts index 205fe403..e04046ad 100644 --- a/backend/src/case_file/case_file.resolver.ts +++ b/backend/src/case_file/case_file.resolver.ts @@ -13,6 +13,7 @@ import { DeleteSupplementalNoteInput } from "./dto/supplemental-note/delete-supp import { DeleteEquipmentInput } from "./dto/equipment/delete-equipment.input"; import { CreateWildlifeInput } from "./dto/wildlife/create-wildlife-input"; import { DeleteWildlifeInput } from "./dto/wildlife/delete-wildlife-input"; +import { UpdateWildlifeInput } from "./dto/wildlife/update-wildlife-input"; @UseGuards(JwtRoleGuard) @Resolver("CaseFile") @@ -109,6 +110,12 @@ export class CaseFileResolver { return this.caseFileService.createWildlife(input); } + @Mutation("updateWildlife") + @Roles(Role.COS_OFFICER) + updateWildlife(@Args("input") input: UpdateWildlifeInput) { + return this.caseFileService.updateWildlife(input); + } + @Mutation("deleteWildlife") @Roles(Role.COS_OFFICER) deleteWildlife(@Args("input") input: DeleteWildlifeInput) { diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index 001a7957..6cf32752 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -1152,7 +1152,7 @@ export class CaseFileService { }, wildlife: { where: { - OR: [{ active_ind: true }, { active_ind: null }], + active_ind: true, }, select: { wildlife_guid: true, @@ -1162,9 +1162,20 @@ export class CaseFileService { conflict_history_code: true, threat_level_code: true, hwcr_outcome_code: true, - drug_administered: true, - ear_tag: true, + drug_administered: { + where: { + active_ind: true, + }, + }, + ear_tag: { + where: { + active_ind: true, + }, + }, action: { + where: { + active_ind: true, + }, select: { action_guid: true, actor_guid: true, @@ -1234,7 +1245,7 @@ export class CaseFileService { //-- add the wildlife items to the subject if there //-- are results to add to the casefile if (queryResult.wildlife) { - caseFile.subject = await this.getCaseFileSubjects(queryResult); + caseFile.subject = await this._getCaseFileSubjects(queryResult); } return caseFile; @@ -1486,7 +1497,7 @@ export class CaseFileService { //-- get all of the subjects for the case files, this can be wildlife as well //-- as people - private getCaseFileSubjects = async (query: SubjectQueryResult): Promise => { + private _getCaseFileSubjects = async (query: SubjectQueryResult): Promise => { let result: Array; if (query?.wildlife) { @@ -1672,6 +1683,7 @@ export class CaseFileService { wildlife_guid: wildlifeId, ear_code: ear, ear_tag_identifier: identifier, + active_ind: true, create_user_id: userId, update_user_id: userId, create_utc_timestamp: new Date(), @@ -1723,6 +1735,7 @@ export class CaseFileService { drug_discarded_amount, discard_method_text, adverse_reaction_text, + active_ind: true, create_user_id: userId, update_user_id: userId, create_utc_timestamp: new Date(), @@ -1764,11 +1777,8 @@ export class CaseFileService { }, }); - console.log("XREFS: ", xrefs); - console.log("ACTIONS: ", actions); const records = actions.map(({ actor: actor_guid, date: action_date, action }) => { const xref = xrefs.find((item) => item.action_code === action); - console.log("XREF: ", xref); return { case_guid: caseId, @@ -1823,13 +1833,524 @@ export class CaseFileService { return result; } catch (error) { - console.log("exception: unable to wildlife ", error); + console.log("exception: unable to create wildlife ", error); throw new GraphQLError("Exception occurred. See server log for details", {}); } }; updateWildlife = async (model: UpdateWildlifeInput): Promise => { - throw new Error("Method not implemented."); + const { caseIdentifier, updateUserId, wildlife } = model; + const { id: wildlifeId } = wildlife; + + let result: CaseFile; + const current = new Date(); + + //-- + //-- apply updates to the base wildlife record + //-- + const _updateWildlife = async ( + db: Omit< + PrismaClient, + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" + >, + input: WildlifeInput, + userId: string, + date: Date, + ) => { + try { + const { id, species, sex, age, categoryLevel, conflictHistory, outcome } = input; + + //-- create a new data record to update based on the input provided + let data = { + species_code: species, + sex_code: sex || null, + age_code: age || null, + threat_level_code: categoryLevel || null, + conflict_history_code: conflictHistory || null, + hwcr_outcome_code: outcome || null, + update_user_id: userId, + update_utc_timestamp: date, + }; + + const result = await db.wildlife.update({ + where: { wildlife_guid: id }, + data, + }); + + return result; + } catch (error) { + console.log(`exception: unable to update wildlife record: ${wildlifeId}`, error); + throw new GraphQLError("Exception occurred. See server log for details", {}); + } + }; + + //-- + //-- add, delete and update any ear-tags + //-- + const _upsertEarTags = async ( + db: Omit< + PrismaClient, + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" + >, + wildlifeId: string, + tags: Array, + userId: string, + date: Date, + ) => { + try { + const current = await db.ear_tag.findMany({ + where: { + wildlife_guid: wildlifeId, + active_ind: true, + }, + }); + + //-- if there are no ear-tags add them + if (!current || (current.length === 0 && tags && tags.length !== 0)) { + const newTags = tags.map(({ ear: ear_code, identifier: ear_tag_identifier }) => { + return { + wildlife_guid: wildlifeId, + ear_code, + ear_tag_identifier, + active_ind: true, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: date, + update_utc_timestamp: date, + }; + }); + + await db.ear_tag.createMany({ data: newTags }); + } else if (current && current.length !== 0 && tags && tags.length !== 0) { + let updates = []; + let remove = []; + let add = []; + + tags.forEach((tag) => { + const { id } = tag; + if (current.find((item) => item.ear_tag_guid === id)) { + updates = [...updates, tag]; + } else if (!current.find((item) => item.ear_tag_guid === id)) { + add = [...add, tag]; + } + }); + + current.forEach((tag) => { + const { ear_tag_guid: id } = tag; + if (!tags.find((item) => item.id === id)) { + remove = [...remove, tag]; + } + }); + + if (updates.length !== 0) { + updates.forEach(async ({ id, ear, identifier }) => { + await db.ear_tag.update({ + where: { + ear_tag_guid: id, + }, + data: { + ear_code: ear, + ear_tag_identifier: identifier, + update_user_id: userId, + update_utc_timestamp: date, + }, + }); + }); + } + + if (remove.length !== 0) { + remove.forEach(async ({ ear_tag_guid }) => { + await db.ear_tag.update({ + where: { + ear_tag_guid, + }, + data: { + active_ind: false, + update_user_id: userId, + update_utc_timestamp: date, + }, + }); + }); + } + + if (add.length !== 0) { + const newTags = add.map(({ ear: ear_code, identifier: ear_tag_identifier }) => { + return { + wildlife_guid: wildlifeId, + ear_code, + ear_tag_identifier, + active_ind: true, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: date, + update_utc_timestamp: date, + }; + }); + + await db.ear_tag.createMany({ data: newTags }); + } + } else if (current && current.length !== 0 && tags && tags.length === 0) { + //-- remove any tags that are currently on the wildlife record + await db.ear_tag.updateMany({ + where: { + wildlife_guid: wildlifeId, + }, + data: { + active_ind: false, + update_user_id: userId, + update_utc_timestamp: date, + }, + }); + } + } catch (error) { + console.log(`exception: unable to update ear-tags for wildlife record: ${wildlifeId}`, error); + throw new GraphQLError("Exception occurred. See server log for details", {}); + } + }; + + //-- + //-- add, delete and update any drugs administered + //-- + const _upsertDrugs = async ( + db: Omit< + PrismaClient, + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" + >, + wildlifeId: string, + drugs: Array, + userId: string, + date: Date, + ) => { + try { + const current = await db.drug_administered.findMany({ + where: { + wildlife_guid: wildlifeId, + active_ind: true, + }, + }); + + if (!current || (current.length === 0 && drugs && drugs.length !== 0)) { + const newDrugs = drugs.map( + ({ + vial: vial_number, + drug: drug_code, + amountUsed: drug_used_amount, + amountDiscarded: drug_discarded_amount, + injectionMethod: drug_method_code, + reactions: adverse_reaction_text, + remainingUse: drug_remaining_outcome_code, + discardMethod: discard_method_text, + }) => { + return { + wildlife_guid: wildlifeId, + + vial_number, + drug_code, + drug_used_amount, + drug_method_code, + adverse_reaction_text, + drug_remaining_outcome_code, + drug_discarded_amount, + discard_method_text, + + active_ind: true, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: date, + update_utc_timestamp: date, + }; + }, + ); + + await db.drug_administered.createMany({ data: newDrugs }); + } else if (current && current.length !== 0 && drugs && drugs.length !== 0) { + let updates = []; + let remove = []; + let add = []; + + //-- get find new, updatebale, and deletable drugs + drugs.forEach((drug) => { + const { id } = drug; + if (current.find((item) => item.drug_administered_guid === id)) { + updates = [...updates, drug]; + } else if (!current.find((item) => item.drug_administered_guid === id)) { + add = [...add, drug]; + } + }); + + current.forEach((tag) => { + const { drug_administered_guid: id } = tag; + if (!drugs.find((item) => item.id === id)) { + remove = [...remove, tag]; + } + }); + + //-- apply changes + if (updates.length !== 0) { + updates.forEach( + async ({ + id, + vial: vial_number, + drug: drug_code, + amountUsed: drug_used_amount, + injectionMethod: drug_method_code, + discardMethod: discard_method_text, + reactions: adverse_reaction_text, + amountDiscarded: drug_discarded_amount, + remainingUse: drug_remaining_outcome_code, + }) => { + await db.drug_administered.update({ + where: { + drug_administered_guid: id, + }, + data: { + vial_number, + drug_code, + drug_method_code, + drug_remaining_outcome_code, + drug_used_amount, + drug_discarded_amount, + discard_method_text, + adverse_reaction_text, + update_user_id: userId, + update_utc_timestamp: date, + }, + }); + }, + ); + } + + if (remove.length !== 0) { + remove.forEach(async ({ drug_administered_guid }) => { + await db.drug_administered.update({ + where: { + drug_administered_guid, + }, + data: { + active_ind: false, + update_user_id: userId, + update_utc_timestamp: date, + }, + }); + }); + } + + if (add.length !== 0) { + const newDrugs = add.map( + ({ + vial: vial_number, + drug: drug_code, + amountUsed: drug_used_amount, + injectionMethod: drug_method_code, + reactions: adverse_reaction_text, + remainingUse: drug_remaining_outcome_code, + amountDiscarded: drug_discarded_amount, + discardMethod: discard_method_text, + }) => { + return { + wildlife_guid: wildlifeId, + + vial_number, + drug_code, + drug_used_amount, + drug_method_code, + adverse_reaction_text, + drug_remaining_outcome_code, + drug_discarded_amount, + discard_method_text, + + active_ind: true, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: date, + update_utc_timestamp: date, + }; + }, + ); + + await db.drug_administered.createMany({ data: newDrugs }); + } + } else if (current && current.length !== 0 && drugs && drugs.length === 0) { + //-- remove any tags that are currently on the wildlife record + await db.drug_administered.updateMany({ + where: { + wildlife_guid: wildlifeId, + }, + data: { + active_ind: false, + update_user_id: userId, + update_utc_timestamp: date, + }, + }); + } + } catch (error) {} + }; + + //-- + //-- update the actions for the seelcted wildlife + //-- record, depending on the drugs and outcome + //-- actions may need to be updated, removed, or added + //-- + const _upsertActions = async ( + db: Omit< + PrismaClient, + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" + >, + caseIdentifier: string, + wildlifeId: string, + actions: Array, + userId: string, + date: Date, + ) => { + try { + const xrefs = await db.action_type_action_xref.findMany({ + where: { + action_type_code: ACTION_TYPE_CODES.WILDLIFE, + }, + select: { + action_type_action_xref_guid: true, + action_code: true, + }, + }); + + const current = await db.action.findMany({ + where: { + wildlife_guid: wildlifeId, + active_ind: true, + }, + }); + + if (!current || (current.length === 0 && actions && actions.length !== 0)) { + //-- add new actions + const items = actions.map(({ actor: actor_guid, date: action_date, action }) => { + const xref = xrefs.find((item) => item.action_code === action); + + return { + case_guid: caseIdentifier, + wildlife_guid: wildlifeId, + action_type_action_xref_guid: xref.action_type_action_xref_guid, + actor_guid, + action_date, + active_ind: true, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: new Date(), + update_utc_timestamp: new Date(), + }; + }); + + await db.action.createMany({ data: items }); + } else if (current && current.length !== 0 && actions && actions.length !== 0) { + //-- check for updates, new actions, and removed actions + let updates = []; + let remove = []; + let add = []; + + actions.forEach((action) => { + const { id } = action; + if (current.find((item) => item.action_guid === id)) { + updates = [...updates, action]; + } else if (!current.find((item) => item.action_guid === id)) { + add = [...add, action]; + } + }); + + current.forEach((action) => { + const { action_guid: id } = action; + if (!actions.find((item) => item.id === id)) { + remove = [...remove, action]; + } + }); + + if (updates.length !== 0) { + updates.forEach(async ({ id, actor: actor_guid, date: action_date, action, activeIndicator }) => { + await db.action.update({ + where: { + action_guid: id, + }, + data: { + actor_guid, + action_date, + active_ind: true, + update_user_id: userId, + update_utc_timestamp: date, + }, + }); + }); + } + + if (remove.length !== 0) { + remove.forEach(async ({ action_guid }) => { + await db.action.update({ + where: { + action_guid, + }, + data: { + active_ind: false, + update_user_id: userId, + update_utc_timestamp: date, + }, + }); + }); + } + + if (add.length !== 0) { + const items = add.map(({ actor: actor_guid, date: action_date, action }) => { + const xref = xrefs.find((item) => item.action_code === action); + + return { + case_guid: caseIdentifier, + wildlife_guid: wildlifeId, + action_type_action_xref_guid: xref.action_type_action_xref_guid, + actor_guid, + action_date, + active_ind: true, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: new Date(), + update_utc_timestamp: new Date(), + }; + }); + await db.action.createMany({ data: items }); + } + } + } catch (error) {} + }; + + try { + await this.prisma.$transaction(async (db) => { + //-- find the wildlife record first, if there is a record, + //-- apply updates to it + const source = await db.wildlife.findUnique({ + where: { + case_file_guid: caseIdentifier, + wildlife_guid: wildlifeId, + }, + }); + + if (source) { + //-- apply any changes to the wildlife record first + let wildlifeUpdate = await _updateWildlife(db, wildlife, updateUserId, current); + + //-- if the wildlife record was updated start applying the remainder of the + //-- updates, make sure to remove items as needed + if (wildlifeUpdate) { + const { tags, drugs, actions } = wildlife; + + const tagsResult = await _upsertEarTags(db, wildlifeId, tags, updateUserId, current); + const drugsResult = await _upsertDrugs(db, wildlifeId, drugs, updateUserId, current); + const actionsResult = await _upsertActions(db, caseIdentifier, wildlifeId, actions, updateUserId, current); + } + } + }); + + result = await this.findOne(caseIdentifier); + + return result; + } catch (error) { + console.log("exception: unable to update wildlife ", error); + throw new GraphQLError("Exception occurred. See server log for details", {}); + } }; deleteWildlife = async (model: DeleteWildlifeInput): Promise => { @@ -1850,8 +2371,6 @@ export class CaseFileService { if (!wildlife) { throw new Error(`Wildlife with ID ${wildlifeId} not found.`); - } else { - console.log("DELETE WILDLIFE"); } //-- soft delete ear_tags @@ -1861,8 +2380,6 @@ export class CaseFileService { where: { wildlife_guid: wildlifeId }, data: softDeleteFragment, }); - } else { - console.log("NO EAR_TAGS"); } //-- soft delete drugs_administered @@ -1872,8 +2389,6 @@ export class CaseFileService { where: { wildlife_guid: wildlifeId }, data: softDeleteFragment, }); - } else { - console.log("NO DRUGS"); } //-- soft delete wildlife record @@ -1886,14 +2401,12 @@ export class CaseFileService { where: { wildlife_guid: wildlifeId }, data: softDeleteFragment, }); - } else { - console.log("NO ACTIONS"); } }); return await this.findOne(caseIdentifier); } catch (error) { - console.log("exception: unable to delete supplemental note", error); + console.log("exception: unable to delete wildlife", error); throw new GraphQLError("Exception occurred. See server log for details", {}); } }; diff --git a/backend/src/case_file/dto/wildlife/drug-input.ts b/backend/src/case_file/dto/wildlife/drug-input.ts index 9eb968ef..6c12d007 100644 --- a/backend/src/case_file/dto/wildlife/drug-input.ts +++ b/backend/src/case_file/dto/wildlife/drug-input.ts @@ -1,5 +1,5 @@ export interface DrugInput { - id?: number; + id?: string; vial: string; drug: string; diff --git a/backend/src/case_file/dto/wildlife/update-wildlife-input.ts b/backend/src/case_file/dto/wildlife/update-wildlife-input.ts index 590e65ad..62dcf510 100644 --- a/backend/src/case_file/dto/wildlife/update-wildlife-input.ts +++ b/backend/src/case_file/dto/wildlife/update-wildlife-input.ts @@ -1,9 +1,7 @@ import { WildlifeInput } from "./wildlife-input"; export class UpdateWildlifeInput { - leadIdentifier: string; - agencyCode: string; - caseCode: string; + caseIdentifier: string; updateUserId: string; wildlife: WildlifeInput; } From 51a07e3ee3b739e43d025fff13c280d2e55bf885 Mon Sep 17 00:00:00 2001 From: afwilcox Date: Wed, 15 May 2024 16:26:16 -0700 Subject: [PATCH 11/17] Merge commit. --- backend/src/case_file/case_file.service.ts | 23 ++++++++++++++++--- backend/src/case_file/case_file_types.graphql | 2 +- .../src/case_file/entities/wildlife-entity.ts | 4 ++-- .../entities/case_file_action.entity.ts | 4 ++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index 4fc043be..9573e296 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -343,6 +343,23 @@ export class CaseFileService { return caseFile; }; + async findOneByLeadId(leadIdentifier: string) { + let caseFileOutput: CaseFile = new CaseFile(); + const caseIdRecord = await this.prisma.lead.findFirst({ + where: { + lead_identifier: leadIdentifier, + }, + select: { + case_identifier: true, + }, + }); + if (caseIdRecord?.case_identifier) { + caseFileOutput = await this.findOne(caseIdRecord.case_identifier); + } + + return caseFileOutput; + } + async updateAssessment(caseIdentifier: string, updateAssessmentInput: UpdateAssessmentInput) { let caseFileOutput: CaseFile; @@ -1393,7 +1410,7 @@ export class CaseFileService { ); const actions = action.map( - ({ action_guid: actionGuid, actor_guid: actor, action_date: date, action_type_action_xref: xref }) => { + ({ action_guid: actionId, actor_guid: actor, action_date: date, action_type_action_xref: xref }) => { //-- the xref contains the action code const { action_code_action_type_action_xref_action_codeToaction_code: { @@ -1404,13 +1421,13 @@ export class CaseFileService { }, } = xref; return { - actionGuid, + actionId, actor, + activeIndicator, actionCode, date, shortDescription, longDescription, - activeIndicator, }; }, ); diff --git a/backend/src/case_file/case_file_types.graphql b/backend/src/case_file/case_file_types.graphql index 01f2fc37..2e89b961 100644 --- a/backend/src/case_file/case_file_types.graphql +++ b/backend/src/case_file/case_file_types.graphql @@ -51,7 +51,7 @@ type Wildlife { outcome: String tags: [EarTag] drugs: [Drug] - actions: [Action] + actions: [CaseFileAction] } type EarTag { diff --git a/backend/src/case_file/entities/wildlife-entity.ts b/backend/src/case_file/entities/wildlife-entity.ts index 842a818f..a5c9175a 100644 --- a/backend/src/case_file/entities/wildlife-entity.ts +++ b/backend/src/case_file/entities/wildlife-entity.ts @@ -1,4 +1,4 @@ -import { Action } from "./case-action.entity"; +import { CaseFileAction } from "../../case_file_action/entities/case_file_action.entity"; export interface Wildlife { id: string; @@ -10,7 +10,7 @@ export interface Wildlife { outcome?: string; tags?: Array; drugs?: Array; - actions?: Array; + actions?: Array; } export interface EarTag { diff --git a/backend/src/case_file_action/entities/case_file_action.entity.ts b/backend/src/case_file_action/entities/case_file_action.entity.ts index dd37b8b2..0a941d2a 100644 --- a/backend/src/case_file_action/entities/case_file_action.entity.ts +++ b/backend/src/case_file_action/entities/case_file_action.entity.ts @@ -6,6 +6,6 @@ export class CaseFileAction { date: Date; shortDescription: string; longDescription: string; - actionTypeCode: string; - displayOrder: number; + actionTypeCode?: string; + displayOrder?: number; } From 6eccce7b0419849c2058d022a16ec064500aec72 Mon Sep 17 00:00:00 2001 From: afwilcox Date: Wed, 15 May 2024 16:42:10 -0700 Subject: [PATCH 12/17] one more merge... --- backend/src/case_file/case_file.service.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index 31234a81..180e6264 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -242,7 +242,7 @@ export class CaseFileService { }, wildlife: { where: { - OR: [{ active_ind: true }, { active_ind: null }], + active_ind: true, }, select: { wildlife_guid: true, @@ -252,9 +252,20 @@ export class CaseFileService { conflict_history_code: true, threat_level_code: true, hwcr_outcome_code: true, - drug_administered: true, - ear_tag: true, + drug_administered: { + where: { + active_ind: true, + }, + }, + ear_tag: { + where: { + active_ind: true, + }, + }, action: { + where: { + active_ind: true, + }, select: { action_guid: true, actor_guid: true, @@ -337,7 +348,7 @@ export class CaseFileService { //-- add the wildlife items to the subject if there //-- are results to add to the casefile if (queryResult.wildlife) { - caseFile.subject = await this.getCaseFileSubjects(queryResult); + caseFile.subject = await this._getCaseFileSubjects(queryResult); } return caseFile; From 2f30897dfde01b7c01f4c5b89762c902fa62e970 Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Tue, 21 May 2024 18:03:23 -0700 Subject: [PATCH 13/17] fix: added an order property to the wildlife outcome added an order property to wildlife, drugs and tags to make sure that when new items are added they re added in an consistent order --- backend/src/case_file/case_file.resolver.ts | 1 + backend/src/case_file/case_file.service.ts | 197 ++++++++++-------- backend/src/case_file/case_file_types.graphql | 3 + .../src/case_file/dto/subject-query-result.ts | 1 + .../src/case_file/entities/wildlife-entity.ts | 1 + 5 files changed, 111 insertions(+), 92 deletions(-) diff --git a/backend/src/case_file/case_file.resolver.ts b/backend/src/case_file/case_file.resolver.ts index e04046ad..16a72985 100644 --- a/backend/src/case_file/case_file.resolver.ts +++ b/backend/src/case_file/case_file.resolver.ts @@ -122,3 +122,4 @@ export class CaseFileResolver { return this.caseFileService.deleteWildlife(input); } } +//moo diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index 180e6264..2b87c2f5 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -252,6 +252,7 @@ export class CaseFileService { conflict_history_code: true, threat_level_code: true, hwcr_outcome_code: true, + update_utc_timestamp: true, drug_administered: { where: { active_ind: true, @@ -1340,8 +1341,6 @@ export class CaseFileService { actions: [], } as Equipment); - this.logger.debug(`Equipment type: ${equipment.equipment_code}`); - // Append the action to this equipment's list of actions equipmentDetail.actions.push({ actionId: action.action_guid, @@ -1372,101 +1371,115 @@ export class CaseFileService { if (query?.wildlife) { const { wildlife } = query; - result = wildlife.map((item) => { - const { - wildlife_guid: id, - species_code: species, - sex_code: sex, - age_code: age, - threat_level_code: categoryLevel, - conflict_history_code: conflictHistory, - hwcr_outcome_code: outcome, - ear_tag, - drug_administered, - action, - } = item; - - const tags = ear_tag.map(({ ear_tag_guid: id, ear_code: ear, ear_tag_identifier: identifier }) => { - return { - id, - ear, - identifier, - }; - }); + console.log(wildlife); + + result = wildlife + .sort((a, b) => a.update_utc_timestamp.getTime() - b.update_utc_timestamp.getTime()) + .map((item, idx) => { + const { + wildlife_guid: id, + species_code: species, + sex_code: sex, + age_code: age, + threat_level_code: categoryLevel, + conflict_history_code: conflictHistory, + hwcr_outcome_code: outcome, + ear_tag, + drug_administered, + action, + } = item; + + const tags = ear_tag + .sort((a, b) => a.update_utc_timestamp.getTime() - b.update_utc_timestamp.getTime()) + .map(({ ear_tag_guid: id, ear_code: ear, ear_tag_identifier: identifier }, idx) => { + return { + id, + ear, + identifier, + order: idx + 1, + }; + }); - const drugs = drug_administered.map( - ({ - drug_administered_guid: id, - vial_number: vial, - drug_code: drug, - drug_used_amount: amountUsed, - drug_method_code: injectionMethod, - adverse_reaction_text: reactions, - drug_remaining_outcome_code: remainingUse, - drug_discarded_amount: amountDiscarded, - discard_method_text: discardMethod, - }) => { - return { - id, - vial, - drug, - amountUsed, - injectionMethod, - reactions, - remainingUse, - amountDiscarded, - discardMethod, - }; - }, - ); - - const actions = action.map( - ({ action_guid: actionId, actor_guid: actor, action_date: date, action_type_action_xref: xref }) => { - //-- the xref contains the action code - const { - action_code_action_type_action_xref_action_codeToaction_code: { - short_description: shortDescription, - long_description: longDescription, - active_ind: activeIndicator, - action_code: actionCode, + const drugs = drug_administered + .sort((a, b) => a.update_utc_timestamp.getTime() - b.update_utc_timestamp.getTime()) + .map( + ( + { + drug_administered_guid: id, + vial_number: vial, + drug_code: drug, + drug_used_amount: amountUsed, + drug_method_code: injectionMethod, + adverse_reaction_text: reactions, + drug_remaining_outcome_code: remainingUse, + drug_discarded_amount: amountDiscarded, + discard_method_text: discardMethod, + }, + idx, + ) => { + return { + id, + vial, + drug, + amountUsed, + injectionMethod, + reactions, + remainingUse, + amountDiscarded, + discardMethod, + order: idx + 1, + }; }, - } = xref; - return { - actionId, - actor, - activeIndicator, - actionCode, - date, - shortDescription, - longDescription, - }; - }, - ); - - let record: Wildlife = { - id, - species, - sex, - age, - categoryLevel, - conflictHistory, - outcome, - }; + ); - if (tags && tags.length !== 0) { - record = { ...record, tags }; - } + const actions = action.map( + ({ action_guid: actionId, actor_guid: actor, action_date: date, action_type_action_xref: xref }) => { + //-- the xref contains the action code + const { + action_code_action_type_action_xref_action_codeToaction_code: { + short_description: shortDescription, + long_description: longDescription, + active_ind: activeIndicator, + action_code: actionCode, + }, + } = xref; + return { + actionId, + actor, + activeIndicator, + actionCode, + date, + shortDescription, + longDescription, + }; + }, + ); - if (drugs && drugs.length !== 0) { - record = { ...record, drugs }; - } + let record: Wildlife = { + id, + species, + sex, + age, + categoryLevel, + conflictHistory, + outcome, + order: idx + 1, + }; - if (actions && actions.length !== 0) { - record = { ...record, actions }; - } + if (tags && tags.length !== 0) { + record = { ...record, tags }; + } - return record; - }); + if (drugs && drugs.length !== 0) { + record = { ...record, drugs }; + } + + if (actions && actions.length !== 0) { + record = { ...record, actions }; + } + + return record; + }); } return result; @@ -2227,7 +2240,7 @@ export class CaseFileService { const current = new Date(); const softDeleteFragment = { active_ind: false, update_user_id: userId, update_utc_timestamp: current }; - console.log("DELETE WILDLIFE"); + try { await this.prisma.$transaction(async (db) => { //-- find the wildlife entry to delete diff --git a/backend/src/case_file/case_file_types.graphql b/backend/src/case_file/case_file_types.graphql index 2e89b961..cbea429e 100644 --- a/backend/src/case_file/case_file_types.graphql +++ b/backend/src/case_file/case_file_types.graphql @@ -52,12 +52,14 @@ type Wildlife { tags: [EarTag] drugs: [Drug] actions: [CaseFileAction] + order: Int } type EarTag { id: String ear: String identifier: String + order: Int } type Drug { @@ -70,4 +72,5 @@ type Drug { remainingUse: String amountDiscarded: String discardMethod: String + order: Int } diff --git a/backend/src/case_file/dto/subject-query-result.ts b/backend/src/case_file/dto/subject-query-result.ts index affac297..c5141bbb 100644 --- a/backend/src/case_file/dto/subject-query-result.ts +++ b/backend/src/case_file/dto/subject-query-result.ts @@ -21,6 +21,7 @@ export interface SubjectQueryResult { age_code: string; hwcr_outcome_code: string; species_code: string; + update_utc_timestamp: Date; drug_administered: { drug_administered_guid: string; wildlife_guid: string; diff --git a/backend/src/case_file/entities/wildlife-entity.ts b/backend/src/case_file/entities/wildlife-entity.ts index a5c9175a..524c3083 100644 --- a/backend/src/case_file/entities/wildlife-entity.ts +++ b/backend/src/case_file/entities/wildlife-entity.ts @@ -1,6 +1,7 @@ import { CaseFileAction } from "../../case_file_action/entities/case_file_action.entity"; export interface Wildlife { + order: number; id: string; species: string; sex?: string; From 68b5f96c7b4419a1318135002c1bb0d03b8a296a Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Wed, 22 May 2024 10:18:13 -0700 Subject: [PATCH 14/17] fix: ear tag ordering problem fixed problem that was rejecting updates and new animal outcomes because of new input value --- backend/src/case_file/case_file.service.ts | 2 -- backend/src/case_file/case_file_inputs.graphql | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index 2b87c2f5..20af80b0 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -1371,8 +1371,6 @@ export class CaseFileService { if (query?.wildlife) { const { wildlife } = query; - console.log(wildlife); - result = wildlife .sort((a, b) => a.update_utc_timestamp.getTime() - b.update_utc_timestamp.getTime()) .map((item, idx) => { diff --git a/backend/src/case_file/case_file_inputs.graphql b/backend/src/case_file/case_file_inputs.graphql index fd5ac29d..ebe6146c 100644 --- a/backend/src/case_file/case_file_inputs.graphql +++ b/backend/src/case_file/case_file_inputs.graphql @@ -176,6 +176,8 @@ input EarTagInput { id: String ear: String! identifier: String! + + order: Int } input DrugInput { @@ -190,6 +192,8 @@ input DrugInput { remainingUse: String amountDiscarded: String discardMethod: String + + order: Int } input WildlifeActionInput { From 33d97db1c041509a77e14c8d10f6eeb871ab068a Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Thu, 23 May 2024 15:58:54 -0700 Subject: [PATCH 15/17] chore: prisma updates --- backend/prisma/schema.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 9f8a09d3..00138249 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -137,7 +137,7 @@ model equipment_code { create_utc_timestamp DateTime @db.Timestamp(6) update_user_id String @db.VarChar(32) update_utc_timestamp DateTime @db.Timestamp(6) - is_trap_ind Boolean @default(false) + is_trap_ind Boolean @default(true) equipment_equipment_equipment_codeToequipment_code equipment[] @relation("equipment_equipment_codeToequipment_code") } From d56b9ad380b01b1a1f456452598a2326d2406cac Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Fri, 24 May 2024 14:50:09 -0700 Subject: [PATCH 16/17] fix: fly script renamed renamed flyway scripts to prevent flyway from failing due to duplicate script versions --- ...tcome-tables.sql => V1.19.0__CE-358-animal-outcome-tables.sql} | 0 ...ome-updates.sql => V1.19.1__CE-358-animal-outcome-updates.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename migrations/sql/{V1.18.0__CE-358-animal-outcome-tables.sql => V1.19.0__CE-358-animal-outcome-tables.sql} (100%) rename migrations/sql/{V1.18.1__CE-358-animal-outcome-updates.sql => V1.19.1__CE-358-animal-outcome-updates.sql} (100%) diff --git a/migrations/sql/V1.18.0__CE-358-animal-outcome-tables.sql b/migrations/sql/V1.19.0__CE-358-animal-outcome-tables.sql similarity index 100% rename from migrations/sql/V1.18.0__CE-358-animal-outcome-tables.sql rename to migrations/sql/V1.19.0__CE-358-animal-outcome-tables.sql diff --git a/migrations/sql/V1.18.1__CE-358-animal-outcome-updates.sql b/migrations/sql/V1.19.1__CE-358-animal-outcome-updates.sql similarity index 100% rename from migrations/sql/V1.18.1__CE-358-animal-outcome-updates.sql rename to migrations/sql/V1.19.1__CE-358-animal-outcome-updates.sql From 20ed5e286e1d99925bb002cb84f958d76ce1a09d Mon Sep 17 00:00:00 2001 From: Mike Sears Date: Mon, 27 May 2024 15:27:59 -0700 Subject: [PATCH 17/17] fix: updated update behaviour updated the update wildlife endpoint to correctly remove and update actions when removing drugs and outcomes --- backend/src/case_file/case_file.service.ts | 199 ++++++++++-------- .../src/case_file/dto/subject-query-result.ts | 2 +- 2 files changed, 110 insertions(+), 91 deletions(-) diff --git a/backend/src/case_file/case_file.service.ts b/backend/src/case_file/case_file.service.ts index ed08d003..07cb2c85 100644 --- a/backend/src/case_file/case_file.service.ts +++ b/backend/src/case_file/case_file.service.ts @@ -252,7 +252,7 @@ export class CaseFileService { conflict_history_code: true, threat_level_code: true, hwcr_outcome_code: true, - update_utc_timestamp: true, + create_utc_timestamp: true, drug_administered: { where: { active_ind: true, @@ -1375,7 +1375,7 @@ export class CaseFileService { const { wildlife } = query; result = wildlife - .sort((a, b) => a.update_utc_timestamp.getTime() - b.update_utc_timestamp.getTime()) + .sort((a, b) => a.create_utc_timestamp.getTime() - b.create_utc_timestamp.getTime()) .map((item, idx) => { const { wildlife_guid: id, @@ -1391,7 +1391,7 @@ export class CaseFileService { } = item; const tags = ear_tag - .sort((a, b) => a.update_utc_timestamp.getTime() - b.update_utc_timestamp.getTime()) + .sort((a, b) => a.create_utc_timestamp.getTime() - b.create_utc_timestamp.getTime()) .map(({ ear_tag_guid: id, ear_code: ear, ear_tag_identifier: identifier }, idx) => { return { id, @@ -1402,7 +1402,7 @@ export class CaseFileService { }); const drugs = drug_administered - .sort((a, b) => a.update_utc_timestamp.getTime() - b.update_utc_timestamp.getTime()) + .sort((a, b) => a.create_utc_timestamp.getTime() - b.create_utc_timestamp.getTime()) .map( ( { @@ -2066,7 +2066,10 @@ export class CaseFileService { }, }); } - } catch (error) {} + } catch (error) { + console.log(`exception: unable to update drug-used for wildlife record: ${wildlifeId}`, error); + throw new GraphQLError("Exception occurred. See server log for details", {}); + } }; //-- @@ -2086,99 +2089,47 @@ export class CaseFileService { date: Date, ) => { try { - const xrefs = await db.action_type_action_xref.findMany({ - where: { - action_type_code: ACTION_TYPE_CODES.WILDLIFE, - }, - select: { - action_type_action_xref_guid: true, - action_code: true, - }, - }); - - const current = await db.action.findMany({ - where: { - wildlife_guid: wildlifeId, - active_ind: true, - }, - }); - - if (!current || (current.length === 0 && actions && actions.length !== 0)) { - //-- add new actions - const items = actions.map(({ actor: actor_guid, date: action_date, action }) => { - const xref = xrefs.find((item) => item.action_code === action); - - return { + //-- if there are no actions present then remove all + //-- actions that are associated with the caseIdentifier + if (!actions || actions?.length === 0) { + await db.action.updateMany({ + where: { case_guid: caseIdentifier, - wildlife_guid: wildlifeId, - action_type_action_xref_guid: xref.action_type_action_xref_guid, - actor_guid, - action_date, - active_ind: true, - create_user_id: userId, + }, + data: { + active_ind: false, update_user_id: userId, - create_utc_timestamp: new Date(), - update_utc_timestamp: new Date(), - }; + create_utc_timestamp: date, + }, }); + } else { + //-- compare the current actions and the actions provided + //-- to determine what needs to be removed and added - await db.action.createMany({ data: items }); - } else if (current && current.length !== 0 && actions && actions.length !== 0) { - //-- check for updates, new actions, and removed actions - let updates = []; - let remove = []; - let add = []; - - actions.forEach((action) => { - const { id } = action; - if (current.find((item) => item.action_guid === id)) { - updates = [...updates, action]; - } else if (!current.find((item) => item.action_guid === id)) { - add = [...add, action]; - } + //-- get the xrefs for wildlife records + const xrefs = await db.action_type_action_xref.findMany({ + where: { + action_type_code: ACTION_TYPE_CODES.WILDLIFE, + }, + select: { + action_type_action_xref_guid: true, + action_code: true, + }, }); - current.forEach((action) => { - const { action_guid: id } = action; - if (!actions.find((item) => item.id === id)) { - remove = [...remove, action]; - } + //-- get the actions associated with the caseIdentifier + const current = await db.action.findMany({ + where: { + wildlife_guid: wildlifeId, + active_ind: true, + }, }); - if (updates.length !== 0) { - updates.forEach(async ({ id, actor: actor_guid, date: action_date, action, activeIndicator }) => { - await db.action.update({ - where: { - action_guid: id, - }, - data: { - actor_guid, - action_date, - active_ind: true, - update_user_id: userId, - update_utc_timestamp: date, - }, - }); - }); - } - - if (remove.length !== 0) { - remove.forEach(async ({ action_guid }) => { - await db.action.update({ - where: { - action_guid, - }, - data: { - active_ind: false, - update_user_id: userId, - update_utc_timestamp: date, - }, - }); - }); - } + //-- there are no existing actions, + if ((!current && actions?.length !== 0) || (current?.length === 0 && actions?.length !== 0)) { + //-- add new actions - if (add.length !== 0) { - const items = add.map(({ actor: actor_guid, date: action_date, action }) => { + const items = actions.map(({ actor: actor_guid, date: action_date, action }) => { const xref = xrefs.find((item) => item.action_code === action); return { @@ -2194,10 +2145,78 @@ export class CaseFileService { update_utc_timestamp: new Date(), }; }); + await db.action.createMany({ data: items }); + } else { + //-- the actions list has been modified + let add = []; + let remove = []; + + for (const xref of xrefs) { + const { action_type_action_xref_guid: id, action_code: actionCode } = xref; + //-- add new actions + if ( + !current.find((item) => item.action_type_action_xref_guid === id) && + actions.find((item) => item.action === actionCode) + ) { + const action = actions.find((item) => item.action === actionCode); + + add = [ + ...add, + { + case_guid: caseIdentifier, + wildlife_guid: wildlifeId, + action_type_action_xref_guid: id, + actor_guid: action.actor, + action_date: action.date, + active_ind: true, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: new Date(), + update_utc_timestamp: new Date(), + }, + ]; + + await db.action.create({ + data: { + case_guid: caseIdentifier, + wildlife_guid: wildlifeId, + action_type_action_xref_guid: id, + actor_guid: action.actor, + action_date: action.date, + active_ind: true, + create_user_id: userId, + update_user_id: userId, + create_utc_timestamp: new Date(), + update_utc_timestamp: new Date(), + }, + }); + } + + if ( + current.find((item) => item.action_type_action_xref_guid === id) && + !actions.find((item) => item.action === actionCode) + ) { + const action = current.find((item) => item.action_type_action_xref_guid === id); + + await db.action.update({ + where: { + action_guid: action.action_guid, + }, + data: { + active_ind: false, + update_user_id: userId, + update_utc_timestamp: date, + }, + }); + } + } } } - } catch (error) {} + } catch (error) { + console.log(`exception: unable to update actions for wildlife record: ${wildlifeId}`, error); + throw new GraphQLError("Exception occurred. See server log for details", {}); + } }; try { diff --git a/backend/src/case_file/dto/subject-query-result.ts b/backend/src/case_file/dto/subject-query-result.ts index c5141bbb..6ab5ec14 100644 --- a/backend/src/case_file/dto/subject-query-result.ts +++ b/backend/src/case_file/dto/subject-query-result.ts @@ -21,7 +21,7 @@ export interface SubjectQueryResult { age_code: string; hwcr_outcome_code: string; species_code: string; - update_utc_timestamp: Date; + create_utc_timestamp: Date; drug_administered: { drug_administered_guid: string; wildlife_guid: string;