edc-metadata


Nameedc-metadata JSON
Version 0.3.33 PyPI version JSON
download
home_pagehttps://github.com/clinicedc/edc-metadata
SummaryAdd a metadata layer to data entry
upload_time2022-06-29 09:02:22
maintainer
docs_urlNone
authorErik van Widenfelt
requires_python>=3.8
licenseGPL license, see LICENSE
keywords django edc clinicedc clinical trials
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            |pypi| |actions| |codecov| |downloads|

edc_metadata
------------

``edc-metadata`` puts a "metadata" layer on top of your data collection forms, namely CRFs and Requisitions. The "metadata" is used on the data entry dashboard (see also `edc_dashboard`). The metadata may be queried directly by a data manager to review the completion status of CRF and Requisition forms.

* Metadata is stored in two models, ``CrfMetaData`` and ``RequisitionMetaData``. One metadata record is created per form per visit. Metadata is only created for the data collection forms of a visit as defined in the ``visit schedule``.
* Metadata model instances are created for each visit when the ``visit`` model is saved. ``edc_metadata`` reads from the ``visit_schedule`` to decide which data collection form metadata model instances to create for a visit. (Note: See ``edc_visit_schedule``)
* Metadata is guaranteed to exist for every form defined in a visit after the visit form has been submitted.


 Note: This module is coupled with ``edc_metadata_files`` and ``edc_reference``.

``metadata`` model instances
============================

Each  ``metadata`` model instance, ``CrfMetadata`` or ``RequisitionMetadata``, is managed by an actual CRF or REQUISITION model listed in the ``visit_schedule``.
``CrfMetadata` model instances are created for each CRF listed in the visit schedule. That is, if the visit schedule schedules a CRF for 5 different visits, 5 ``CrfMetadata` model instances will eventually be created. Metadata model instances are created when the ``visit`` model for a timepoint is saved.
When you  ``save`` a CRF within a visit, the ``entry_status`` of the the metadata instance's it manages is updated from ``REQUIRED`` to ``KEYED``.

    The same applies to ``RequisitionMetadata`` for REQUISITIONS.

Entry status
============

By default the ``entry_status`` field attribute is set to ``REQUIRED``. You can change the default of each CRF to ``NOT_REQUIRED`` in your declaration in the visit schedule.  See ``visit_schedule.crf``.

    The same applies to REQUISITIONS.


``metadata_rules`` manipulate ``metadata`` model instances
==========================================================

``metadata_rules`` are declared to manipulate ``metadata`` model instances. The rules change the ``entry_status`` field attribute from ``REQUIRED`` to ``NOT_REQUIRED`` or visa-versa.
If the manager of the metadata instance, the CRF or REQUISITION model instance, exists, the entry status is updated to ``KEYED``and the ``metadata_rules`` targeting the metadata instance are ignored.
``metadata rules`` are run on each ``save`` of the visit and managing model instances.
If a value on some other form implies that your form should not be completed, your form's metadata "entry_status" will change from REQUIRED to NOT REQUIRED upon ``save`` of the other form.
Metadata is ``updated`` through a ``post_save`` signal that re-runs the ``metadata rules``.

    See also ``edc_metadata_rules``


``metadata_rules`` access data through ``edc_reference``
========================================================

In order to de-couple the ``metadata rules`` from each managing model class, ``metadata rules`` access the field values for each model via ``edc_reference`` instead of directly from the model. It would be too complex and resource intensive to directly query each model separately every time the ``metadata rules`` are run.
Each managing model class referenced by ``metadata rules`` must be declared with the ``ReferenceModelMixin`` and the fields list registered with ``site_reference_configs`` global. This applies to all managing models, ``source_model`` and ``target_models``.

    See also ``edc_reference``


Getting started
---------------

Models: Visit, Crfs and Requisitions
====================================

Let's prepare the models that will be used in the scheduled data collection. These models are your visit models, crf models and requisition models.

Your application also has one or more ``Visit`` models. Each visit model is declared with the ``CreatesMetadataModelMixin``:

.. code-block:: python

    class SubjectVisit(CreatesMetadataModelMixin, PreviousVisitMixin, VisitModelMixin,
                       RequiresConsentModelMixin, BaseUuidModel):

        appointment = models.OneToOneField(Appointment)

        class Meta(RequiresConsentModelMixin.Meta):
            app_label = 'example'

Your ``Crf`` models are declared with the ``CrfModelMixin`` and ``ReferenceModelMixin``:

.. code-block:: python

    class CrfOne(CrfModelMixin, ReferenceModelMixin, BaseUuidModel):

        subject_visit = models.ForeignKey(SubjectVisit)

        f1 = models.CharField(max_length=10, default='erik')

        class Meta:
            app_label = 'example'

