edc-reportable


Nameedc-reportable JSON
Version 0.3.40 PyPI version JSON
download
home_pagehttps://github.com/clinicedc/edc-reportable
SummaryReportable clinic events, reference ranges, grading for clinicedc/edc projects
upload_time2024-11-20 22:32:34
maintainerNone
docs_urlNone
authorErik van Widenfelt
requires_python>=3.12
licenseGPL license, see LICENSE
keywords django edc daids reference ranges normal ranges clinicedc clinical trials
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            |pypi| |actions| |codecov|

edc-reportable
--------------

Reportable clinic events, reference ranges, grading

.. code-block:: python

    from dateutil.relativedelta import relativedelta
    from edc_utils import get_utcnow
    from edc_constants.constants import MALE, FEMALE
    from edc_reportable import ValueReferenceGroup, NormalReference, GradeReference
    from edc_reportable import site_reportables
    from edc_reportable.tests.reportables import normal_data, grading_data

Create a group for each test:

.. code-block:: python

    neutrophils = ValueReferenceGroup(name='neutrophils')

A normal reference is declared like this:

.. code-block:: python

    ref = NormalReference(
        name='neutrophils',
        lower=2.5,
        upper=7.5,
        units='10e9/L',
        age_lower=18,
        age_upper=99,
        age_units='years',
        gender=[MALE, FEMALE])

    >>> ref
    NormalReference(neutrophils, 2.5<x<7.5 10e9/L MF, 18<AGE<99 years)

And added to a group like this:

.. code-block:: python

    neutrophils.add_normal(ref)

Add as many normal references in a group as you like, just ensure the ``lower`` and ``upper`` boundaries don't overlap.

 **Note**: If the lower and upper values of a normal reference overlap
 with another normal reference in the same group, a ``BoundaryOverlap``
 exception will be raised when the value is evaluated.
 Catch this in your tests.

A grading reference is declared like this:

.. code-block:: python

    g3 = GradeReference(
        name='neutrophils',
        grade=3,
        lower=0.4,
        lower_inclusive=True,
        upper=0.59,
        upper_inclusive=True,
        units='10e9/L',
        age_lower=18,
        age_upper=99,
        age_units='years',
        gender=[MALE, FEMALE])

    >>> g3
    GradeReference(neutrophils, 0.4<=x<=0.59 in 10e9/L GRADE 3, MF, 18<AGE<99 in years) GRADE 3)

    or using lower / upper limits of normal:

    g3 = GradeReference(
        name="amylase",
        grade=1,
        lower="3.0*ULN",
        upper="5.0*ULN",
        lower_inclusive=True,
        upper_inclusive=False,
        units=IU_LITER,
        gender=MALE,
        normal_references={MALE: [normal_reference]},
        **adult_age_options)

    >>> g3
    GradeReference(amylase, 375.0<=x<625.0 IU/L GRADE 3) GRADE 3)

And added to the group like this:

.. code-block:: python

    neutrophils.add_grading(g3)

Declare and add a ``GradeReference`` for each reportable grade of the test.

 **Note**: If the lower and upper values of a grade reference overlap
 with another grade reference in the same group, a ``BoundaryOverlap``
 exception will be raised when the value is evaluated.
 Catch this in your tests.


Declaring with ``parse``
========================

You may find using ``parse`` somewhat simplifies the declaration where ``lower``, ``lower_inclusive``, ``upper`` and ``upper_inclusive`` can be written as a phrase, like ``13.5<=x<=17.5``. For example:

.. code-block:: python

    age_opts = dict(
        age_lower=18,
        age_upper=120,
        age_units='years',
        age_lower_inclusive=True,
        age_upper_inclusive=True)

    normal_data = {
        'haemoglobin': [
            p('13.5<=x<=17.5', units=GRAMS_PER_DECILITER,
              gender=[MALE], **age_opts),
            p('12.0<=x<=15.5', units=GRAMS_PER_DECILITER, gender=[FEMALE], **age_opts)],
         ...
    }


Registering with ``site_reportables``
=====================================

Once you have declared all your references, register them

.. code-block:: python

    site_reportables.register(
        name='my_project',
        normal_data=normal_data,
        grading_data=grading_data)



