fhir-py icon indicating copy to clipboard operation
fhir-py copied to clipboard

[FR] Support for FHIRPatch

Open ZuSe opened this issue 4 years ago • 6 comments

The lib is missing support for proper PATCH operations as defined in the standard: http://hl7.org/implement/standards/fhir/http.html#patch

According to the docs update/save with a given list of fields should use this operation.

Tested with HAPI FHIR 5.2.0

ZuSe avatar Feb 09 '21 13:02 ZuSe

This feature is already implemented. Could you provide an example of your code?

mkizesov avatar Feb 09 '21 14:02 mkizesov

Hi @mkizesov

thanks for your help. Comes the code:

        client: SyncFHIRClient = get_client(async=False)
        # get or create FHIR object
        try:
            patient: SyncFHIRResource = client.reference('Patient', id=str(user.username)).to_resource()
            modifiedfields = [] #do a PATCH
        except exceptions.ResourceNotFound:
            modifiedfields = None #do a post
            patient = client.resource('Patient', id=str(user.username))

        # set telecom fields
        if any(x in update_fields for x in ["email", "mobile", "phone"]):
            if modifiedfields is not None:
                modifiedfields.append("telecom")
            try:
                # https://github.com/beda-software/fhir-py/issues/66
                getattr(patient, "telecom")
            except KeyError:
                patient.telecom = []

            def update_or_create(system, telelist, entry):
                updated = False
                # update if system already exists in FHIR object
                for i, t in enumerate(telelist):
                    if t["system"] == system:
                        updated = True
                        telelist[i] = entry
                        break
                if not updated:
                    telelist.append(entry)

            if "email" in update_fields:
                update_or_create("email", patient.telecom, entry={
                    "system": 'email',
                    "value": str(user.email),
                    "use": 'home'
                })
            if "mobile" in update_fields:
                update_or_create("sms", patient.telecom, entry={
                    "system": 'sms',
                    "value": str(user.mobile),
                    "use": 'mobile'
                })
            if "phone" in update_fields:
                update_or_create("phone", patient.telecom, entry={
                    "system": 'phone',
                    "value": str(user.phone),
                    "use": 'home'
                })

        if "first_name" in update_fields or "last_name" in update_fields:
            if modifiedfields is not None:
                modifiedfields.append("name")
            try:
                # https://github.com/beda-software/fhir-py/issues/66
                getattr(patient, "name")
            except KeyError:
                patient.name = [{}]
            patient.name[0]["use"] = 'official'
            if "first_name" in update_fields:
                patient.name[0]["given"] = str(user.first_name)
                patient.name[0]["text"] = f'{user.first_name} {patient.name[0].get("family", "")}'
            if "last_name" in update_fields:
                patient.name[0]["family"] = str(user.last_name)
                patient.name[0]["text"] = f'{patient.name[0].get("given", "")} {user.last_name}'
        if "birthdate" in update_fields:
            if modifiedfields is not None:
                modifiedfields.append("birthDate")
            patient.birthDate = user.birthdate.isoformat()
        if "gender" in update_fields:
            if modifiedfields is not None:
                modifiedfields.append("gender")
            patient.gender = user.gender
        if "is_active" in update_fields:
            if modifiedfields is not None:
                modifiedfields.append("active")
            patient.active = user.is_active    
        patient.save(fields=modifiedfields)

So basically the purpose of the code is to update only attributes that have been updated on our side. What we try achieve is to implement some kind of sync-adapter that keeps the FHIR Server in sync with the client. However, as many clients write to the server we want to limit the usage of PUT as much as possible ;)

Best Patrick

ZuSe avatar Feb 09 '21 17:02 ZuSe

Hi! Maybe it will worth if we just allow passing not only field names in save, for example:

resource.save(fhirpatch=[{"op": "add", "path": "path expr here", "value": "value to add"}])

It will be more flexible. What do you think? Will it solve the issue?

ruscoder avatar Jun 23 '21 15:06 ruscoder

yes, should work. Maybe it makes sense to compute as well. Basically

  1. Define FHIR related resource by id
  2. change fields via Dot-notation e.g. tmpPatient.name = "ABC"
  3. Save changed/added fields via patch tmpPatient.save(partial_update=True)

For me that's always the nice use case to use patch, when I know that I want to add/change some specific params but try to avoid loading the whole resource in advance and PUT everything back, just for a couple of small changes (and risk that I run into raise conditions because it changed in the meanwhile).

ZuSe avatar Jun 25 '21 16:06 ZuSe

https://github.com/beda-software/fhir-py/issues/68#issuecomment-866916629 is patch operation available ?

usr-av avatar Sep 19 '22 19:09 usr-av

As I understand, fhir patch is not implemented yet. Only standard http PATCH operation (fhir update) is available using either resource.update(fieldToUpdate="") or resource.save(fields=['fieldsToUpdate']).

mkizesov avatar Sep 19 '22 21:09 mkizesov