Your ``Requisition`` models are declared with the ``RequisitionModelMixin`` and ``ReferenceModelMixin``:

.. code-block:: python

    class SubjectRequisition(RequisitionModelMixin, ReferenceModelMixin, BaseUuidModel):

        subject_visit = models.ForeignKey(SubjectVisit)

        f1 = models.CharField(max_length=10, default='erik')

        class Meta:
            app_label = 'example'

metadata_rules
--------------

As described above, ``metadata_rules`` manipulate the ``entry_status`` of CRF and Requisition ``metadata``. ``metadata_rules`` are registered to ``site_metadata_rules`` in module ``metadata_rules.py``. Place this file in the root of your app. Each app can have one ``metadata_rules.py``.

 See also ``edc_metadata_rules``

autodiscovering metadata_rules
==============================

AppConfig will ``autodiscover`` the rule files and print to the console whatever it finds:

* checking for metadata_rules ...
* registered metadata_rules from application 'edc_example'

Inspect metadata_rules
======================

Inspect ``metadata_rules`` from the site registry:

.. code-block:: python

    >>> from edc_metadata.rules.site_metadata_rules import site_metadata_rules

    >>> for rule_groups in site_metadata_rules.registry.values():
    >>>    for rule_group in rule_groups:
    >>>        print(rule_group._meta.rules)

    (<edc_example.rule_groups.ExampleRuleGroup: crfs_male>, <edc_example.rule_groups.ExampleRuleGroup: crfs_female>)
    (<edc_example.rule_groups.ExampleRuleGroup2: bicycle>, <edc_example.rule_groups.ExampleRuleGroup2: car>)

Writing metadata_rules
======================

``metadata_rules`` are declared in a ``RuleGroup``. The syntax is similar to the ``django`` model class.

Let's start with an example from the perspective of the person entering subject data. On a dashboard there are 4 forms (models) to complete. The "rule" is that if the subject is male, only the first two forms should be complete. If the subject is female, only the last two forms should be complete. So the metadata should show:

**Subject is Male:**

* crf_one - REQUIRED, link to entry screen available
* crf_two - REQUIRED, link to entry screen available
* crf_three - NOT REQUIRED, link to entry screen not available
* crf_four - NOT REQUIRED, link to entry screen not available

**Subject is Female:**

* crf_one - NOT REQUIRED
* crf_two - NOT REQUIRED
* crf_three - REQUIRED
* crf_four - REQUIRED

A ``Rule`` that changes the ``metadata`` if the subject is male would look like this:

.. code-block:: python

    crfs_male = CrfRule(
        predicate=P('gender', 'eq', 'MALE'),
        consequence=REQUIRED,
        alternative=NOT_REQUIRED,
        target_models=['crfone', 'crftwo'])

The rule above has a ``predicate`` that evaluates to True or not. If ``gender`` is equal to ``MALE`` the consequence is ``REQUIRED``, else ``NOT_REQUIRED``. For this rule, for a MALE, the metadata ``entry_status`` for ``crf_one`` and ``crf_two`` will be updated to ``REQUIRED``. For a FEMALE both will be set to ``NOT_REQUIRED``.

Rules are declared as attributes of a RuleGroup much like fields in a ``django`` model:

.. code-block:: python

    @register()
    class ExampleRuleGroup(CrfRuleGroup):

        crfs_male = CrfRule(
            predicate=P('gender', 'eq', 'MALE'),
            consequence=REQUIRED,
            alternative=NOT_REQUIRED,
            target_models=['crfone', 'crftwo'])

        crfs_female = CrfRule(
            predicate=P('gender', 'eq', FEMALE),
            consequence=REQUIRED,
            alternative=NOT_REQUIRED,
            target_models=['crfthree', 'crffour'])

        class Meta:
            app_label = 'edc_example'

``RuleGroup`` class declarations are placed in file ``metadata_rules.py`` in the root of your application. They are registered in the order in which they appear in the file. All rule groups are available from the ``site_metadata_rules`` global.

More on Rules
=============

The rule ``consequence`` and ``alternative`` except these values:

.. code-block:: python

    from edc_metadata.constants import REQUIRED, NOT_REQUIRED
    from edc_metadata.rules.constants import DO_NOTHING

* REQUIRED
* NOT_REQUIRED
* DO_NOTHING

It is recommended to write the logic so that the ``consequence`` is REQUIRED if the ``predicate`` evaluates to  ``True``.

In the examples above, the rule ``predicate`` can only access values that can be found on the subjects's current ``visit`` instance or ``registered_subject`` instance. If the value you need for the rule ``predicate`` is not on either of those instances, you can pass a ``source_model``. With the ``source_model`` declared you would have these data available:

