Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define Conditional Update Criteria for All Resource Profiles #4

Open
hhund opened this issue Mar 29, 2021 · 6 comments
Open

Define Conditional Update Criteria for All Resource Profiles #4

hhund opened this issue Mar 29, 2021 · 6 comments

Comments

@hhund
Copy link
Member

hhund commented Mar 29, 2021

Conditional update criteria need to be defined for all GECCO resource profiles. For example:

https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/chronic-lung-diseases

Condition?_profile=...&recorded-date=...&patient:identifier=...
@hhund
Copy link
Member Author

hhund commented Jun 7, 2021

The current implementation might be sufficient for most cases, but should be improved. See

protected String getConditionalUpdateUrl(String pseudonym, DomainResource resource)
{
if (resource instanceof Patient)
return "Patient?identifier=" + ConstantsDataTransfer.NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM + "|"
+ pseudonym;
else if (resource instanceof Condition)
{
Condition c = (Condition) resource;
Optional<CanonicalType> profile = c.getMeta().getProfile().stream()
.filter(p -> p.getValue().startsWith(NUM_CODEX_STRUCTURE_DEFINITION_PREFIX)).findFirst();
if (profile.isPresent())
{
return "Condition?_profile=" + profile.get().getValue() + "&recorded-date="
+ c.getRecordedDateElement().getValueAsString() + "&patient:identifier="
+ ConstantsDataTransfer.NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM + "|" + pseudonym;
}
else
throw new RuntimeException("Resource of type " + resource.getResourceType().name()
+ " not supported, missing NUM profile");
}
else if (resource instanceof DiagnosticReport)
{
DiagnosticReport dr = (DiagnosticReport) resource;
Optional<CanonicalType> profile = dr.getMeta().getProfile().stream()
.filter(p -> p.getValue().startsWith(NUM_CODEX_STRUCTURE_DEFINITION_PREFIX)).findFirst();
if (profile.isPresent())
{
return "DiagnosticReport?_profile=" + profile.get().getValue() + "&date="
+ dr.getEffectiveDateTimeType().getValueAsString() + "&patient:identifier="
+ ConstantsDataTransfer.NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM + "|" + pseudonym;
}
else
throw new RuntimeException("Resource of type " + resource.getResourceType().name()
+ " not supported, missing NUM profile");
}
else if (resource instanceof Immunization)
{
Immunization i = (Immunization) resource;
Optional<CanonicalType> profile = i.getMeta().getProfile().stream()
.filter(p -> p.getValue().startsWith(NUM_CODEX_STRUCTURE_DEFINITION_PREFIX)).findFirst();
if (profile.isPresent())
{
return "Immunization?_profile=" + profile.get().getValue() + "&date="
+ i.getOccurrenceDateTimeType().getValueAsString() + "&patient:identifier="
+ ConstantsDataTransfer.NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM + "|" + pseudonym;
}
else
throw new RuntimeException("Resource of type " + resource.getResourceType().name()
+ " not supported, missing NUM profile");
}
else if (resource instanceof MedicationStatement)
{
MedicationStatement ms = (MedicationStatement) resource;
Optional<CanonicalType> profile = ms.getMeta().getProfile().stream()
.filter(p -> p.getValue().startsWith(NUM_CODEX_STRUCTURE_DEFINITION_PREFIX)).findFirst();
if (profile.isPresent())
{
return "MedicationStatement?_profile=" + profile.get().getValue() + "&effective="
+ ms.getEffectiveDateTimeType().getValueAsString() + "&patient:identifier="
+ ConstantsDataTransfer.NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM + "|" + pseudonym;
}
else
throw new RuntimeException("Resource of type " + resource.getResourceType().name()
+ " not supported, missing NUM profile");
}
else if (resource instanceof Observation)
{
Observation o = (Observation) resource;
Optional<CanonicalType> profile = o.getMeta().getProfile().stream()
.filter(p -> p.getValue().startsWith(NUM_CODEX_STRUCTURE_DEFINITION_PREFIX)).findFirst();
if (profile.isPresent())
{
return "Observation?_profile=" + profile.get().getValue() + "&date="
+ o.getEffectiveDateTimeType().getValueAsString() + "&patient:identifier="
+ ConstantsDataTransfer.NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM + "|" + pseudonym;
}
else
throw new RuntimeException("Resource of type " + resource.getResourceType().name()
+ " not supported, missing NUM profile");
}
else if (resource instanceof Procedure)
{
Procedure pr = (Procedure) resource;
Optional<CanonicalType> profile = pr.getMeta().getProfile().stream()
.filter(p -> p.getValue().startsWith(NUM_CODEX_STRUCTURE_DEFINITION_PREFIX)).findFirst();
if (profile.isPresent())
{
return "Procedure?_profile=" + profile.get().getValue() + "&date="
+ pr.getPerformedDateTimeType().getValueAsString() + "&patient:identifier="
+ ConstantsDataTransfer.NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM + "|" + pseudonym;
}
else
throw new RuntimeException("Resource of type " + resource.getResourceType().name()
+ " not supported, missing NUM profile");
}
else
throw new RuntimeException("Resource of type " + resource.getResourceType().name() + " not supported");
}

