@@ -5,41 +5,39 @@ import { PatientService } from '../../services/patient.service';
55import { saveAs } from 'file-saver' ;
66import { FhirService } from '../../services/fhir.service' ;
77import { IpsService } from '../../services/ips.service' ;
8+ import { FLAT_PATIENT_RESOURCE_CATALOG , FlatPatientResourceType } from '../../services/patient-resource-catalog' ;
89import { Subscription , firstValueFrom } from 'rxjs' ;
910import type {
1011 AllergyIntolerance ,
12+ BodyStructure ,
1113 Condition ,
1214 DeathRecord ,
1315 Encounter ,
1416 FhirObservation ,
1517 LaboratoryOrderGroup ,
1618 MedicationStatement ,
1719 Patient ,
20+ Provenance ,
1821 Procedure ,
1922 QuestionnaireResponse ,
2023 ServiceRequest
2124} from '../../model' ;
2225
2326type SupportedResourceType =
2427 | 'Patient'
25- | 'Condition'
26- | 'Procedure'
27- | 'MedicationStatement'
28- | 'AllergyIntolerance'
29- | 'Observation'
30- | 'Encounter'
31- | 'QuestionnaireResponse'
32- | 'ServiceRequest'
28+ | FlatPatientResourceType
3329 | 'Bundle' ;
3430
3531type FhirBundleResource = LaboratoryOrderGroup [ 'fhirBundle' ] | DeathRecord ;
3632
3733type SupportedResource =
3834 | Patient
35+ | BodyStructure
3936 | Condition
4037 | Procedure
4138 | MedicationStatement
4239 | AllergyIntolerance
40+ | Provenance
4341 | FhirObservation
4442 | Encounter
4543 | QuestionnaireResponse
@@ -337,71 +335,27 @@ export class FhirDataComponent implements OnChanges, OnDestroy {
337335 const patientId = this . patient . id ;
338336 const freshPatient = this . patientService . getPatientById ( patientId ) || this . patient ;
339337 const deathRecordBundle = this . patientService . getPatientDeathRecord ( patientId ) ;
340- const laboratoryBundles = this . patientService . getPatientLabOrders ( patientId ) ;
341-
342338 const groups : ResourceGroup [ ] = [
343339 {
344340 resourceType : 'Patient' ,
345341 title : 'Patient' ,
346342 icon : 'person' ,
347343 items : [ this . toPatientItem ( freshPatient ) ]
348344 } ,
349- {
350- resourceType : 'Condition' ,
351- title : 'Condition' ,
352- icon : 'stethoscope' ,
353- iconFontSet : 'material-symbols-outlined' ,
354- items : this . patientService . getPatientConditions ( patientId ) . map ( resource => this . toConditionItem ( resource ) )
355- } ,
356- {
357- resourceType : 'Procedure' ,
358- title : 'Procedure' ,
359- icon : 'healing' ,
360- items : this . patientService . getPatientProcedures ( patientId ) . map ( resource => this . toProcedureItem ( resource ) )
361- } ,
362- {
363- resourceType : 'MedicationStatement' ,
364- title : 'MedicationStatement' ,
365- icon : 'medication' ,
366- items : this . patientService . getPatientMedications ( patientId ) . map ( resource => this . toMedicationItem ( resource ) )
367- } ,
368- {
369- resourceType : 'AllergyIntolerance' ,
370- title : 'AllergyIntolerance' ,
371- icon : 'warning' ,
372- items : this . patientService . getPatientAllergies ( patientId ) . map ( resource => this . toAllergyItem ( resource ) )
373- } ,
374- {
375- resourceType : 'Observation' ,
376- title : 'Observation' ,
377- icon : 'monitor_heart' ,
378- items : this . patientService . getPatientObservations ( patientId ) . map ( resource => this . toObservationItem ( resource ) )
379- } ,
380- {
381- resourceType : 'Encounter' ,
382- title : 'Encounter' ,
383- icon : 'event_note' ,
384- items : this . patientService . getPatientEncounters ( patientId ) . map ( resource => this . toEncounterItem ( resource ) )
385- } ,
386- {
387- resourceType : 'QuestionnaireResponse' ,
388- title : 'QuestionnaireResponse' ,
389- icon : 'assignment' ,
390- items : this . patientService . getPatientQuestionnaireResponses ( patientId ) . map ( resource => this . toQuestionnaireItem ( resource ) )
391- } ,
392- {
393- resourceType : 'ServiceRequest' ,
394- title : 'ServiceRequest' ,
395- icon : 'biotech' ,
396- items : laboratoryBundles
397- . flatMap ( labOrder => labOrder . serviceRequests . map ( resource => this . toServiceRequestItem ( resource , labOrder ) ) )
398- } ,
345+ ...FLAT_PATIENT_RESOURCE_CATALOG . map ( ( entry ) => ( {
346+ resourceType : entry . resourceType ,
347+ title : entry . title ,
348+ icon : entry . icon ,
349+ iconFontSet : entry . iconFontSet ,
350+ items : this . getFlatResources ( patientId , entry . patientServiceGetter )
351+ . map ( ( resource ) => this . toResourceListItem ( entry . resourceType , resource ) )
352+ } ) ) ,
399353 {
400354 resourceType : 'Bundle' ,
401355 title : 'Bundle' ,
402356 icon : 'folder_zip' ,
403357 items : [
404- ...laboratoryBundles . map ( resource => this . toBundleItem ( resource ) ) ,
358+ ...this . patientService . getPatientLabOrders ( patientId ) . map ( resource => this . toBundleItem ( resource ) ) ,
405359 ...( deathRecordBundle ? [ this . toDeathCertificateBundleItem ( deathRecordBundle ) ] : [ ] ) ,
406360 ...( this . localIpsItem && this . localIpsItem . id === `${ patientId } -local-ips` ? [ this . localIpsItem ] : [ ] ) ,
407361 ...( this . serverSummaryItem && this . serverSummaryItem . id === `${ patientId } -$summary` ? [ this . serverSummaryItem ] : [ ] )
@@ -485,6 +439,50 @@ export class FhirDataComponent implements OnChanges, OnDestroy {
485439 } ;
486440 }
487441
442+ private getFlatResources ( patientId : string , getterName : string ) : any [ ] {
443+ const getter = ( this . patientService as any ) [ getterName ] ;
444+ return typeof getter === 'function' ? getter . call ( this . patientService , patientId ) : [ ] ;
445+ }
446+
447+ private toResourceListItem ( resourceType : FlatPatientResourceType , resource : any ) : ResourceListItem {
448+ switch ( resourceType ) {
449+ case 'BodyStructure' :
450+ return this . toBodyStructureItem ( resource as BodyStructure ) ;
451+ case 'Condition' :
452+ return this . toConditionItem ( resource as Condition ) ;
453+ case 'Procedure' :
454+ return this . toProcedureItem ( resource as Procedure ) ;
455+ case 'MedicationStatement' :
456+ return this . toMedicationItem ( resource as MedicationStatement ) ;
457+ case 'AllergyIntolerance' :
458+ return this . toAllergyItem ( resource as AllergyIntolerance ) ;
459+ case 'Provenance' :
460+ return this . toProvenanceItem ( resource as Provenance ) ;
461+ case 'Observation' :
462+ return this . toObservationItem ( resource as FhirObservation ) ;
463+ case 'Encounter' :
464+ return this . toEncounterItem ( resource as Encounter ) ;
465+ case 'QuestionnaireResponse' :
466+ return this . toQuestionnaireItem ( resource as QuestionnaireResponse ) ;
467+ case 'ServiceRequest' :
468+ return this . toServiceRequestItem ( resource as ServiceRequest ) ;
469+ }
470+ }
471+
472+ private toBodyStructureItem ( resource : BodyStructure ) : ResourceListItem {
473+ const label = resource . includedStructure ?. [ 0 ] ?. structure ?. text
474+ || resource . includedStructure ?. [ 0 ] ?. structure ?. coding ?. [ 0 ] ?. display
475+ || resource . note ?. [ 0 ] ?. text
476+ || resource . id ;
477+ return {
478+ resourceType : 'BodyStructure' ,
479+ id : resource . id ,
480+ label,
481+ subtitle : 'Body structure resource' ,
482+ resource
483+ } ;
484+ }
485+
488486 private toConditionItem ( resource : Condition ) : ResourceListItem {
489487 const label = resource . code ?. text || resource . code ?. coding ?. [ 0 ] ?. display || resource . id ;
490488 return {
@@ -544,6 +542,23 @@ export class FhirDataComponent implements OnChanges, OnDestroy {
544542 } ;
545543 }
546544
545+ private toProvenanceItem ( resource : Provenance ) : ResourceListItem {
546+ const targetLabel = resource . target ?. find ( target => ! target . reference . startsWith ( 'Patient/' ) ) ?. display
547+ || resource . target ?. find ( target => ! target . reference . startsWith ( 'Patient/' ) ) ?. reference
548+ || resource . id ;
549+ const sourceLabel = resource . entity ?. [ 0 ] ?. what ?. display
550+ || resource . entity ?. [ 0 ] ?. what ?. identifier ?. value
551+ || 'IPS import' ;
552+
553+ return {
554+ resourceType : 'Provenance' ,
555+ id : resource . id ,
556+ label : `Import provenance for ${ targetLabel } ` ,
557+ subtitle : `${ sourceLabel } • ${ this . buildDateSubtitle ( resource . recorded ) } ` ,
558+ resource
559+ } ;
560+ }
561+
547562 private toEncounterItem ( resource : Encounter ) : ResourceListItem {
548563 const label = resource . type ?. [ 0 ] ?. text || resource . type ?. [ 0 ] ?. coding ?. [ 0 ] ?. display || resource . id ;
549564 const dateValue = resource . period ?. start ;
@@ -597,17 +612,13 @@ export class FhirDataComponent implements OnChanges, OnDestroy {
597612 } ;
598613 }
599614
600- private toServiceRequestItem ( resource : ServiceRequest , labOrder : LaboratoryOrderGroup ) : ResourceListItem {
615+ private toServiceRequestItem ( resource : ServiceRequest ) : ResourceListItem {
601616 const label = resource . code ?. text || resource . code ?. coding ?. [ 0 ] ?. display || resource . id ;
602- const bundleLabel = labOrder . serviceRequests . length === 1
603- ? 'From laboratory order bundle'
604- : `From laboratory order bundle (${ labOrder . serviceRequests . length } determinations)` ;
605-
606617 return {
607618 resourceType : 'ServiceRequest' ,
608619 id : resource . id ,
609620 label,
610- subtitle : ` ${ bundleLabel } • ${ this . buildDateSubtitle ( resource . authoredOn || labOrder . createdAt ) } ` ,
621+ subtitle : this . buildDateSubtitle ( resource . authoredOn || resource . occurrenceDateTime ) ,
611622 resource
612623 } ;
613624 }
@@ -643,15 +654,10 @@ export class FhirDataComponent implements OnChanges, OnDestroy {
643654 throw new Error ( 'No patient selected' ) ;
644655 }
645656
646- const conditions = this . patientService . getPatientConditions ( patientId ) ;
647- const procedures = this . patientService . getPatientProcedures ( patientId ) ;
648- const medications = this . patientService . getPatientMedications ( patientId ) ;
649- const allergies = this . patientService . getPatientAllergies ( patientId ) ;
650- const observations = this . patientService . getPatientObservations ( patientId ) ;
651- const encounters = this . patientService . getPatientEncounters ( patientId ) ;
652- const questionnaireResponses = this . patientService . getPatientQuestionnaireResponses ( patientId ) ;
653- const serviceRequests = this . patientService . getPatientLabOrders ( patientId )
654- . flatMap ( ( labOrder ) => labOrder . serviceRequests ) ;
657+ const flatResourceCollections = FLAT_PATIENT_RESOURCE_CATALOG . map ( ( entry ) => ( {
658+ entry,
659+ resources : this . getFlatResources ( patientId , entry . patientServiceGetter )
660+ } ) ) ;
655661 const bundles = [
656662 ...this . patientService . getPatientLabOrders ( patientId ) . map ( ( labOrder ) => labOrder . fhirBundle ) ,
657663 ...( this . patientService . getPatientDeathRecord ( patientId ) ? [ this . patientService . getPatientDeathRecord ( patientId ) ] : [ ] )
@@ -662,25 +668,27 @@ export class FhirDataComponent implements OnChanges, OnDestroy {
662668 [ `Patient/${ patient . id } ` , patientFullUrl ]
663669 ] ) ;
664670
665- conditions . forEach ( ( resource ) => referenceMap . set ( `Condition/ ${ resource . id } ` , this . createTransactionFullUrl ( `condition- ${ resource . id } ` ) ) ) ;
666- encounters . forEach ( ( resource ) => referenceMap . set ( `Encounter/ ${ resource . id } ` , this . createTransactionFullUrl ( `encounter- ${ resource . id } ` ) ) ) ;
667- procedures . forEach ( ( resource ) => referenceMap . set ( `Procedure/ ${ resource . id } ` , this . createTransactionFullUrl ( `procedure- ${ resource . id } ` ) ) ) ;
668- medications . forEach ( ( resource ) => referenceMap . set ( `MedicationStatement/ ${ resource . id } ` , this . createTransactionFullUrl ( `medication- ${ resource . id } `) ) ) ;
669- allergies . forEach ( ( resource ) => referenceMap . set ( `AllergyIntolerance/ ${ resource . id } ` , this . createTransactionFullUrl ( `allergy -${ resource . id } `) ) ) ;
670- observations . forEach ( ( resource ) => referenceMap . set ( `Observation/ ${ resource . id } ` , this . createTransactionFullUrl ( `observation- ${ resource . id } ` ) ) ) ;
671- questionnaireResponses . forEach ( ( resource ) => referenceMap . set ( `QuestionnaireResponse/ ${ resource . id } ` , this . createTransactionFullUrl ( `questionnaire- ${ resource . id } ` ) ) ) ;
672- serviceRequests . forEach ( ( resource ) => referenceMap . set ( `ServiceRequest/ ${ resource . id } ` , this . createTransactionFullUrl ( `service-request- ${ resource . id } ` ) ) ) ;
671+ flatResourceCollections . forEach ( ( { entry , resources } ) => {
672+ resources . forEach ( ( resource : any ) => {
673+ referenceMap . set (
674+ ` ${ entry . resourceType } / ${ resource . id } `,
675+ this . createTransactionFullUrl ( ` ${ entry . exportFullUrlPrefix } -${ resource . id } `)
676+ ) ;
677+ } ) ;
678+ } ) ;
673679
674680 const entries = [
675681 this . createExportTransactionEntry ( patient , 'Patient' , patientFullUrl , referenceMap ) ,
676- ...conditions . map ( ( resource ) => this . createExportTransactionEntry ( resource , 'Condition' , referenceMap . get ( `Condition/${ resource . id } ` ) ! , referenceMap ) ) ,
677- ...procedures . map ( ( resource ) => this . createExportTransactionEntry ( resource , 'Procedure' , referenceMap . get ( `Procedure/${ resource . id } ` ) ! , referenceMap ) ) ,
678- ...medications . map ( ( resource ) => this . createExportTransactionEntry ( resource , 'MedicationStatement' , referenceMap . get ( `MedicationStatement/${ resource . id } ` ) ! , referenceMap ) ) ,
679- ...allergies . map ( ( resource ) => this . createExportTransactionEntry ( resource , 'AllergyIntolerance' , referenceMap . get ( `AllergyIntolerance/${ resource . id } ` ) ! , referenceMap ) ) ,
680- ...observations . map ( ( resource ) => this . createExportTransactionEntry ( resource , 'Observation' , referenceMap . get ( `Observation/${ resource . id } ` ) ! , referenceMap ) ) ,
681- ...encounters . map ( ( resource ) => this . createExportTransactionEntry ( resource , 'Encounter' , referenceMap . get ( `Encounter/${ resource . id } ` ) ! , referenceMap ) ) ,
682- ...questionnaireResponses . map ( ( resource ) => this . createExportTransactionEntry ( resource , 'QuestionnaireResponse' , referenceMap . get ( `QuestionnaireResponse/${ resource . id } ` ) ! , referenceMap ) ) ,
683- ...serviceRequests . map ( ( resource ) => this . createExportTransactionEntry ( resource , 'ServiceRequest' , referenceMap . get ( `ServiceRequest/${ resource . id } ` ) ! , referenceMap ) ) ,
682+ ...flatResourceCollections . flatMap ( ( { entry, resources } ) =>
683+ resources . map ( ( resource : any ) =>
684+ this . createExportTransactionEntry (
685+ resource ,
686+ entry . resourceType ,
687+ referenceMap . get ( `${ entry . resourceType } /${ resource . id } ` ) ! ,
688+ referenceMap
689+ )
690+ )
691+ ) ,
684692 ...bundles . map ( ( resource : any , index : number ) => this . createExportTransactionEntry ( resource , 'Bundle' , this . createTransactionFullUrl ( `bundle-${ index + 1 } ` ) , referenceMap ) )
685693 ] ;
686694
0 commit comments