* current visit model instance
* registered subject (see ``edc_registration``)
* source model instance for the current visit

Let's say the rules changes and instead of refering to ``gender`` (male/female) you wish to refer to the value field of ``favorite_transport`` on model ``CrfTransport``. ``favorite_transport`` can be "car" or "bicycle". You want the first rule ``predicate`` to read as:

* "If ``favorite_transport`` is equal to ``bicycle`` then set the metadata ``entry_status`` for ``crf_one`` and ``crf_two`` to REQUIRED, if not, set both to NOT_REQUIRED"

and the second to read as:

* "If ``favorite_transport`` is equal to ``car`` then set the metadata ``entry_status`` for ``crf_three`` and ``crf_four`` to REQUIRED, if not, set both to NOT_REQUIRED".

The field for car/bicycle, ``favorite_transport`` is on model ``CrfTransport``. The RuleGroup might look like this:

.. code-block:: python

    @register()
    class ExampleRuleGroup(RuleGroup):

        bicycle = CrfRule(
            predicate=P('favorite_transport', 'eq', 'bicycle'),
            consequence=REQUIRED,
            alternative=NOT_REQUIRED,
            target_models=['crfone', 'crftwo'])

        car = CrfRule(
            predicate=P('favorite_transport', 'eq', car),
            consequence=REQUIRED,
            alternative=NOT_REQUIRED,
            target_models=['crfthree', 'crffour'])

        class Meta:
            app_label = 'edc_example'
            source_model = 'CrfTransport'

Note that ``CrfTransport`` is a ``crf`` model in the Edc. That is, it has a ``foreign key`` to the visit model. Internally the query will be constructed like this:

.. code-block:: python

    # source model instance for the current visit
    visit_attr = 'subject_visit'
    source_obj = CrfTansport.objects.get(**{visit_attr: visit})

    # queryset of source model for the current subject_identifier
    visit_attr = 'subject_visit'
    source_qs = CrfTansport.objects.filter(**{'{}__subject_identifier'.format(visit_attr): subject_identifier})

* If the source model instance does not exist, the rules in the rule group will not run.
* If the target model instance exists, no rule can change it's metadata from KEYED.

More Complex Rule Predicates
============================

There are two provided classes for the rule ``predicate``, ``P`` and ``PF``. With ``P`` you can make simple rule predicates like those used in the examples above. All standard opertors can be used. For example:

.. code-block:: python

    predicate = P('gender', 'eq', 'MALE')
    predicate = P('referral_datetime', 'is not', None)
    predicate = P('age', '<=', 64)

If the logic needs to a bit more complicated, the ``PF`` class allows you to pass a ``lambda`` function directly:

.. code-block:: python

    predicate = PF('age', func=lambda x: True if x >= 18 and x <= 64 else False)

    predicate = PF('age', 'gender', func=lambda x, y: True if x >= 18 and x <= 64 and y == MALE else False)

If the logic needs to be more complicated than is recommended for a simple lambda, you can just pass a function. When writing your function just remember that the rule ``predicate`` must always evaluate to True or False.

.. code-block:: python

    def my_func(visit, registered_subject, source_obj, source_qs):
        if source_obj.married and registered_subject.gender == FEMALE:
            return True
        return False

    predicate = my_func


Rule Group Order
================

    **IMPORTANT**: RuleGroups are evaluated in the order they are registered and the rules within each rule group are evaluated in the order they are declared on the RuleGroup.


Testing
=======

Since the order in which rules run matters, it is essential to test the rules together. See ``tests`` for some examples. When writing tests it may be helpful to know the following:

* the standard Edc model configuration assumes you have consent->enrollment->appointments->visit->crfs and requisitions.
* rules can be instected after boot up in the global registry ``site_metadata_rules``.
* all rules are run when the visit  is saved.

More examples
=============

See ``edc_example`` for working RuleGroups and how models are configured with the ``edc_metadata`` mixins. The ``tests`` in ``edc_metadata.rules`` use the rule group and model classes in ``edc_example``.


Notes on Edc
============

The standard Edc model configuration assumes you have a data entry flow like this:::

    consent->enrollment->appointment->visit (1000)->crfs and requisitions
                         appointment->visit (2000)->crfs and requisitions
                         appointment->visit (3000)->crfs and requisitions
                         appointment->visit (4000)->crfs and requisitions

You should also see the other dependencies, ``edc_consent``, ``edc_visit_schedule``, ``edc_appointment``, ``edc_visit_tracking``, ``edc_metadata``, etc.

Signals
=======

In the ``signals`` file:

**visit model ``post_save``:**

* Metadata is created for a particular visit and visit code, e.g. 1000, when the ``visit`` model is saved for a subject and visit code using the default ``entry_status`` configured in the ``visit_schedule``.
* Immediately after creating metadata, all rules for the ``app_label`` are run in order. The ``app_label`` is the ``app_label`` of the visit model.

**crf or requisition model ``post_save``:**

* the metadata instance for the crf/requisition is updated and then all rules are run.

**crf or requisition model ``post_delete``:**

* the metadata instance for the crf/requisition is reset to the default ``entry_status`` and then all rules are run.


Changing visit_schedule name and/ or schedule name
==================================================


If the visit_schedule_name or schedule_name changes, the existing metadata must be manually updated. For example;


.. code-block:: sql

    update edc_metadata_crfmetadata set visit_schedule_name='visit_schedule'
    where visit_schedule_name='old_visit_schedule';

    update edc_metadata_crfmetadata set schedule_name='schedule'
    where schedule_name='old_schedule';

    update edc_metadata_requisitionmetadata set visit_schedule_name='visit_schedule'
    where visit_schedule_name='old_visit_schedule';

    update edc_metadata_requisitionmetadata set schedule_name='schedule'
    where schedule_name='old_schedule';

You also need to update any existing enrollment and disenrollment model data. For example;

.. code-block:: sql

    update ambition_subject_enrollment set visit_schedule_name='visit_schedule'
    where visit_schedule_name='old_visit_schedule';

    update ambition_subject_enrollment set schedule_name='schedule'
    where schedule_name='old_schedule';

For any other table that use these fields:

.. code-block:: sql

    SELECT DISTINCT TABLE_NAME
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE COLUMN_NAME IN ('visit_schedule_name','schedule_name')
            AND TABLE_SCHEMA='edc';

A typical list of tables that need to be updated may look like this::

    +------------------------------------------+
    | TABLE_NAME                               |
    +------------------------------------------+
    | ambition_subject_disenrollment           |
    | ambition_subject_enrollment              |
    | ambition_subject_historicaldisenrollment |
    | ambition_subject_historicalenrollment    |
    | ambition_subject_historicalsubjectvisit  |
    | ambition_subject_subjectvisit            |
    | edc_appointment_appointment              |
    | edc_appointment_historicalappointment    |
    | edc_metadata_crfmetadata                 |
    | edc_metadata_requisitionmetadata         |
    +------------------------------------------+

In the code you need to update where the visit_schedule or schedule are hard coded.

* visit schedule, schedule
* Meta attributes on the enrollment and disenrollment models.




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

.. |actions| image:: https://github.com/clinicedc/edc-metadata/workflows/build/badge.svg?branch=develop
  :target: https://github.com/clinicedc/edc-metadata/actions?query=workflow:build

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