**Important**:
 Writing out references is prone to error. It is better to declare a
 dictionary of normal references and grading references. Use the ``parse`` function
 so that you can use a phrase like ``13.5<=x<=17.5`` instead of a listing attributes.
 There are examples of complete ``normal_data`` and ``grading_data`` in the tests.
 See``edc_reportable.tests.reportables``.

Attempting to grade a value without grading data
++++++++++++++++++++++++++++++++++++++++++++++++
If a value is pased to the evaluator and no grading data exists in the reference lists for
that test, an exception is raised.

Limiting what is "gradeable" for your project
+++++++++++++++++++++++++++++++++++++++++++++
The default tables have grading data for grades 1-4. The evaluator will grade any value
if there is grading data. You can prevent the evaluator from considering grades by passing
``reportable_grades`` when you register the normal and grading data.

For example:

.. code-block:: python

    site_reportables.register(
        name='my_project',
        normal_data=normal_data,
        grading_data=grading_data,
        reportable_grades=[GRADE3, GRADE4],
    )

In the above, by explicitly passing a list of grades, the evaluator will only raise an
exception for grades 3 and 4. If a value meets the criteria for grade 1 or 2, it will be ignored.

Declaring minor exceptions
++++++++++++++++++++++++++

Minor exceptions can be specified using the parameter ``reportable_grades_exceptions``.
For example, you wish to report grades 2,3,4 for Serum Amylase
but grades 3,4 for everything else. You would register as follows:

.. code-block:: python

    site_reportables.register(
        name='my_project',
        normal_data=normal_data,
        grading_data=grading_data,
        reportable_grades=[GRADE3, GRADE4],
        reportable_grades_exceptions={"amylase": [GRADE2, GRADE3, GRADE4]}
    )



Exporting the reference tables
++++++++++++++++++++++++++++++

You can export your declared references to CSV for further inspection

.. code-block:: python

    >>> site_reportables.to_csv(name='my_project', path='~/')

    ('/Users/erikvw/my_project_normal_ranges.csv',
    '/Users/erikvw/my_project_grading.csv')

Using your reportables
======================

In your code, get the references by collection name:

.. code-block:: python

    my_project_reportables = site_reportables.get('my_project')

    neutrophil = my_project_reportables.get('neutrophil')

    report_datetime = get_utcnow()
    dob = (report_datetime - relativedelta(years=25)).date()

Check a normal value
====================

If a value is normal, ``get_normal`` returns the ``NormalReference`` instance that matched with the value.

.. code-block:: python

    # evaluate a normal value
    normal = neutrophil.get_normal(
        value=3.5, units='10^9/L',
        gender=MALE, dob=dob, report_datetime=report_datetime)

    # returns a normal object with information about the range selected
    >>> normal.description
    '2.5<=3.5<=7.5 10^9/L MF, 18<=AGE years'

Check an abnormal value
=======================

If a value is abnormal, ``get_normal`` returns ``None``.

.. code-block:: python

    # evaluate an abnormal value
    opts = dict(
        units='10^9/L',
        gender=MALE, dob=dob,
        report_datetime=report_datetime)
    normal = neutrophil.get_normal(value=0.3, **opts)

    # returns None
    >>> if not normal:
            print('abnormal')
    'abnormal'

To show which ranges the value was evaluated against

.. code-block:: python

    # use same options for units, gender, dob, report_datetime
    >>> neutrophil.get_normal_description(**opts)
    ['2.5<=x<=7.5 10^9/L MF, 18<=AGE years']

Check if a value is "reportable"
================================

.. code-block:: python

    grade = neutrophil.get_grade(
        value=0.43, units='10^9/L',
        gender=MALE, dob=dob, report_datetime=report_datetime)

    >>> grade.grade
    3

    >>> grade.description
    '0.4<=0.43<=0.59 10^9/L GRADE 3'

    grade = neutrophil.get_grade(
        value=0.3, units='10^9/L',
        gender=MALE, dob=dob, report_datetime=report_datetime)

    >>> grade.grade
    4

    >>> grade.description
    '0.3<0.4 10^9/L GRADE 4'

If the value is not evaluated against any reportable ranges, a ``NotEvaluated`` exception is raised

.. code-block:: python

    # call with the wrong units

    >>> grade = neutrophil.get_grade(
            value=0.3, units='mmol/L',
            gender=MALE, dob=dob, report_datetime=report_datetime)

        NotEvaluated: neutrophil value not graded. No reference range found ...

