diff --git a/CHANGELOG.md b/CHANGELOG.md index 859ce02b0..62c2bba65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project does **not** adhere to [Semantic Versioning](https://semver.org - When material or personnel is selected, the corresponding vehicle is highlighted as well. - When a vehicle is selected, the corresponding material and personnel are highlighted as well. - The reports behavior can generate reports on the counts of transferred patients per triage category. +- The reports behavior can generate event-based reports when the last patient of a triage category has been transferred to a hospital. ### Changed diff --git a/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/human-readable-radiogram-type.pipe.ts b/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/human-readable-radiogram-type.pipe.ts index dde5c1291..018b31096 100644 --- a/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/human-readable-radiogram-type.pipe.ts +++ b/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/human-readable-radiogram-type.pipe.ts @@ -11,6 +11,7 @@ const map: { [Key in ExerciseRadiogram['type']]: string } = { vehicleCountRadiogram: 'Anzahl an Fahrzeugen', resourceRequestRadiogram: 'Anfrage nach Fahrzeugen', transferCountsRadiogram: 'Anzahl abtransportierter Patienten', + transferCategoryCompletedRadiogram: 'Transport für SK abgeschlossen', }; @Pipe({ diff --git a/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content-transfer-category-completed/radiogram-card-content-transfer-category-completed.component.html b/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content-transfer-category-completed/radiogram-card-content-transfer-category-completed.component.html new file mode 100644 index 000000000..970af2ab7 --- /dev/null +++ b/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content-transfer-category-completed/radiogram-card-content-transfer-category-completed.component.html @@ -0,0 +1,16 @@ +
Transport für SK abgeschlossen
+ + + + In + + dieser Patientenablage + + + allen von dieser Transportorganisation verwalteten Patientenablagen + + ist der Abtransport der Patienten mit Sichtungskategorie + + + abgeschlossen. + diff --git a/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content-transfer-category-completed/radiogram-card-content-transfer-category-completed.component.scss b/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content-transfer-category-completed/radiogram-card-content-transfer-category-completed.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content-transfer-category-completed/radiogram-card-content-transfer-category-completed.component.ts b/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content-transfer-category-completed/radiogram-card-content-transfer-category-completed.component.ts new file mode 100644 index 000000000..5d1b2b7eb --- /dev/null +++ b/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content-transfer-category-completed/radiogram-card-content-transfer-category-completed.component.ts @@ -0,0 +1,34 @@ +import type { OnInit } from '@angular/core'; +import { Component, Input } from '@angular/core'; +import { Store } from '@ngrx/store'; +import type { TransferCategoryCompletedRadiogram } from 'digital-fuesim-manv-shared'; +import { UUID } from 'digital-fuesim-manv-shared'; +import type { Observable } from 'rxjs'; +import type { AppState } from 'src/app/state/app.state'; +import { createSelectRadiogram } from 'src/app/state/application/selectors/exercise.selectors'; + +@Component({ + selector: 'app-radiogram-card-content-transfer-category-completed', + templateUrl: + './radiogram-card-content-transfer-category-completed.component.html', + styleUrls: [ + './radiogram-card-content-transfer-category-completed.component.scss', + ], +}) +export class RadiogramCardContentTransferCategoryCompletedComponent + implements OnInit +{ + @Input() radiogramId!: UUID; + + radiogram$!: Observable; + + constructor(private readonly store: Store) {} + + ngOnInit(): void { + this.radiogram$ = this.store.select( + createSelectRadiogram( + this.radiogramId + ) + ); + } +} diff --git a/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content/radiogram-card-content.component.html b/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content/radiogram-card-content.component.html index a1006ec7f..1bfab05be 100644 --- a/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content/radiogram-card-content.component.html +++ b/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/radiogram-list/radiogram-card/radiogram-card-content/radiogram-card-content.component.html @@ -34,6 +34,10 @@ *ngSwitchCase="'transferCountsRadiogram'" [radiogramId]="radiogram.id" /> + Berichte
Ereignisbasierte Berichte
-
Wenn sich die Behandlungsphase ändert
-
+
Ereignisbasierte Berichte class="form-check-input no-validation" />
+
Wenn sich die Behandlungsphase ändert
+
+
+
+ +
+
+ Wenn in dieser Patientenablage alle Patienten einer SK + abtransportiert wurden +
+
+
+
+ +
+
+ Wenn in allen Patientenablagen dieser Transportorganisation alle + Patienten einer SK abtransportiert wurden +
Regelmäßige Berichte
diff --git a/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/tabs/behavior-tab/behaviors/report/simulated-region-overview-behavior-report.component.ts b/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/tabs/behavior-tab/behaviors/report/simulated-region-overview-behavior-report.component.ts index b61cbf4b5..44e8208a0 100644 --- a/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/tabs/behavior-tab/behaviors/report/simulated-region-overview-behavior-report.component.ts +++ b/frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/tabs/behavior-tab/behaviors/report/simulated-region-overview-behavior-report.component.ts @@ -98,6 +98,28 @@ export class SimulatedRegionOverviewBehaviorReportComponent implements OnInit { }); } + updateReportTransferOfCategoryInSingleRegionCompleted( + reportsEnabled: boolean + ) { + this.exerciseService.proposeAction({ + type: '[ReportBehavior] Update report transfer of category in single region completed', + simulatedRegionId: this.simulatedRegionId, + behaviorId: this.reportBehaviorId, + reportChanges: reportsEnabled, + }); + } + + updateReportTransferOfCategoryInMultipleRegionsCompleted( + reportsEnabled: boolean + ) { + this.exerciseService.proposeAction({ + type: '[ReportBehavior] Update report transfer of category in multiple regions completed', + simulatedRegionId: this.simulatedRegionId, + behaviorId: this.reportBehaviorId, + reportChanges: reportsEnabled, + }); + } + updateInterval(informationType: ReportableInformation, interval: string) { this.exerciseService.proposeAction({ type: '[ReportBehavior] Update Recurring Report', diff --git a/shared/src/models/radiogram/exercise-radiogram.ts b/shared/src/models/radiogram/exercise-radiogram.ts index 492d61208..3a2d25748 100644 --- a/shared/src/models/radiogram/exercise-radiogram.ts +++ b/shared/src/models/radiogram/exercise-radiogram.ts @@ -9,6 +9,7 @@ import { TreatmentStatusRadiogram } from './treatment-status-radiogram'; import { VehicleCountRadiogram } from './vehicle-count-radiogram'; import { ResourceRequestRadiogram } from './resource-request-radiogram'; import { TransferCountsRadiogram } from './transfer-counts-radiogram'; +import { TransferCategoryCompletedRadiogram } from './transfer-category-completed-radiogram'; export const radiograms = { MaterialCountRadiogram, @@ -16,6 +17,7 @@ export const radiograms = { PatientCountRadiogram, PersonnelCountRadiogram, ResourceRequestRadiogram, + TransferCategoryCompletedRadiogram, TransferCountsRadiogram, TreatmentStatusRadiogram, VehicleCountRadiogram, @@ -35,6 +37,7 @@ export const radiogramDictionary: ExerciseRadiogramDictionary = { patientCountRadiogram: PatientCountRadiogram, personnelCountRadiogram: PersonnelCountRadiogram, resourceRequestRadiogram: ResourceRequestRadiogram, + transferCategoryCompletedRadiogram: TransferCategoryCompletedRadiogram, transferCountsRadiogram: TransferCountsRadiogram, treatmentStatusRadiogram: TreatmentStatusRadiogram, vehicleCountRadiogram: VehicleCountRadiogram, diff --git a/shared/src/models/radiogram/index.ts b/shared/src/models/radiogram/index.ts index 01d5f969a..c8522f677 100644 --- a/shared/src/models/radiogram/index.ts +++ b/shared/src/models/radiogram/index.ts @@ -9,4 +9,5 @@ export * from './vehicle-count-radiogram'; export * from './resource-request-radiogram'; export * from './status'; export * from './transfer-counts-radiogram'; +export * from './transfer-category-completed-radiogram'; export * from './utils'; diff --git a/shared/src/models/radiogram/transfer-category-completed-radiogram.ts b/shared/src/models/radiogram/transfer-category-completed-radiogram.ts new file mode 100644 index 000000000..89c024623 --- /dev/null +++ b/shared/src/models/radiogram/transfer-category-completed-radiogram.ts @@ -0,0 +1,54 @@ +import { IsBoolean, IsUUID, ValidateNested } from 'class-validator'; +import { UUID, uuidValidationOptions } from '../../utils'; +import { IsLiteralUnion, IsValue } from '../../utils/validators'; +import { IsRadiogramStatus } from '../../utils/validators/is-radiogram-status'; +import { getCreate, PatientStatus, patientStatusAllowedValues } from '../utils'; +import type { Radiogram } from './radiogram'; +import { ExerciseRadiogramStatus } from './status/exercise-radiogram-status'; +import { + Scope as TransferProgressScope, + scopeAllowedValues, +} from './utils/transfer-progress-scope'; + +export class TransferCategoryCompletedRadiogram implements Radiogram { + @IsUUID(4, uuidValidationOptions) + readonly id: UUID; + + @IsValue('transferCategoryCompletedRadiogram') + readonly type = 'transferCategoryCompletedRadiogram'; + + @IsUUID(4, uuidValidationOptions) + readonly simulatedRegionId: UUID; + + /** + * @deprecated use the helpers from {@link radiogram-helpers.ts} + * or {@link radiogram-helpers-mutable.ts} instead + */ + @IsRadiogramStatus() + @ValidateNested() + readonly status: ExerciseRadiogramStatus; + + @IsBoolean() + readonly informationAvailable: boolean = false; + + @IsLiteralUnion(patientStatusAllowedValues) + readonly completedCategory: PatientStatus = 'white'; + + @IsLiteralUnion(scopeAllowedValues) + readonly scope: TransferProgressScope = 'singleRegion'; + + /** + * @deprecated Use {@link create} instead + */ + constructor( + id: UUID, + simulatedRegionId: UUID, + status: ExerciseRadiogramStatus + ) { + this.id = id; + this.simulatedRegionId = simulatedRegionId; + this.status = status; + } + + static readonly create = getCreate(this); +} diff --git a/shared/src/models/radiogram/transfer-counts-radiogram.ts b/shared/src/models/radiogram/transfer-counts-radiogram.ts index 265f898e1..360660699 100644 --- a/shared/src/models/radiogram/transfer-counts-radiogram.ts +++ b/shared/src/models/radiogram/transfer-counts-radiogram.ts @@ -8,7 +8,7 @@ import { ResourceDescription } from '../utils/resource-description'; import { IsResourceDescription } from '../../utils/validators/is-resource-description'; import type { Radiogram } from './radiogram'; import { ExerciseRadiogramStatus } from './status/exercise-radiogram-status'; -import { Scope, scopeAllowedValues } from './utils/scope'; +import { Scope, scopeAllowedValues } from './utils/transfer-progress-scope'; export class TransferCountsRadiogram implements Radiogram { @IsUUID(4, uuidValidationOptions) diff --git a/shared/src/models/radiogram/utils/index.ts b/shared/src/models/radiogram/utils/index.ts index 4f041b0c7..d4230b0d4 100644 --- a/shared/src/models/radiogram/utils/index.ts +++ b/shared/src/models/radiogram/utils/index.ts @@ -1 +1 @@ -export * from './scope'; +export * from './transfer-progress-scope'; diff --git a/shared/src/models/radiogram/utils/scope.ts b/shared/src/models/radiogram/utils/scope.ts deleted file mode 100644 index 8bb06460f..000000000 --- a/shared/src/models/radiogram/utils/scope.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { AllowedValues } from '../../../utils/validators'; - -export type Scope = 'singleRegion' | 'transportManagement'; - -export const scopeAllowedValues: AllowedValues = { - singleRegion: true, - transportManagement: true, -}; diff --git a/shared/src/models/radiogram/utils/transfer-progress-scope.ts b/shared/src/models/radiogram/utils/transfer-progress-scope.ts new file mode 100644 index 000000000..bdcd76c68 --- /dev/null +++ b/shared/src/models/radiogram/utils/transfer-progress-scope.ts @@ -0,0 +1,14 @@ +import type { AllowedValues } from '../../../utils/validators'; + +/** + * Defines the scope of the radiogram on the transfer progress. + * * `singleRegion`: The information is only about the simulated region that sent the radiogram + * * `transportManagement`: The information is about all simulated regions that are managed + * by the transport management behavior of the simulated region that sent the radiogram + */ +export type Scope = 'singleRegion' | 'transportManagement'; + +export const scopeAllowedValues: AllowedValues = { + singleRegion: true, + transportManagement: true, +}; diff --git a/shared/src/simulation/behaviors/report.ts b/shared/src/simulation/behaviors/report.ts index 31d7668f6..eae08f88b 100644 --- a/shared/src/simulation/behaviors/report.ts +++ b/shared/src/simulation/behaviors/report.ts @@ -12,7 +12,10 @@ import { IsLiteralUnionMap, IsValue } from '../../utils/validators'; import { GenerateReportActivityState } from '../activities/generate-report'; import { CollectInformationEvent } from '../events/collect'; import { nextUUID } from '../utils/randomness'; -import { TreatmentStatusRadiogram } from '../../models/radiogram'; +import { + TransferCategoryCompletedRadiogram, + TreatmentStatusRadiogram, +} from '../../models/radiogram'; import { addActivity } from '../activities/utils'; import { PublishRadiogramActivityState } from '../activities'; import type { @@ -39,6 +42,14 @@ export class ReportBehaviorState implements SimulationBehaviorState { @IsBoolean() public readonly reportTreatmentProgressChanges: boolean = true; + @IsBoolean() + public readonly reportTransferOfCategoryInSingleRegionCompleted: boolean = + false; + + @IsBoolean() + public readonly reportTransferOfCategoryInMultipleRegionsCompleted: boolean = + true; + static readonly create = getCreate(this); } @@ -95,6 +106,37 @@ export const reportBehavior: SimulationBehavior = { ); break; } + case 'patientCategoryTransferToHospitalFinishedEvent': { + if ( + (event.isRelatedOnlyToOwnRegion && + behaviorState.reportTransferOfCategoryInSingleRegionCompleted) || + (!event.isRelatedOnlyToOwnRegion && + behaviorState.reportTransferOfCategoryInMultipleRegionsCompleted) + ) { + const radiogram = cloneDeepMutable( + TransferCategoryCompletedRadiogram.create( + nextUUID(draftState), + simulatedRegion.id, + RadiogramUnpublishedStatus.create() + ) + ); + radiogram.completedCategory = event.patientCategory; + radiogram.scope = event.isRelatedOnlyToOwnRegion + ? 'singleRegion' + : 'transportManagement'; + radiogram.informationAvailable = true; + + addActivity( + simulatedRegion, + PublishRadiogramActivityState.create( + nextUUID(draftState), + radiogram + ) + ); + } + + break; + } default: // Ignore event } diff --git a/shared/src/state-migrations/33-report-transfer-category-completed.ts b/shared/src/state-migrations/33-report-transfer-category-completed.ts new file mode 100644 index 000000000..2e27772ce --- /dev/null +++ b/shared/src/state-migrations/33-report-transfer-category-completed.ts @@ -0,0 +1,70 @@ +import type { Migration } from './migration-functions'; + +interface SimulatedRegionStub { + behaviors: BehaviorStateStub[]; +} + +interface ReportBehaviorStub { + type: 'reportBehavior'; + reportTransferOfCategoryInSingleRegionCompleted: boolean; + reportTransferOfCategoryInMultipleRegionsCompleted: boolean; +} + +type BehaviorStateStub = + | ReportBehaviorStub + | { + type: Exclude<'reportBehavior', unknown>; + }; + +export const reportTransferCategoryCompleted33: Migration = { + action: (_intermediaryState, action) => { + if ( + (action as { type: string }).type === + '[SimulatedRegion] Add Behavior' + ) { + const typedAction = action as { + behaviorState: BehaviorStateStub; + }; + if (typedAction.behaviorState.type === 'reportBehavior') { + migrateReportBehavior(typedAction.behaviorState); + } + } else if ( + (action as { type: string }).type === + '[SimulatedRegion] Add simulated region' + ) { + const typedAction = action as { + simulatedRegion: SimulatedRegionStub; + }; + migrateSimulatedRegion(typedAction.simulatedRegion); + } + return true; + }, + state: (state) => { + const typedState = state as { + simulatedRegions: { + [key: string]: SimulatedRegionStub; + }; + }; + + Object.values(typedState.simulatedRegions).forEach( + (simulatedRegion) => { + migrateSimulatedRegion(simulatedRegion); + } + ); + }, +}; + +function migrateSimulatedRegion(simulatedRegion: SimulatedRegionStub) { + simulatedRegion.behaviors.forEach((behavior) => { + { + if (behavior.type === 'reportBehavior') { + migrateReportBehavior(behavior); + } + } + }); +} + +function migrateReportBehavior(behavior: ReportBehaviorStub) { + behavior.reportTransferOfCategoryInSingleRegionCompleted = false; + behavior.reportTransferOfCategoryInMultipleRegionsCompleted = false; +} diff --git a/shared/src/state-migrations/migration-functions.ts b/shared/src/state-migrations/migration-functions.ts index 121f687ed..b286bf9e9 100644 --- a/shared/src/state-migrations/migration-functions.ts +++ b/shared/src/state-migrations/migration-functions.ts @@ -22,6 +22,7 @@ import { updateEocLog3 } from './3-update-eoc-log'; import { reportTreatmentStatusChanges30 } from './30-report-treatment-status-changes'; import { improveLoadVehicleActivity31 } from './31-improve-load-vehicle-activity'; import { removeIdFromEvents32 } from './32-remove-id-from-events'; +import { reportTransferCategoryCompleted33 } from './33-report-transfer-category-completed'; import { removeSetParticipantIdAction4 } from './4-remove-set-participant-id-action'; import { removeStatistics5 } from './5-remove-statistics'; import { removeStateHistory6 } from './6-remove-state-history'; @@ -87,4 +88,5 @@ export const migrations: { 30: reportTreatmentStatusChanges30, 31: improveLoadVehicleActivity31, 32: removeIdFromEvents32, + 33: reportTransferCategoryCompleted33, }; diff --git a/shared/src/state.ts b/shared/src/state.ts index 13fe39a02..57c33c38f 100644 --- a/shared/src/state.ts +++ b/shared/src/state.ts @@ -160,5 +160,5 @@ export class ExerciseState { * * This number MUST be increased every time a change to any object (that is part of the state or the state itself) is made in a way that there may be states valid before that are no longer valid. */ - static readonly currentStateVersion = 32; + static readonly currentStateVersion = 33; } diff --git a/shared/src/store/action-reducers/simulation.ts b/shared/src/store/action-reducers/simulation.ts index 26421921d..149e0fbe2 100644 --- a/shared/src/store/action-reducers/simulation.ts +++ b/shared/src/store/action-reducers/simulation.ts @@ -145,6 +145,44 @@ export class UpdateReportTreatmentStatusChangesAction implements Action { public readonly reportTreatmentProgressChanges!: boolean; } +export class UpdateReportTransferOfCategoryInSingleRegionCompletedAction + implements Action +{ + @IsValue( + '[ReportBehavior] Update report transfer of category in single region completed' + ) + public readonly type = + '[ReportBehavior] Update report transfer of category in single region completed'; + + @IsUUID(4, uuidValidationOptions) + public readonly simulatedRegionId!: UUID; + + @IsUUID(4, uuidValidationOptions) + public readonly behaviorId!: UUID; + + @IsBoolean() + public readonly reportChanges!: boolean; +} + +export class UpdateReportTransferOfCategoryInMultipleRegionsCompletedAction + implements Action +{ + @IsValue( + '[ReportBehavior] Update report transfer of category in multiple regions completed' + ) + public readonly type = + '[ReportBehavior] Update report transfer of category in multiple regions completed'; + + @IsUUID(4, uuidValidationOptions) + public readonly simulatedRegionId!: UUID; + + @IsUUID(4, uuidValidationOptions) + public readonly behaviorId!: UUID; + + @IsBoolean() + public readonly reportChanges!: boolean; +} + export class CreateRecurringReportsAction implements Action { @IsValue('[ReportBehavior] Create Recurring Report') public readonly type = '[ReportBehavior] Create Recurring Report'; @@ -474,6 +512,50 @@ export namespace SimulationActionReducers { rights: 'trainer', }; + export const updateReportTransferOfCategoryInSingleRegionCompleted: ActionReducer = + { + action: UpdateReportTransferOfCategoryInSingleRegionCompletedAction, + reducer( + draftState, + { simulatedRegionId, behaviorId, reportChanges } + ) { + const reportBehaviorState = getBehaviorById( + draftState, + simulatedRegionId, + behaviorId, + 'reportBehavior' + ); + + reportBehaviorState.reportTransferOfCategoryInSingleRegionCompleted = + reportChanges; + + return draftState; + }, + rights: 'trainer', + }; + + export const updateReportTransferOfCategoryInMultipleRegionsCompleted: ActionReducer = + { + action: UpdateReportTransferOfCategoryInMultipleRegionsCompletedAction, + reducer( + draftState, + { simulatedRegionId, behaviorId, reportChanges } + ) { + const reportBehaviorState = getBehaviorById( + draftState, + simulatedRegionId, + behaviorId, + 'reportBehavior' + ); + + reportBehaviorState.reportTransferOfCategoryInMultipleRegionsCompleted = + reportChanges; + + return draftState; + }, + rights: 'trainer', + }; + export const createRecurringReports: ActionReducer = { action: CreateRecurringReportsAction, diff --git a/test-scenarios b/test-scenarios index 7c4d485f2..1c0300264 160000 --- a/test-scenarios +++ b/test-scenarios @@ -1 +1 @@ -Subproject commit 7c4d485f201a068e03da9c860ef68a39177cbbfa +Subproject commit 1c03002648ec6af938298e4e75c83f70d044beda