@storckmi
Copy link

storckmi commented Jun 7, 2021

Laboratory results often contain results on several laboratory values. Therefore, these are provided with the same time stamps, since they are determined from one sample at the same time. With the given condition for the update of observation resources values would be overwritten and only the last laboratory value would be safed.

Also deleting of already sent data is difficult. If a laboratory value for a specific LOINC-code has been submitted before and is not present in the updated data it should be deleted in my opinion.

hhund added a commit that referenced this issue Jun 13, 2021
fTTP BF resolution was missing the source/ prefix for the pseudonym.
Adds support for lab values with profile
https://www.medizininformatik-initiative.de/fhir/core/modul-labor/StructureDefinition/ObservationLab
Uses the loinc code in the update condition for lab values as discussed
in #4.
Adds an initial FHIR store implementation for the fhir-bridge.
@julsas
Copy link
Member

julsas commented Jun 30, 2021

The _profile parameter might not be a good choice because the GECCO profiles do not enforce instances to populate Resource.meta.profile.

Condition:
Condition?category=system|code&code=system|code&recorded-date=dateTime&subject:identifier=system|value

I started testing this with a local Hapi and Firely server. Firely server works but I'm having issues with the subject:identifier parameter on Hapi. I'll check the search parameters for other resource types next.

@hhund
Copy link
Member Author

hhund commented Jul 1, 2021

The send process uses a transaction Bundle as a template to search for new resources that need to be transferred to the central research repository (CRR). Since there is currently no way to transfer a pre-build transaction Bundle with our process, resources transferred to the CRR will always have a resource.meta.profile entry.

Even if we modified the search transaction Bundle to use something other then the GECCO and MII profiles to identify new or updated resource. We could always add a profile deceleration, since we would need to validate the resources not identified by a GECCO or MII profile anyways.

Currently we only validate resource while storing them in the CRR fhir-bridge, validation before Bundle generation and transmission is planed.

I have a workaround for the missing support of identifier based searches in HAPI. But since we do not store data in HAPI this is not a problem.

@julsas Is there an equivalent for Condition?category=system|code&code=system|code for other resources? Using _profile was kind of a cheap workaround to easily differentiate a lot of resources. But I agree, we should not use it for the conditional update criteria.

@julsas
Copy link
Member

julsas commented Jul 2, 2021

Procedure, Observation and MedicationStatement have .category. We currently do not use .category in MedicationStatement, but these can be differentiated bei .medicationCodeableConcept. In general the following search patterns work with a few exceptions.

DiagnosticReport:
DiagnosticReport?category=system|code&code=system|code&subject:identifier=system|value&subject:identifier=system|value
Problem here is that the profile currently does not enforce using a date, i.e. multiple matches possible.

Immunization:
Immunization?vaccine-code=system|code&date=dateTime&target-disease=system|code&subject:identifier=system|value

MedicationStatement:
MedicationStatement?status=code&code=system|code&effective=dateTime&subject:identifier=system|value

Procedure:
Procedure?status=code&category=system|code&code=system|code&date=dateTime&subject:identifier=system|value
The imaging procedure profile currently does not require using .category.

Observation:
Observation?status=code&category=system|code&code=system|code&date=dateTime&subject:identifier=system|value
Some observations currently do not require a date, like the history of travel observation.

Consent:
Consent?status=code&scope=system|code&category=system|code&patient.identifier=system|value

@wetret
Copy link
Member

wetret commented Jul 15, 2021

There is another issue with the update criteria. If a date used as update criteria is not present or replaced with a data-absent-reason extension, the update criteria includes a null value and therefore leads to an error.

For example a condition with the following recordedDate

"_recordedDate":{
      "extension":[
                  {
                     "url":"http://hl7.org/fhir/StructureDefinition/data-absent-reason",
                     "valueCode":"unknown"
                  }
         ]
 }

leads to the update criteria:

Condition?_profile=https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/chronic-liver-diseases&recorded-date=null&patient.identifier=http://www.netzwerk-universitaetsmedizin.de/sid/crr-pseudonym|4d8dbeb183c227fb20688e02ccaa9b112b4b466248c8fdc2b8d3c0ee1cacc12b

--> recorded-date=null

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants