.. image:: https://img.shields.io/pypi/status/collective.fhirpath.svg
:target: https://pypi.org/project/collective.fhirpath/
:alt: Egg Status
.. image:: https://img.shields.io/travis/nazrulworld/collective.fhirpath/master.svg
:target: https://app.travis-ci.com/github/nazrulworld/collective.fhirpath
:alt: Travis Build Status
.. image:: https://coveralls.io/repos/github/nazrulworld/collective.fhirpath/badge.svg?branch=master
:target: https://coveralls.io/github/nazrulworld/collective.fhirpath?branch=master
:alt: Test Coverage
.. image:: https://img.shields.io/pypi/pyversions/collective.fhirpath.svg
:target: https://pypi.python.org/pypi/collective.fhirpath/
:alt: Python Versions
.. image:: https://img.shields.io/pypi/v/collective.fhirpath.svg
:target: https://pypi.python.org/pypi/collective.fhirpath/
:alt: Latest Version
.. image:: https://img.shields.io/pypi/l/collective.fhirpath.svg
:target: https://pypi.python.org/pypi/collective.fhirpath/
:alt: License
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/ambv/black
Background (collective.fhirpath)
================================
`fhirpath`_ implementation in Plone, essential battery included, ready to use.
Installation
------------
Install collective.fhirpath by adding it to your buildout::
[buildout]
...
eggs +=
collective.fhirpath
and then running ``bin/buildout``
From Plone controlpanel in the addon settings, install ``collective.fhirpath``.
How It Works
------------
**``FhirResource`` the fhirfield**
Make sure this specialized field is used properly according to `plone.app.fhirfield`_ documentation.
**Make field indexable**
A specilized Catalog PluginIndexes is named ``FhirFieldIndex`` is available, you will use it as like other catalog indexes.
Example::
<?xml version="1.0"?>
<object name="portal_catalog" meta_type="Plone Catalog Tool">
<index name="organization_resource" meta_type="FhirFieldIndex">
<indexed_attr value="organization_resource"/>
</index>
</object>
**Elasticsearch settings**
Make sure elasticsearch has been configured accourding to `collective.elasticsearch`_ docs.
Usages
~~~~~~
FHIR Search::
>>> from fhirpath.interfaces import IElasticsearchEngineFactory
>>> from fhirpath.interfaces import IFhirSearch
>>> from fhirpath.interfaces import ISearchContextFactory
>>> from plone import api
>>> from collective.elasticsearch.es import ElasticSearchCatalog
>>> from zope.component import queryMultiAdapter
>>> es_catalog = ElasticSearchCatalog(api.portal.get_tool("portal_catalog"))
>>> factory = queryMultiAdapter(
.... (es_catalog,), IElasticsearchEngineFactory
.... )
>>> engine = factory(fhir_release="STU3")
>>> search_context = queryMultiAdapter((engine,), ISearchContextFactory)(
.... resource_type, unrestricted=False)
>>> search_factory = queryMultiAdapter((search_context,), IFhirSearch)
>>> params = (
.... ("_profile", "http://hl7.org/fhir/Organization"),
.... ("identifier", "urn:oid:2.16.528.1|91654"),
.... ("type", "http://hl7.org/fhir/organization-type|prov"),
.... ("address-postalcode", "9100 AA"),
.... ("address", "Den Burg"),
.... )
>>> bundle = search_factory(params)
>>> len(bundle.entry)
2
>>> # with query string.
>>> # query_string = self.request["QUERY_STRING]
>>> query_string = "_profile=http://hl7.org/fhir/Organization&identifier=urn:oid:2.16.528.1|91654&type=http://hl7.org/fhir/organization-type|prov&address-postalcode=9100+AA"
>>> bundle = search_factory(query_string=query_string)
>>> len(bundle.entry)
2
ZCatlog FHIR Search::
>>> from collective.fhirpath.interfaces import IZCatalogFhirSearch
>>> zcatalog_factory = queryMultiAdapter((search_context,), IZCatalogFhirSearch)
>>> # with query string.
>>> # query_string = self.request["QUERY_STRING]
>>> query_string = "_profile=http://hl7.org/fhir/Organization&identifier=urn:oid:2.16.528.1|91654&type=http://hl7.org/fhir/organization-type|prov&address-postalcode=9100+AA"
>>> brains = zcatalog_factory(query_string=query_string)
>>> len(brains)
2
FHIR Query::
>>> from fhirpath.interfaces import IElasticsearchEngineFactory
>>> from fhirpath.interfaces import IFhirSearch
>>> from fhirpath.interfaces import ISearchContextFactory
>>> from plone import api
>>> from collective.elasticsearch.es import ElasticSearchCatalog
>>> from zope.component import queryMultiAdapter
>>> from fhirpath.query import Q_
>>> from fhirpath.fql import T_
>>> from fhirpath.fql import sort_
>>> from fhirpath.enums import SortOrderType
>>> es_catalog = ElasticSearchCatalog(api.portal.get_tool("portal_catalog"))
>>> factory = queryMultiAdapter(
.... (es_catalog,), IElasticsearchEngineFactory
.... )
>>> engine = factory(fhir_release="STU3")
>>> query_builder = Q_(resource="Organization", engine=engine)
.... query_builder = query_builder.where(
.... T_("Organization.meta.profile", "http://hl7.org/fhir/Organization")
.... ).sort(sort_("Organization.meta.lastUpdated", SortOrderType.DESC))
>>> result = query_builder(async_result=False, unrestricted=True).fetchall()
>>> result.header.total
2
>>> query_result = query_builder(async_result=False, unrestricted=True)
>>> for resource in query_result:
.... count += 1
.... assert resource.__class__.__name__ == "Organization"
>>> query_builder = Q_(resource="Organization", engine=engine)
>>> query_builder = query_builder.where(T_("Organization.id", "f001"))
>>> result_query = query_builder(async_result=False, unrestricted=True)
>>> resource = result_query.single()
>>> resource is not None
True
>>> query_builder = Q_(resource="Organization", engine=engine)
>>> query_builder = query_builder.where(
.... T_("Organization.meta.profile", "http://hl7.org/fhir/Organization")
.... )
>>> result_query = builder(async_result=False, unrestricted=True)
>>> result = result_query.first()
>>> isinstance(result, result_query._query.get_from()[0][1])
True
Use ``FHIRModelServiceMixin``
-----------------------------
For better performance optimization, you should use ``FHIRModelServiceMixin`` to response ``FHIRModel``, ``FhirFieldValue`` object efficiently.
Example 1::
>>> from plone.restapi.services import Service
>>> from collective.fhirpath.utils import FHIRModelServiceMixin
>>> class MyFHIRGetService(FHIRModelServiceMixin, Service):
.... """ """
.... def reply(self):
.... # do return bellow's types of data
.... # could be ``dict`` type data
.... # could be instance of ``FHIRAbstractModel`` derrived class.
.... # could be instance of ``plone.app.fhirfield.FhirResourceValue`` derrived class.
.... # or self.reply_no_content()
configuration
-------------
This product provides three plone registry based records ``fhirpath.es.index.mapping.nested_fields.limit``, ``fhirpath.es.index.mapping.depth.limit``, ``fhirpath.es.index.mapping.total_fields.limit``. Those are related to ElasticSearch index mapping setup, if you aware about it, then you have option to modify from plone control panel (Registry).
Documentation
-------------
Full documentation for end users can be found in the "docs" folder,
and is also available online at https://collective-fhirpath.readthedocs.io/
Contribute
----------
- Issue Tracker: https://github.com/nazrulworld/collective.fhirpath/issues
- Source Code: https://github.com/nazrulworld/collective.fhirpath
- Documentation: https://collective-fhirpath.readthedocs.io/
Support
-------
If you are having issues, please let us know at: Md Nazrul Islam<email2nazrul@gmail.com>
License
-------
The project is licensed under the GPLv2.
.. _`elasticsearch`: https://www.elastic.co/products/elasticsearch
.. _`fhirpath`: https://pypi.org/project/fhirpath/
.. _`PostgreSQL`: https://www.postgresql.org/
.. _`plone.app.fhirfield`: https://pypi.org/project/plone.app.fhirfield/
.. _`collective.elasticsearch`: https://pypi.org/project/collective.elasticsearch/
.. _restapi_examples_doctest:
REST Client Examples
--------------------
Getting single resource, here we are getting Patient resource by ID.
Example(1)::
>>> response = admin_session.get('/@fhir/Patient/19c5245f-89a8-49f8-b244-666b32adb92e')
>>> response.status_code
200
<BLANKLINE>
>>> response.json()['resourceType'] == 'Patient'
True
<BLANKLINE>
>>> response = admin_session.get('/@fhir/Patient/19c5245f-fake-id')
>>> response.status_code
404
<BLANKLINE>
Search Observation by Patient reference with status condition. Any observation until December 2017 and earlier than January 2017.
Example(2)::
>>> response = admin_session.get('/@fhir/Observation?patient=Patient/19c5245f-89a8-49f8-b244-666b32adb92e&status=final&_lastUpdated=lt2017-12-31T00%3A00%3A00%2B00%3A00&_lastUpdated=gt2017-01-01T00%3A00%3A00%2B00%3A00')
>>> response.status_code
200
>>> response.json()["total"]
1
<BLANKLINE>
Add FHIR Resource through REST API
Example(3)::
>>> import os
>>> import json
>>> import uuid
>>> import DateTime
>>> import time
>>> with open(os.path.join(FIXTURE_PATH, 'Patient.json'), 'r') as f:
... fhir_json = json.load(f)
>>> fhir_json['id'] = str(uuid.uuid4())
>>> fhir_json['name'][0]['text'] = 'Another Patient'
>>> response = admin_session.post('/@fhir/Patient', json=fhir_json)
>>> response.status_code
201
>>> time.sleep(1)
>>> response = admin_session.get('/@fhir/Patient?active=true')
>>> response.json()["total"]
2
Update (PATCH) FHIR Resource the Patient is currently activated, we will deactive.
Example(4)::
>>> patch = [{'op': 'replace', 'path': '/active', 'value': False}]
>>> response = admin_session.patch('/@fhir/Patient/19c5245f-89a8-49f8-b244-666b32adb92e', json={'patch': patch})
>>> response.status_code
204
<BLANKLINE>
Contributors
============
- Md Nazrul Islam, email2nazrul@gmail.com (author)
Changelog
=========
0.8.0 (2022-06-13)
------------------
New features
- A patch has been added in response to `issue#91 <https://github.com/collective/collective.elasticsearch/issues/91>`_ on ``collective.elasticsearch``.
0.7.6 (2021-05-04)
------------------
- minimum ``fhir.resources`` version is now ``6.1.0`` and minimum ``collective.elasticsearch`` version is now ``3.0.5``
0.7.5 (2020-12-17)
------------------
- minimum ``fhir.resources`` version is now ``6.0.0``, see it's associated changes at https://pypi.org/project/fhir.resources/6.0.0/.
- minimum ``fhirpath``version is now ``0.10.5``, see it's associated improvements and bug fixes at https://pypi.org/project/fhirpath/0.10.4/
- minimum ``plone.app.fhirfield``version is now ``4.2.0``, see it's associated improvements and bug fixes at https://pypi.org/project/plone.app.fhirfield/4.2.0/
0.7.4 (2020-11-19)
------------------
- Minimum required ``fhirpath`` version is now ``0.10.4`` (see it's changes log) and added compabilities.
0.7.3 (2020-10-24)
------------------
- new helper function ``json_body`` has been created, which is using ``orjson`` deserializer.
- ``FHIRModelServiceMixin`` is now more performance optimized.
0.7.2 (2020-10-06)
------------------
- Bundle resposne as ``dict`` option enable in ZCatalog based search.
0.7.1 (2020-10-05)
------------------
- ``fhirpath``minimum version has been updated, which includes minimum ``fhir.resources`` version ``6.0.0b5``.
- Improvements for ``FHIRModelServiceMixin`` as orjson serializer used.
0.7.0 (2020-09-25)
------------------
Improvements
- Issue #2 `ZCatalog based search should have option to return bundle type alongside as LazyBrain <https://github.com/nazrulworld/collective.fhirpath/issues/2>`_
- Supports all features from ``fhirpath`` 0.8.0.
- Elasticsearch Mappings JSON files are updated.
Fixes
- ``utils.FHIRModelServiceMixin`` can now handle ``list`` type data in response.
0.6.1 (2020-09-09)
------------------
- ``plone.app.fhirfield:default``has been added in dependency, so no need separete install of ``plone.app.fhirfield``.
0.6.0 (2020-09-09)
------------------
Improvements
- ``FHIRModelServiceMixin`` class has been available under ``utils`` module, which can be used with your ``plone.restapi``
services to response type as ``FhirModel`` aka pydantic's ``BaseModel`` or ``plone.app.fhirfield.FhirFieldValue`` object with the best possible effecient way.
0.5.0 (2020-08-18)
------------------
Improvements
- Supports the revolutionary version of `fhir.resources <https://pypi.org/project/fhir.resources/>`_ via `fhirpath <https://pypi.org/project/fhirpath/>`_
we may expect some refactor on your existing codebase because of some breaking changes, please see changes at ``fhir.resources``, ``plone.app.fhirfield`` and ``fhirpath``.
- Brings back support for Python version 3.6
- Three configurations (``fhirpath.es.index.mapping.nested_fields.limit``, ``fhirpath.es.index.mapping.depth.limit``, ``fhirpath.es.index.mapping.total_fields.limit``) based on plone registry has now been available.
0.4.0 (2020-05-15)
------------------
Breakings
- As a part of supporting latest ``fhirpath`` version (from ``0.6.1``), drop python version later than ``3.7.0``.
- ``ElasticsearchEngineFactory.__call__``'s argument name ``fhir_version`` changed to ``fhir_release``.
0.3.0 (2019-11-10)
------------------
Improvements
- ZCatalog featured fhir search added, from which you will get ZCatalog´s brain feature.
- ``FhirFieldIndex`` named PluginIndex is now available.
- FHIR ``STU3``and ``R4`` search mapping is now available.
- Others improvements that make able to use in production project (of course without guarantee.)
0.2.0 (2019-09-16)
------------------
- first working versions, with lots of improvements.
0.1.0 (2019-09-06)
------------------
- Initial release.
[nazrulworld]