.. |downloads| image:: https://pepy.tech/badge/edc-metadata
   :target: https://pepy.tech/project/edc-metadata


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/clinicedc/edc-metadata",
    "name": "edc-metadata",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "django,edc,clinicedc,clinical trials",
    "author": "Erik van Widenfelt",
    "author_email": "ew2789@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/a4/b4/813ea4e0343dd0a41e8c2dc021d9f28637c67c5ad3e519e51feb87c131e7/edc-metadata-0.3.33.tar.gz",
    "platform": null,
    "description": "|pypi| |actions| |codecov| |downloads|\n\nedc_metadata\n------------\n\n``edc-metadata`` puts a \"metadata\" layer on top of your data collection forms, namely CRFs and Requisitions. The \"metadata\" is used on the data entry dashboard (see also `edc_dashboard`). The metadata may be queried directly by a data manager to review the completion status of CRF and Requisition forms.\n\n* Metadata is stored in two models, ``CrfMetaData`` and ``RequisitionMetaData``. One metadata record is created per form per visit. Metadata is only created for the data collection forms of a visit as defined in the ``visit schedule``.\n* Metadata model instances are created for each visit when the ``visit`` model is saved. ``edc_metadata`` reads from the ``visit_schedule`` to decide which data collection form metadata model instances to create for a visit. (Note: See ``edc_visit_schedule``)\n* Metadata is guaranteed to exist for every form defined in a visit after the visit form has been submitted.\n\n\n Note: This module is coupled with ``edc_metadata_files`` and ``edc_reference``.\n\n``metadata`` model instances\n============================\n\nEach  ``metadata`` model instance, ``CrfMetadata`` or ``RequisitionMetadata``, is managed by an actual CRF or REQUISITION model listed in the ``visit_schedule``.\n``CrfMetadata` model instances are created for each CRF listed in the visit schedule. That is, if the visit schedule schedules a CRF for 5 different visits, 5 ``CrfMetadata` model instances will eventually be created. Metadata model instances are created when the ``visit`` model for a timepoint is saved.\nWhen you  ``save`` a CRF within a visit, the ``entry_status`` of the the metadata instance's it manages is updated from ``REQUIRED`` to ``KEYED``.\n\n    The same applies to ``RequisitionMetadata`` for REQUISITIONS.\n\nEntry status\n============\n\nBy default the ``entry_status`` field attribute is set to ``REQUIRED``. You can change the default of each CRF to ``NOT_REQUIRED`` in your declaration in the visit schedule.  See ``visit_schedule.crf``.\n\n    The same applies to REQUISITIONS.\n\n\n``metadata_rules`` manipulate ``metadata`` model instances\n==========================================================\n\n``metadata_rules`` are declared to manipulate ``metadata`` model instances. The rules change the ``entry_status`` field attribute from ``REQUIRED`` to ``NOT_REQUIRED`` or visa-versa.\nIf the manager of the metadata instance, the CRF or REQUISITION model instance, exists, the entry status is updated to ``KEYED``and the ``metadata_rules`` targeting the metadata instance are ignored.\n``metadata rules`` are run on each ``save`` of the visit and managing model instances.\nIf a value on some other form implies that your form should not be completed, your form's metadata \"entry_status\" will change from REQUIRED to NOT REQUIRED upon ``save`` of the other form.\nMetadata is ``updated`` through a ``post_save`` signal that re-runs the ``metadata rules``.\n\n    See also ``edc_metadata_rules``\n\n\n``metadata_rules`` access data through ``edc_reference``\n========================================================\n\nIn order to de-couple the ``metadata rules`` from each managing model class, ``metadata rules`` access the field values for each model via ``edc_reference`` instead of directly from the model. It would be too complex and resource intensive to directly query each model separately every time the ``metadata rules`` are run.\nEach managing model class referenced by ``metadata rules`` must be declared with the ``ReferenceModelMixin`` and the fields list registered with ``site_reference_configs`` global. This applies to all managing models, ``source_model`` and ``target_models``.\n\n    See also ``edc_reference``\n\n\nGetting started\n---------------\n\nModels: Visit, Crfs and Requisitions\n====================================\n\nLet's prepare the models that will be used in the scheduled data collection. These models are your visit models, crf models and requisition models.\n\nYour application also has one or more ``Visit`` models. Each visit model is declared with the ``CreatesMetadataModelMixin``:\n\n.. code-block:: python\n\n    class SubjectVisit(CreatesMetadataModelMixin, PreviousVisitMixin, VisitModelMixin,\n                       RequiresConsentModelMixin, BaseUuidModel):\n\n        appointment = models.OneToOneField(Appointment)\n\n        class Meta(RequiresConsentModelMixin.Meta):\n            app_label = 'example'\n\nYour ``Crf`` models are declared with the ``CrfModelMixin`` and ``ReferenceModelMixin``:\n\n.. code-block:: python\n\n    class CrfOne(CrfModelMixin, ReferenceModelMixin, BaseUuidModel):\n\n        subject_visit = models.ForeignKey(SubjectVisit)\n\n        f1 = models.CharField(max_length=10, default='erik')\n\n        class Meta:\n            app_label = 'example'\n\nYour ``Requisition`` models are declared with the ``RequisitionModelMixin`` and ``ReferenceModelMixin``:\n\n.. code-block:: python\n\n    class SubjectRequisition(RequisitionModelMixin, ReferenceModelMixin, BaseUuidModel):\n\n        subject_visit = models.ForeignKey(SubjectVisit)\n\n        f1 = models.CharField(max_length=10, default='erik')\n\n        class Meta:\n            app_label = 'example'\n\nmetadata_rules\n--------------\n\nAs described above, ``metadata_rules`` manipulate the ``entry_status`` of CRF and Requisition ``metadata``. ``metadata_rules`` are registered to ``site_metadata_rules`` in module ``metadata_rules.py``. Place this file in the root of your app. Each app can have one ``metadata_rules.py``.\n\n See also ``edc_metadata_rules``\n\nautodiscovering metadata_rules\n==============================\n\nAppConfig will ``autodiscover`` the rule files and print to the console whatever it finds:\n\n* checking for metadata_rules ...\n* registered metadata_rules from application 'edc_example'\n\nInspect metadata_rules\n======================\n\nInspect ``metadata_rules`` from the site registry:\n\n.. code-block:: python\n\n    >>> from edc_metadata.rules.site_metadata_rules import site_metadata_rules\n\n    >>> for rule_groups in site_metadata_rules.registry.values():\n    >>>    for rule_group in rule_groups:\n    >>>        print(rule_group._meta.rules)\n\n    (<edc_example.rule_groups.ExampleRuleGroup: crfs_male>, <edc_example.rule_groups.ExampleRuleGroup: crfs_female>)\n    (<edc_example.rule_groups.ExampleRuleGroup2: bicycle>, <edc_example.rule_groups.ExampleRuleGroup2: car>)\n\nWriting metadata_rules\n======================\n\n``metadata_rules`` are declared in a ``RuleGroup``. The syntax is similar to the ``django`` model class.\n\nLet's start with an example from the perspective of the person entering subject data. On a dashboard there are 4 forms (models) to complete. The \"rule\" is that if the subject is male, only the first two forms should be complete. If the subject is female, only the last two forms should be complete. So the metadata should show:\n\n**Subject is Male:**\n\n* crf_one - REQUIRED, link to entry screen available\n* crf_two - REQUIRED, link to entry screen available\n* crf_three - NOT REQUIRED, link to entry screen not available\n* crf_four - NOT REQUIRED, link to entry screen not available\n\n**Subject is Female:**\n\n* crf_one - NOT REQUIRED\n* crf_two - NOT REQUIRED\n* crf_three - REQUIRED\n* crf_four - REQUIRED\n\nA ``Rule`` that changes the ``metadata`` if the subject is male would look like this:\n\n.. code-block:: python\n\n    crfs_male = CrfRule(\n        predicate=P('gender', 'eq', 'MALE'),\n        consequence=REQUIRED,\n        alternative=NOT_REQUIRED,\n        target_models=['crfone', 'crftwo'])\n\nThe rule above has a ``predicate`` that evaluates to True or not. If ``gender`` is equal to ``MALE`` the consequence is ``REQUIRED``, else ``NOT_REQUIRED``. For this rule, for a MALE, the metadata ``entry_status`` for ``crf_one`` and ``crf_two`` will be updated to ``REQUIRED``. For a FEMALE both will be set to ``NOT_REQUIRED``.\n\nRules are declared as attributes of a RuleGroup much like fields in a ``django`` model:\n\n.. code-block:: python\n\n    @register()\n    class ExampleRuleGroup(CrfRuleGroup):\n\n        crfs_male = CrfRule(\n            predicate=P('gender', 'eq', 'MALE'),\n            consequence=REQUIRED,\n            alternative=NOT_REQUIRED,\n            target_models=['crfone', 'crftwo'])\n\n        crfs_female = CrfRule(\n            predicate=P('gender', 'eq', FEMALE),\n            consequence=REQUIRED,\n            alternative=NOT_REQUIRED,\n            target_models=['crfthree', 'crffour'])\n\n        class Meta:\n            app_label = 'edc_example'\n\n``RuleGroup`` class declarations are placed in file ``metadata_rules.py`` in the root of your application. They are registered in the order in which they appear in the file. All rule groups are available from the ``site_metadata_rules`` global.\n\nMore on Rules\n=============\n\nThe rule ``consequence`` and ``alternative`` except these values:\n\n.. code-block:: python\n\n    from edc_metadata.constants import REQUIRED, NOT_REQUIRED\n    from edc_metadata.rules.constants import DO_NOTHING\n\n* REQUIRED\n* NOT_REQUIRED\n* DO_NOTHING\n\nIt is recommended to write the logic so that the ``consequence`` is REQUIRED if the ``predicate`` evaluates to  ``True``.\n\nIn the examples above, the rule ``predicate`` can only access values that can be found on the subjects's current ``visit`` instance or ``registered_subject`` instance. If the value you need for the rule ``predicate`` is not on either of those instances, you can pass a ``source_model``. With the ``source_model`` declared you would have these data available:\n\n* current visit model instance\n* registered subject (see ``edc_registration``)\n* source model instance for the current visit\n\nLet's say the rules changes and instead of refering to ``gender`` (male/female) you wish to refer to the value field of ``favorite_transport`` on model ``CrfTransport``. ``favorite_transport`` can be \"car\" or \"bicycle\". You want the first rule ``predicate`` to read as:\n\n* \"If ``favorite_transport`` is equal to ``bicycle`` then set the metadata ``entry_status`` for ``crf_one`` and ``crf_two`` to REQUIRED, if not, set both to NOT_REQUIRED\"\n\nand the second to read as:\n\n* \"If ``favorite_transport`` is equal to ``car`` then set the metadata ``entry_status`` for ``crf_three`` and ``crf_four`` to REQUIRED, if not, set both to NOT_REQUIRED\".\n\nThe field for car/bicycle, ``favorite_transport`` is on model ``CrfTransport``. The RuleGroup might look like this:\n\n.. code-block:: python\n\n    @register()\n    class ExampleRuleGroup(RuleGroup):\n\n        bicycle = CrfRule(\n            predicate=P('favorite_transport', 'eq', 'bicycle'),\n            consequence=REQUIRED,\n            alternative=NOT_REQUIRED,\n            target_models=['crfone', 'crftwo'])\n\n        car = CrfRule(\n            predicate=P('favorite_transport', 'eq', car),\n            consequence=REQUIRED,\n            alternative=NOT_REQUIRED,\n            target_models=['crfthree', 'crffour'])\n\n        class Meta:\n            app_label = 'edc_example'\n            source_model = 'CrfTransport'\n\nNote that ``CrfTransport`` is a ``crf`` model in the Edc. That is, it has a ``foreign key`` to the visit model. Internally the query will be constructed like this:\n\n.. code-block:: python\n\n    # source model instance for the current visit\n    visit_attr = 'subject_visit'\n    source_obj = CrfTansport.objects.get(**{visit_attr: visit})\n\n    # queryset of source model for the current subject_identifier\n    visit_attr = 'subject_visit'\n    source_qs = CrfTansport.objects.filter(**{'{}__subject_identifier'.format(visit_attr): subject_identifier})\n\n* If the source model instance does not exist, the rules in the rule group will not run.\n* If the target model instance exists, no rule can change it's metadata from KEYED.\n\nMore Complex Rule Predicates\n============================\n\nThere are two provided classes for the rule ``predicate``, ``P`` and ``PF``. With ``P`` you can make simple rule predicates like those used in the examples above. All standard opertors can be used. For example:\n\n.. code-block:: python\n\n    predicate = P('gender', 'eq', 'MALE')\n    predicate = P('referral_datetime', 'is not', None)\n    predicate = P('age', '<=', 64)\n\nIf the logic needs to a bit more complicated, the ``PF`` class allows you to pass a ``lambda`` function directly:\n\n.. code-block:: python\n\n    predicate = PF('age', func=lambda x: True if x >= 18 and x <= 64 else False)\n\n    predicate = PF('age', 'gender', func=lambda x, y: True if x >= 18 and x <= 64 and y == MALE else False)\n\nIf the logic needs to be more complicated than is recommended for a simple lambda, you can just pass a function. When writing your function just remember that the rule ``predicate`` must always evaluate to True or False.\n\n.. code-block:: python\n\n    def my_func(visit, registered_subject, source_obj, source_qs):\n        if source_obj.married and registered_subject.gender == FEMALE:\n            return True\n        return False\n\n    predicate = my_func\n\n\nRule Group Order\n================\n\n    **IMPORTANT**: RuleGroups are evaluated in the order they are registered and the rules within each rule group are evaluated in the order they are declared on the RuleGroup.\n\n\nTesting\n=======\n\nSince the order in which rules run matters, it is essential to test the rules together. See ``tests`` for some examples. When writing tests it may be helpful to know the following:\n\n* the standard Edc model configuration assumes you have consent->enrollment->appointments->visit->crfs and requisitions.\n* rules can be instected after boot up in the global registry ``site_metadata_rules``.\n* all rules are run when the visit  is saved.\n\nMore examples\n=============\n\nSee ``edc_example`` for working RuleGroups and how models are configured with the ``edc_metadata`` mixins. The ``tests`` in ``edc_metadata.rules`` use the rule group and model classes in ``edc_example``.\n\n\nNotes on Edc\n============\n\nThe standard Edc model configuration assumes you have a data entry flow like this:::\n\n    consent->enrollment->appointment->visit (1000)->crfs and requisitions\n                         appointment->visit (2000)->crfs and requisitions\n                         appointment->visit (3000)->crfs and requisitions\n                         appointment->visit (4000)->crfs and requisitions\n\nYou should also see the other dependencies, ``edc_consent``, ``edc_visit_schedule``, ``edc_appointment``, ``edc_visit_tracking``, ``edc_metadata``, etc.\n\nSignals\n=======\n\nIn the ``signals`` file:\n\n**visit model ``post_save``:**\n\n* Metadata is created for a particular visit and visit code, e.g. 1000, when the ``visit`` model is saved for a subject and visit code using the default ``entry_status`` configured in the ``visit_schedule``.\n* Immediately after creating metadata, all rules for the ``app_label`` are run in order. The ``app_label`` is the ``app_label`` of the visit model.\n\n**crf or requisition model ``post_save``:**\n\n* the metadata instance for the crf/requisition is updated and then all rules are run.\n\n**crf or requisition model ``post_delete``:**\n\n* the metadata instance for the crf/requisition is reset to the default ``entry_status`` and then all rules are run.\n\n\nChanging visit_schedule name and/ or schedule name\n==================================================\n\n\nIf the visit_schedule_name or schedule_name changes, the existing metadata must be manually updated. For example;\n\n\n.. code-block:: sql\n\n    update edc_metadata_crfmetadata set visit_schedule_name='visit_schedule'\n    where visit_schedule_name='old_visit_schedule';\n\n    update edc_metadata_crfmetadata set schedule_name='schedule'\n    where schedule_name='old_schedule';\n\n    update edc_metadata_requisitionmetadata set visit_schedule_name='visit_schedule'\n    where visit_schedule_name='old_visit_schedule';\n\n    update edc_metadata_requisitionmetadata set schedule_name='schedule'\n    where schedule_name='old_schedule';\n\nYou also need to update any existing enrollment and disenrollment model data. For example;\n\n.. code-block:: sql\n\n    update ambition_subject_enrollment set visit_schedule_name='visit_schedule'\n    where visit_schedule_name='old_visit_schedule';\n\n    update ambition_subject_enrollment set schedule_name='schedule'\n    where schedule_name='old_schedule';\n\nFor any other table that use these fields:\n\n.. code-block:: sql\n\n    SELECT DISTINCT TABLE_NAME\n        FROM INFORMATION_SCHEMA.COLUMNS\n        WHERE COLUMN_NAME IN ('visit_schedule_name','schedule_name')\n            AND TABLE_SCHEMA='edc';\n\nA typical list of tables that need to be updated may look like this::\n\n    +------------------------------------------+\n    | TABLE_NAME                               |\n    +------------------------------------------+\n    | ambition_subject_disenrollment           |\n    | ambition_subject_enrollment              |\n    | ambition_subject_historicaldisenrollment |\n    | ambition_subject_historicalenrollment    |\n    | ambition_subject_historicalsubjectvisit  |\n    | ambition_subject_subjectvisit            |\n    | edc_appointment_appointment              |\n    | edc_appointment_historicalappointment    |\n    | edc_metadata_crfmetadata                 |\n    | edc_metadata_requisitionmetadata         |\n    +------------------------------------------+\n\nIn the code you need to update where the visit_schedule or schedule are hard coded.\n\n* visit schedule, schedule\n* Meta attributes on the enrollment and disenrollment models.\n\n\n\n\n.. |pypi| image:: https://img.shields.io/pypi/v/edc-metadata.svg\n    :target: https://pypi.python.org/pypi/edc-metadata\n\n.. |actions| image:: https://github.com/clinicedc/edc-metadata/workflows/build/badge.svg?branch=develop\n  :target: https://github.com/clinicedc/edc-metadata/actions?query=workflow:build\n\n.. |codecov| image:: https://codecov.io/gh/clinicedc/edc-metadata/branch/develop/graph/badge.svg\n  :target: https://codecov.io/gh/clinicedc/edc-metadata\n\n.. |downloads| image:: https://pepy.tech/badge/edc-metadata\n   :target: https://pepy.tech/project/edc-metadata\n\n",
    "bugtrack_url": null,
    "license": "GPL license, see LICENSE",
    "summary": "Add a metadata layer to data entry",
    "version": "0.3.33",
    "split_keywords": [
        "django",
        "edc",
        "clinicedc",
        "clinical trials"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "f538b1722f3b3f7736d2a6b27f486166",
                "sha256": "7e40f98570958304a8c4945cc0a03a7fcafe3195630077bcce1086fe94329d36"
            },
            "downloads": -1,
            "filename": "edc_metadata-0.3.33-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f538b1722f3b3f7736d2a6b27f486166",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 118927,
            "upload_time": "2022-06-29T09:02:18",
            "upload_time_iso_8601": "2022-06-29T09:02:18.107960Z",
            "url": "https://files.pythonhosted.org/packages/d1/72/f1d49fda10c856368f27f7db81b7d4cb94eae7485ae080be0fd57bafda33/edc_metadata-0.3.33-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "md5": "23920e8294638dbe77f1244eca49f39c",
                "sha256": "f96b1338df2051673fd435fc547e526a7fcac26c5146b8d1faf56a5c1d0df7bc"
            },
            "downloads": -1,
            "filename": "edc-metadata-0.3.33.tar.gz",
            "has_sig": false,
            "md5_digest": "23920e8294638dbe77f1244eca49f39c",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 80842,
            "upload_time": "2022-06-29T09:02:22",
            "upload_time_iso_8601": "2022-06-29T09:02:22.541962Z",
            "url": "https://files.pythonhosted.org/packages/a4/b4/813ea4e0343dd0a41e8c2dc021d9f28637c67c5ad3e519e51feb87c131e7/edc-metadata-0.3.33.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2022-06-29 09:02:22",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "clinicedc",
    "github_project": "edc-metadata",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "edc-metadata"
}
        
Elapsed time: 0.86701s