1010import java .util .EnumSet ;
1111import java .util .List ;
1212import java .util .Objects ;
13+ import java .util .Optional ;
1314import java .util .function .Function ;
1415import java .util .regex .Matcher ;
1516import java .util .regex .Pattern ;
2021import org .hl7 .fhir .r4 .model .Bundle .BundleEntryComponent ;
2122import org .hl7 .fhir .r4 .model .Bundle .BundleType ;
2223import org .hl7 .fhir .r4 .model .Bundle .HTTPVerb ;
24+ import org .hl7 .fhir .r4 .model .Condition ;
25+ import org .hl7 .fhir .r4 .model .Consent ;
26+ import org .hl7 .fhir .r4 .model .DiagnosticReport ;
2327import org .hl7 .fhir .r4 .model .DomainResource ;
28+ import org .hl7 .fhir .r4 .model .IdType ;
29+ import org .hl7 .fhir .r4 .model .Immunization ;
30+ import org .hl7 .fhir .r4 .model .MedicationStatement ;
31+ import org .hl7 .fhir .r4 .model .Observation ;
2432import org .hl7 .fhir .r4 .model .Patient ;
33+ import org .hl7 .fhir .r4 .model .Procedure ;
34+ import org .hl7 .fhir .r4 .model .Reference ;
35+ import org .hl7 .fhir .r4 .model .Resource ;
2536import org .slf4j .Logger ;
2637import org .slf4j .LoggerFactory ;
2738
@@ -352,13 +363,17 @@ public PseudonymList getPseudonymsWithNewData(DateWithPrecision exportFrom, Date
352363 .flatMap (this ::getPatients );
353364
354365 return new PseudonymList (patients
355- .map (p -> p .getIdentifier ().stream ()
356- .filter (i -> i .hasSystem () && i .hasValue ()
357- && ConstantsDataTransfer .NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM .equals (i .getSystem ()))
358- .map (i -> i .getValue ()).findFirst ().orElse (null ))
366+ .map (p -> getPseudonym (p , ConstantsDataTransfer .NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM ).orElse (null ))
359367 .filter (p -> p != null ).distinct ().collect (Collectors .toList ()));
360368 }
361369
370+ private Optional <String > getPseudonym (Patient p , String namingSystem )
371+ {
372+ return p .getIdentifier ().stream ()
373+ .filter (i -> i .hasSystem () && i .hasValue () && namingSystem .equals (i .getSystem ())).map (i -> i .getValue ())
374+ .findFirst ();
375+ }
376+
362377 private Stream <Patient > getPatients (Bundle bundle )
363378 {
364379 Stream <Patient > patients = getPatientsFromBundle (bundle );
@@ -433,26 +448,19 @@ private Stream<DomainResource> getNewDataWithIdentifierReferenceSupport(String p
433448 private Stream <DomainResource > getNewDataWithoutIdentifierReferenceSupport (String pseudonym ,
434449 DateWithPrecision exportFrom , Date exportTo )
435450 {
436- Bundle patientBundle = (Bundle ) clientFactory .getFhirStoreClient ().search ().forResource (Patient .class )
437- .where (Patient .IDENTIFIER .exactly ()
438- .systemAndIdentifier (ConstantsDataTransfer .NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM , pseudonym ))
439- .execute ();
440-
441- if (logger .isDebugEnabled ())
442- logger .debug ("Patient search-bundle result: {}" ,
443- fhirContext .newJsonParser ().encodeResourceToString (patientBundle ));
444-
445- if (patientBundle .getTotal () != 1 || !(patientBundle .getEntryFirstRep ().getResource () instanceof Patient ))
451+ Optional <Patient > localPatient = findPatientInLocalFhirStore (
452+ ConstantsDataTransfer .NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM , pseudonym );
453+ if (localPatient .isEmpty ())
446454 {
447455 logger .warn (
448456 "Error while retrieving patient for pseudonym {}, result bundle total not 1 or first entry not patient" ,
449457 pseudonym );
450458 throw new RuntimeException ("Error while retrieving patient for pseudonym " + pseudonym );
451459 }
452460
453- Patient patient = (Patient ) patientBundle .getEntryFirstRep ().getResource ();
454461
455- Bundle searchBundle = getSearchBundleWithPatientId (patient .getIdElement ().getIdPart (), exportFrom , exportTo );
462+ Bundle searchBundle = getSearchBundleWithPatientId (localPatient .get ().getIdElement ().getIdPart (), exportFrom ,
463+ exportTo );
456464
457465 if (logger .isDebugEnabled ())
458466 logger .debug ("Executing Search-Bundle: {}" ,
@@ -464,11 +472,26 @@ private Stream<DomainResource> getNewDataWithoutIdentifierReferenceSupport(Strin
464472 if (logger .isDebugEnabled ())
465473 logger .debug ("Search-Bundle result: {}" , fhirContext .newJsonParser ().encodeResourceToString (resultBundle ));
466474
467- return Stream .concat (Stream .of (patient ),
475+ return Stream .concat (Stream .of (localPatient . get () ),
468476 resultBundle .getEntry ().stream ().filter (e -> e .hasResource () && e .getResource () instanceof Bundle )
469477 .map (e -> (Bundle ) e .getResource ()).flatMap (this ::getDomainResources ));
470478 }
471479
480+ private Optional <Patient > findPatientInLocalFhirStore (String system , String pseudonym )
481+ {
482+ Bundle patientBundle = (Bundle ) clientFactory .getFhirStoreClient ().search ().forResource (Patient .class )
483+ .where (Patient .IDENTIFIER .exactly ().systemAndIdentifier (system , pseudonym )).execute ();
484+
485+ if (logger .isDebugEnabled ())
486+ logger .debug ("Patient search-bundle result: {}" ,
487+ fhirContext .newJsonParser ().encodeResourceToString (patientBundle ));
488+
489+ if (patientBundle .getTotal () != 1 || !(patientBundle .getEntryFirstRep ().getResource () instanceof Patient ))
490+ return Optional .empty ();
491+ else
492+ return Optional .of ((Patient ) patientBundle .getEntryFirstRep ().getResource ());
493+ }
494+
472495 private Stream <DomainResource > getDomainResources (Bundle bundle )
473496 {
474497 Stream <DomainResource > domainResources = getDomainResourcesFromBundle (bundle );
@@ -501,7 +524,175 @@ private Stream<DomainResource> doGetDomainResources(String nextUrl, int subTotal
501524 @ Override
502525 public void storeBundle (Bundle bundle )
503526 {
527+ if (logger .isDebugEnabled ())
528+ logger .debug ("Bundle: {}" , fhirContext .newJsonParser ().encodeResourceToString (bundle ));
529+
530+ if (clientFactory .supportsIdentifierReferenceSearch ())
531+ clientFactory .getFhirStoreClient ().transaction ().withBundle (bundle )
532+ .withAdditionalHeader (Constants .HEADER_PREFER , "handling=strict" ).execute ();
533+ else
534+ storeBundleWithoutLogicalReferencesSupport (bundle );
535+ }
536+
537+ private void storeBundleWithoutLogicalReferencesSupport (Bundle bundle )
538+ {
539+ modifyBundle (bundle );
540+
504541 clientFactory .getFhirStoreClient ().transaction ().withBundle (bundle )
505542 .withAdditionalHeader (Constants .HEADER_PREFER , "handling=strict" ).execute ();
506543 }
544+
545+ private void modifyBundle (Bundle bundle )
546+ {
547+ // bundle has patient
548+ // - db has patient by pseudonym -> update references, modify conditions
549+ // - db does not have patient -> remove patient condition
550+ // bundle has no patient, select from first resource by ref
551+ // - db has patient by pseudonym -> update references, modify conditions
552+ // - error
553+
554+ Optional <Patient > bundlePatient = bundle .getEntry ().stream ()
555+ .filter (e -> e .hasResource () && e .getResource () instanceof Patient ).map (e -> (Patient ) e .getResource ())
556+ .findFirst ();
557+
558+ if (bundlePatient .isPresent ())
559+ {
560+ Optional <String > pseudonym = getPseudonym (bundlePatient .get (),
561+ ConstantsDataTransfer .NAMING_SYSTEM_NUM_CODEX_CRR_PSEUDONYM );
562+
563+ if (pseudonym .isEmpty ())
564+ throw new RuntimeException ("Patient has no pseudonym" );
565+
566+ Optional <Patient > localPatient = findPatientInLocalFhirStore (
567+ ConstantsDataTransfer .NAMING_SYSTEM_NUM_CODEX_CRR_PSEUDONYM , pseudonym .get ());
568+ if (localPatient .isPresent ())
569+ {
570+ String localPatientid = localPatient .get ().getIdElement ().getIdPart ();
571+ modifyBundleWithPatientId (bundle , pseudonym .get (), localPatientid );
572+ }
573+ else
574+ {
575+ String tempId = bundlePatient .get ().getIdElement ().getIdPart ();
576+ modifyBundleWithTempPatientId (bundle , pseudonym .get (), tempId );
577+ }
578+ }
579+ else
580+ {
581+ Optional <String > pseudonym = bundle .getEntry ().stream ().filter (e -> e .hasResource ())
582+ .map (e -> e .getResource ()).map (this ::getSubject ).filter (r -> r .hasIdentifier ())
583+ .map (r -> r .getIdentifier ())
584+ .filter (i -> ConstantsDataTransfer .NAMING_SYSTEM_NUM_CODEX_CRR_PSEUDONYM .equals (i .getSystem ()))
585+ .map (i -> i .getValue ()).findFirst ();
586+
587+ if (pseudonym .isEmpty ())
588+ throw new RuntimeException ("Patient has no pseudonym" );
589+
590+ Optional <Patient > localPatient = findPatientInLocalFhirStore (
591+ ConstantsDataTransfer .NAMING_SYSTEM_NUM_CODEX_CRR_PSEUDONYM , pseudonym .get ());
592+ if (localPatient .isPresent ())
593+ {
594+ String localPatientid = localPatient .get ().getIdElement ().getIdPart ();
595+ modifyBundleWithPatientId (bundle , pseudonym .get (), localPatientid );
596+ }
597+ else
598+ {
599+ logger .warn (
600+ "Bundle does not contain Patient, and Patient with pseudonym {} not found in local fhir store" ,
601+ pseudonym .get ());
602+ throw new RuntimeException (
603+ "Bundle has no patient and local fhir store has no patient with pseudonym " + pseudonym .get ());
604+ }
605+ }
606+
607+ if (logger .isDebugEnabled ())
608+ logger .debug ("Modified bundle: {}" , fhirContext .newJsonParser ().encodeResourceToString (bundle ));
609+ }
610+
611+ private void modifyBundleWithTempPatientId (Bundle bundle , String pseudonym , String tempId )
612+ {
613+ bundle .getEntry ().stream ().filter (e -> e .hasResource () && !(e .getResource () instanceof Patient )).forEach (e ->
614+ {
615+ setSubject (e .getResource (), new Reference (tempId ));
616+ modifyConditionalUpdateUrl (e , pseudonym , "" );
617+ });
618+ }
619+
620+ private void modifyBundleWithPatientId (Bundle bundle , String pseudonym , String patientId )
621+ {
622+ bundle .getEntry ().stream ().filter (e -> e .hasResource () && !(e .getResource () instanceof Patient )).forEach (e ->
623+ {
624+ setSubject (e .getResource (), new Reference (new IdType ("Patient" , patientId )));
625+ modifyConditionalUpdateUrl (e , pseudonym , "&patient=Patient/" + patientId );
626+ });
627+ }
628+
629+ private void modifyConditionalUpdateUrl (BundleEntryComponent entry , String pseudonym , String replacement )
630+ {
631+ String url = entry .getRequest ().getUrl ();
632+ String newUrl = url .replace (
633+ "&patient:identifier=" + ConstantsDataTransfer .NAMING_SYSTEM_NUM_CODEX_CRR_PSEUDONYM + "|" + pseudonym ,
634+ replacement );
635+ entry .getRequest ().setUrl (newUrl );
636+ }
637+
638+ private Resource setSubject (Resource resource , Reference patientRef )
639+ {
640+ if (resource instanceof Condition )
641+ {
642+ ((Condition ) resource ).setSubject (patientRef );
643+ return resource ;
644+ }
645+ else if (resource instanceof Consent )
646+ {
647+ ((Consent ) resource ).setPatient (patientRef );
648+ return resource ;
649+ }
650+ else if (resource instanceof DiagnosticReport )
651+ {
652+ ((DiagnosticReport ) resource ).setSubject (patientRef );
653+ return resource ;
654+ }
655+ else if (resource instanceof Immunization )
656+ {
657+ ((Immunization ) resource ).setPatient (patientRef );
658+ return resource ;
659+ }
660+ else if (resource instanceof MedicationStatement )
661+ {
662+ ((MedicationStatement ) resource ).setSubject (patientRef );
663+ return resource ;
664+ }
665+ else if (resource instanceof Observation )
666+ {
667+ ((Observation ) resource ).setSubject (patientRef );
668+ return resource ;
669+ }
670+ else if (resource instanceof Procedure )
671+ {
672+ ((Procedure ) resource ).setSubject (patientRef );
673+ return resource ;
674+ }
675+ else
676+ throw new RuntimeException ("Resource of type " + resource .getResourceType ().name () + " not supported" );
677+ }
678+
679+ private Reference getSubject (Resource resource )
680+ {
681+ if (resource instanceof Condition )
682+ return ((Condition ) resource ).getSubject ();
683+ else if (resource instanceof Consent )
684+ return ((Consent ) resource ).getPatient ();
685+ else if (resource instanceof DiagnosticReport )
686+ return ((DiagnosticReport ) resource ).getSubject ();
687+ else if (resource instanceof Immunization )
688+ return ((Immunization ) resource ).getPatient ();
689+ else if (resource instanceof MedicationStatement )
690+ return ((MedicationStatement ) resource ).getSubject ();
691+ else if (resource instanceof Observation )
692+ return ((Observation ) resource ).getSubject ();
693+ else if (resource instanceof Procedure )
694+ return ((Procedure ) resource ).getSubject ();
695+ else
696+ throw new RuntimeException ("Resource of type " + resource .getResourceType ().name () + " not supported" );
697+ }
507698}
0 commit comments