.. |pypi| image:: https://img.shields.io/pypi/v/edc-reportable.svg
    :target: https://pypi.python.org/pypi/edc-reportable

.. |actions| image:: https://github.com/clinicedc/edc-reportable/actions/workflows/build.yml/badge.svg
  :target: https://github.com/clinicedc/edc-reportable/actions/workflows/build.yml

.. |codecov| image:: https://codecov.io/gh/clinicedc/edc-reportable/branch/develop/graph/badge.svg
  :target: https://codecov.io/gh/clinicedc/edc-reportable

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/clinicedc/edc-reportable",
    "name": "edc-reportable",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.12",
    "maintainer_email": null,
    "keywords": "django, edc, DAIDS, reference ranges, normal ranges, clinicedc, clinical trials",
    "author": "Erik van Widenfelt",
    "author_email": "ew2789@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/98/47/85d00daaee8256f91927454310b50ca57e8e04917e8604beb3db29dab44f/edc_reportable-0.3.40.tar.gz",
    "platform": null,
    "description": "|pypi| |actions| |codecov|\n\nedc-reportable\n--------------\n\nReportable clinic events, reference ranges, grading\n\n.. code-block:: python\n\n    from dateutil.relativedelta import relativedelta\n    from edc_utils import get_utcnow\n    from edc_constants.constants import MALE, FEMALE\n    from edc_reportable import ValueReferenceGroup, NormalReference, GradeReference\n    from edc_reportable import site_reportables\n    from edc_reportable.tests.reportables import normal_data, grading_data\n\nCreate a group for each test:\n\n.. code-block:: python\n\n    neutrophils = ValueReferenceGroup(name='neutrophils')\n\nA normal reference is declared like this:\n\n.. code-block:: python\n\n    ref = NormalReference(\n        name='neutrophils',\n        lower=2.5,\n        upper=7.5,\n        units='10e9/L',\n        age_lower=18,\n        age_upper=99,\n        age_units='years',\n        gender=[MALE, FEMALE])\n\n    >>> ref\n    NormalReference(neutrophils, 2.5<x<7.5 10e9/L MF, 18<AGE<99 years)\n\nAnd added to a group like this:\n\n.. code-block:: python\n\n    neutrophils.add_normal(ref)\n\nAdd as many normal references in a group as you like, just ensure the ``lower`` and ``upper`` boundaries don't overlap.\n\n **Note**: If the lower and upper values of a normal reference overlap\n with another normal reference in the same group, a ``BoundaryOverlap``\n exception will be raised when the value is evaluated.\n Catch this in your tests.\n\nA grading reference is declared like this:\n\n.. code-block:: python\n\n    g3 = GradeReference(\n        name='neutrophils',\n        grade=3,\n        lower=0.4,\n        lower_inclusive=True,\n        upper=0.59,\n        upper_inclusive=True,\n        units='10e9/L',\n        age_lower=18,\n        age_upper=99,\n        age_units='years',\n        gender=[MALE, FEMALE])\n\n    >>> g3\n    GradeReference(neutrophils, 0.4<=x<=0.59 in 10e9/L GRADE 3, MF, 18<AGE<99 in years) GRADE 3)\n\n    or using lower / upper limits of normal:\n\n    g3 = GradeReference(\n        name=\"amylase\",\n        grade=1,\n        lower=\"3.0*ULN\",\n        upper=\"5.0*ULN\",\n        lower_inclusive=True,\n        upper_inclusive=False,\n        units=IU_LITER,\n        gender=MALE,\n        normal_references={MALE: [normal_reference]},\n        **adult_age_options)\n\n    >>> g3\n    GradeReference(amylase, 375.0<=x<625.0 IU/L GRADE 3) GRADE 3)\n\nAnd added to the group like this:\n\n.. code-block:: python\n\n    neutrophils.add_grading(g3)\n\nDeclare and add a ``GradeReference`` for each reportable grade of the test.\n\n **Note**: If the lower and upper values of a grade reference overlap\n with another grade reference in the same group, a ``BoundaryOverlap``\n exception will be raised when the value is evaluated.\n Catch this in your tests.\n\n\nDeclaring with ``parse``\n========================\n\nYou may find using ``parse`` somewhat simplifies the declaration where ``lower``, ``lower_inclusive``, ``upper`` and ``upper_inclusive`` can be written as a phrase, like ``13.5<=x<=17.5``. For example:\n\n.. code-block:: python\n\n    age_opts = dict(\n        age_lower=18,\n        age_upper=120,\n        age_units='years',\n        age_lower_inclusive=True,\n        age_upper_inclusive=True)\n\n    normal_data = {\n        'haemoglobin': [\n            p('13.5<=x<=17.5', units=GRAMS_PER_DECILITER,\n              gender=[MALE], **age_opts),\n            p('12.0<=x<=15.5', units=GRAMS_PER_DECILITER, gender=[FEMALE], **age_opts)],\n         ...\n    }\n\n\nRegistering with ``site_reportables``\n=====================================\n\nOnce you have declared all your references, register them\n\n.. code-block:: python\n\n    site_reportables.register(\n        name='my_project',\n        normal_data=normal_data,\n        grading_data=grading_data)\n\n\n\n**Important**:\n Writing out references is prone to error. It is better to declare a\n dictionary of normal references and grading references. Use the ``parse`` function\n so that you can use a phrase like ``13.5<=x<=17.5`` instead of a listing attributes.\n There are examples of complete ``normal_data`` and ``grading_data`` in the tests.\n See``edc_reportable.tests.reportables``.\n\nAttempting to grade a value without grading data\n++++++++++++++++++++++++++++++++++++++++++++++++\nIf a value is pased to the evaluator and no grading data exists in the reference lists for\nthat test, an exception is raised.\n\nLimiting what is \"gradeable\" for your project\n+++++++++++++++++++++++++++++++++++++++++++++\nThe default tables have grading data for grades 1-4. The evaluator will grade any value\nif there is grading data. You can prevent the evaluator from considering grades by passing\n``reportable_grades`` when you register the normal and grading data.\n\nFor example:\n\n.. code-block:: python\n\n    site_reportables.register(\n        name='my_project',\n        normal_data=normal_data,\n        grading_data=grading_data,\n        reportable_grades=[GRADE3, GRADE4],\n    )\n\nIn the above, by explicitly passing a list of grades, the evaluator will only raise an\nexception for grades 3 and 4. If a value meets the criteria for grade 1 or 2, it will be ignored.\n\nDeclaring minor exceptions\n++++++++++++++++++++++++++\n\nMinor exceptions can be specified using the parameter ``reportable_grades_exceptions``.\nFor example, you wish to report grades 2,3,4 for Serum Amylase\nbut grades 3,4 for everything else. You would register as follows:\n\n.. code-block:: python\n\n    site_reportables.register(\n        name='my_project',\n        normal_data=normal_data,\n        grading_data=grading_data,\n        reportable_grades=[GRADE3, GRADE4],\n        reportable_grades_exceptions={\"amylase\": [GRADE2, GRADE3, GRADE4]}\n    )\n\n\n\nExporting the reference tables\n++++++++++++++++++++++++++++++\n\nYou can export your declared references to CSV for further inspection\n\n.. code-block:: python\n\n    >>> site_reportables.to_csv(name='my_project', path='~/')\n\n    ('/Users/erikvw/my_project_normal_ranges.csv',\n    '/Users/erikvw/my_project_grading.csv')\n\nUsing your reportables\n======================\n\nIn your code, get the references by collection name:\n\n.. code-block:: python\n\n    my_project_reportables = site_reportables.get('my_project')\n\n    neutrophil = my_project_reportables.get('neutrophil')\n\n    report_datetime = get_utcnow()\n    dob = (report_datetime - relativedelta(years=25)).date()\n\nCheck a normal value\n====================\n\nIf a value is normal, ``get_normal`` returns the ``NormalReference`` instance that matched with the value.\n\n.. code-block:: python\n\n    # evaluate a normal value\n    normal = neutrophil.get_normal(\n        value=3.5, units='10^9/L',\n        gender=MALE, dob=dob, report_datetime=report_datetime)\n\n    # returns a normal object with information about the range selected\n    >>> normal.description\n    '2.5<=3.5<=7.5 10^9/L MF, 18<=AGE years'\n\nCheck an abnormal value\n=======================\n\nIf a value is abnormal, ``get_normal`` returns ``None``.\n\n.. code-block:: python\n\n    # evaluate an abnormal value\n    opts = dict(\n        units='10^9/L',\n        gender=MALE, dob=dob,\n        report_datetime=report_datetime)\n    normal = neutrophil.get_normal(value=0.3, **opts)\n\n    # returns None\n    >>> if not normal:\n            print('abnormal')\n    'abnormal'\n\nTo show which ranges the value was evaluated against\n\n.. code-block:: python\n\n    # use same options for units, gender, dob, report_datetime\n    >>> neutrophil.get_normal_description(**opts)\n    ['2.5<=x<=7.5 10^9/L MF, 18<=AGE years']\n\nCheck if a value is \"reportable\"\n================================\n\n.. code-block:: python\n\n    grade = neutrophil.get_grade(\n        value=0.43, units='10^9/L',\n        gender=MALE, dob=dob, report_datetime=report_datetime)\n\n    >>> grade.grade\n    3\n\n    >>> grade.description\n    '0.4<=0.43<=0.59 10^9/L GRADE 3'\n\n    grade = neutrophil.get_grade(\n        value=0.3, units='10^9/L',\n        gender=MALE, dob=dob, report_datetime=report_datetime)\n\n    >>> grade.grade\n    4\n\n    >>> grade.description\n    '0.3<0.4 10^9/L GRADE 4'\n\nIf the value is not evaluated against any reportable ranges, a ``NotEvaluated`` exception is raised\n\n.. code-block:: python\n\n    # call with the wrong units\n\n    >>> grade = neutrophil.get_grade(\n            value=0.3, units='mmol/L',\n            gender=MALE, dob=dob, report_datetime=report_datetime)\n\n        NotEvaluated: neutrophil value not graded. No reference range found ...\n\n.. |pypi| image:: https://img.shields.io/pypi/v/edc-reportable.svg\n    :target: https://pypi.python.org/pypi/edc-reportable\n\n.. |actions| image:: https://github.com/clinicedc/edc-reportable/actions/workflows/build.yml/badge.svg\n  :target: https://github.com/clinicedc/edc-reportable/actions/workflows/build.yml\n\n.. |codecov| image:: https://codecov.io/gh/clinicedc/edc-reportable/branch/develop/graph/badge.svg\n  :target: https://codecov.io/gh/clinicedc/edc-reportable\n",
    "bugtrack_url": null,
    "license": "GPL license, see LICENSE",
    "summary": "Reportable clinic events, reference ranges, grading for clinicedc/edc projects",
    "version": "0.3.40",
    "project_urls": {
        "Homepage": "https://github.com/clinicedc/edc-reportable"
    },
    "split_keywords": [
        "django",
        " edc",
        " daids",
        " reference ranges",
        " normal ranges",
        " clinicedc",
        " clinical trials"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "1c04b30a89c09aa78428c439996f37da6d600dadeb3af928a688a9e1ec3052ec",
                "md5": "3d36a6fc853de80cadc16229cb95f0a9",
                "sha256": "8fe4a2d6457b079d4032603913732148977b35cee709468e7bf408bba9b64cee"
            },
            "downloads": -1,
            "filename": "edc_reportable-0.3.40-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "3d36a6fc853de80cadc16229cb95f0a9",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.12",
            "size": 60359,
            "upload_time": "2024-11-20T22:32:32",
            "upload_time_iso_8601": "2024-11-20T22:32:32.307564Z",
            "url": "https://files.pythonhosted.org/packages/1c/04/b30a89c09aa78428c439996f37da6d600dadeb3af928a688a9e1ec3052ec/edc_reportable-0.3.40-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "984785d00daaee8256f91927454310b50ca57e8e04917e8604beb3db29dab44f",
                "md5": "3b5cca3e758e458369cb0037cfee72c2",
                "sha256": "eb535717652f639c87b5987fd9aca0c5973d8300f56590067f22155f152233a9"
            },
            "downloads": -1,
            "filename": "edc_reportable-0.3.40.tar.gz",
            "has_sig": false,
            "md5_digest": "3b5cca3e758e458369cb0037cfee72c2",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.12",
            "size": 53638,
            "upload_time": "2024-11-20T22:32:34",
            "upload_time_iso_8601": "2024-11-20T22:32:34.171101Z",
            "url": "https://files.pythonhosted.org/packages/98/47/85d00daaee8256f91927454310b50ca57e8e04917e8604beb3db29dab44f/edc_reportable-0.3.40.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-11-20 22:32:34",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "clinicedc",
    "github_project": "edc-reportable",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "edc-reportable"
}
        
Elapsed time: 0.38490s