edc-visit-tracking


Nameedc-visit-tracking JSON
Version 0.3.91 PyPI version JSON
download
home_pagehttps://github.com/clinicedc/edc-visit-tracking
SummaryBase classes for visit reports/tracking in clinicedc/edc
upload_time2024-03-27 03:56:03
maintainerNone
docs_urlNone
authorErik van Widenfelt
requires_python>=3.11
licenseGPL license, see LICENSE
keywords django edc visit tracking clinicedc clinical trials
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            |pypi| |actions| |codecov| |downloads|

edc-visit-tracking
------------------

Track study participant visit reports.


Declaring a visit model
+++++++++++++++++++++++

A **visit_model** is declared using the model mixin `VisitModelMixin`. Normally, a **visit_model** will be declared with additional model mixins, but `VisitModelMixin` must be there.


.. code-block:: python

    class SubjectVisit(VisitModelMixin, BaseUuidModel):
        ...

Also, ensure the `Meta` class attributes of `VisitModelMixin` are inherited. These include required constraints and ordering.


.. code-block:: python

    class SubjectVisit(VisitModelMixin, BaseUuidModel):

        ...

        class Meta(VisitModelMixin.Meta):
            pass

Among other features, `VisitModelMixin` adds a `OneToOneField` foreign key to the **visit_model** that points to `edc_appointment.Appointment`.

 Important: A **visit model** is a special model in the EDC. A model declared with the model mixin, `VisitModelMixin`, is the definition of a **visit model**. CRFs and Requisitions have a foreign key pointing to a **visit model**. A number of methods on CRFs and Requisitions detect their **visit model** foreign key name, model class and value by looking for the FK declared with `VisitModelMixin`.


For a subject that requires ICF the **visit model** would use the `RequiresConsentModelMixin`:

.. code-block:: python

    class SubjectVisit(
        VisitModelMixin,
        RequiresConsentFieldsModelMixin,
        BaseUuidModel,
    ):

        class Meta(VisitModelMixin.Meta, BaseUuidModel.Meta):
            pass


If the subject does not require ICF, such as an infant, don't include the `RequiresConsentModelMixin`:

.. code-block:: python

    class InfantVisit(
        VisitModelMixin,
        BaseUuidModel
    ):

        class Meta(VisitModelMixin.Meta, , BaseUuidModel.Meta):
            pass


A more complete declaration will include model mixins from other libraries. For example:

.. code-block:: python

    from edc_consent.model_mixins import RequiresConsentFieldsModelMixin
    from edc_metadata.model_mixins.creates import CreatesMetadataModelMixin
    from edc_model.models import BaseUuidModel
    from edc_offstudy.model_mixins import OffstudyVisitModelMixin
    from edc_sites.managers import CurrentSiteManager
    from edc_sites.model_mixins import SiteModelMixin
    from edc_visit_tracking.managers import VisitModelManager
    from edc_visit_tracking.model_mixins import VisitModelMixin

    class SubjectVisit(
        SiteModelMixin,
        VisitModelMixin,
        CreatesMetadataModelMixin,
        RequiresConsentFieldsModelMixin,
        OffstudyNonCrfModelMixin,
        BaseUuidModel,
    ):

        objects = VisitModelManager()

        on_site = CurrentSiteManager()

        history = edc_models.HistoricalRecords()

    class Meta(VisitModelMixin.Meta, BaseUuidModel.Meta):
        pass

Declaring a CRF
+++++++++++++++

The `CrfModelMixin` is required for all CRF models. CRF models have a `OneToOneField` key to a **visit model**.

.. code-block:: python

    class CrfOne(CrfModelMixin, OffstudyCrfModelMixin, RequiresConsentModelMixin,
                 UpdatesCrfMetadataModelMixin, BaseUuidModel):

        subject_visit = models.OneToOneField(SubjectVisit)

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

        vl = models.CharField(max_length=10, default=NO)

        rdb = models.CharField(max_length=10, default=NO)

        class Meta:
            consent_model = 'myapp.subjectconsent'  # for RequiresConsentModelMixin

Declaring forms:
++++++++++++++++
The `VisitFormMixin` includes a number of common validations in the `clean` method:

.. code-block:: python

    class SubjectVisitForm(VisitFormMixin, FormValidatorMixin, forms.ModelForm):

        form_validator_cls = VisitFormValidator

        class Meta:
            model = SubjectVisit

`PreviousVisitModelMixin`
+++++++++++++++++++++++++

The `PreviousVisitModelMixin` ensures that visits are entered in sequence. It is included with the `VisitModelMixin`.

`VisitTrackingModelFormMixin`
+++++++++++++++++++++++++++++

    see `DEFAULT_REPORT_DATETIME_ALLOWANCE`


Missed Visit Report
+++++++++++++++++++

A detail report should be submitted for scheduled visits that are missed.
By selecting the reason ``missed visit`` on ``SubjectVisit``, only the missed visit CRF will be required
for the timepoint. All other CRFs and requisitions will be excluded.

Unscheduled visits cannot be missed. (To change this behaviour see `settings` attrubute `EDC_VISIT_TRACKING_ALLOW_MISSED_UNSCHEDULED`)

The model mixin ``SubjectVisitMissedModelMixin`` provides the basic features of a `SubjectVisitMissed` model.

In your subject app declare:

.. code-block:: python

    from django.db.models import PROTECT
    from edc_crf.model_mixins import CrfWithActionModelMixin
    from edc_model import models as edc_models
    from edc_visit_tracking.model_mixins import SubjectVisitMissedModelMixin

    class SubjectVisitMissed(SubjectVisitMissedModelMixin, edc_models.BaseUuidModel):

        missed_reasons = models.ManyToManyField(
            SubjectVisitMissedReasons, blank=True, related_name="+"
        )

        class Meta(CrfWithActionModelMixin.Meta, edc_models.BaseUuidModel.Meta):
            verbose_name = "Missed Visit Report"
            verbose_name_plural = "Missed Visit Report"

In your list model app, e.g. ``meta_lists``, declare the list model:

.. code-block:: python

    class SubjectVisitMissedReasons(ListModelMixin):
        class Meta(ListModelMixin.Meta):
            verbose_name = "Subject Missed Visit Reasons"
            verbose_name_plural = "Subject Missed Visit Reasons"

... and update the ``list_data`` dictionary, for example:

.. code-block:: python

    list_data = {
    ...
    "meta_lists.subjectvisitmissedreasons": [
        ("forgot", "Forgot / Can’t remember being told about appointment"),
        ("family_emergency", "Family emergency (e.g. funeral) and was away"),
        ("travelling", "Away travelling/visiting"),
        ("working_schooling", "Away working/schooling"),
        ("too_sick", "Too sick or weak to come to the centre"),
        ("lack_of_transport", "Transportation difficulty"),
        (OTHER, "Other reason (specify below)",),
    ],
    ...
    }


Window period
+++++++++++++

By default, the visit `report_datetime` is validated to stay within the same window period as the appointment.
This may be too restrictive in some cases.

To bypass this override ```validate_visit_datetime_in_window_period``` in the ```VisitFormValidator```

.. code-block:: python

    from edc_visit_tracking.form_validators import VisitFormValidator as BaseVisitFormValidator

    class VisitFormValidator(BaseVisitFormValidator):

        ...

        def validate_visit_datetime_in_window_period():
            pass

        ...

Be sure that your appointment form validator is enforcing window periods before
bypassing this check.

See also `edc_appointment`.


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

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

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

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

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/clinicedc/edc-visit-tracking",
    "name": "edc-visit-tracking",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "django, edc, visit tracking, clinicedc, clinical trials",
    "author": "Erik van Widenfelt",
    "author_email": "ew2789@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/06/81/883dd2c2717ae15ee22f4e40fa59bd419939d769bd8f3029914ab45e852d/edc-visit-tracking-0.3.91.tar.gz",
    "platform": null,
    "description": "|pypi| |actions| |codecov| |downloads|\n\nedc-visit-tracking\n------------------\n\nTrack study participant visit reports.\n\n\nDeclaring a visit model\n+++++++++++++++++++++++\n\nA **visit_model** is declared using the model mixin `VisitModelMixin`. Normally, a **visit_model** will be declared with additional model mixins, but `VisitModelMixin` must be there.\n\n\n.. code-block:: python\n\n    class SubjectVisit(VisitModelMixin, BaseUuidModel):\n        ...\n\nAlso, ensure the `Meta` class attributes of `VisitModelMixin` are inherited. These include required constraints and ordering.\n\n\n.. code-block:: python\n\n    class SubjectVisit(VisitModelMixin, BaseUuidModel):\n\n        ...\n\n        class Meta(VisitModelMixin.Meta):\n            pass\n\nAmong other features, `VisitModelMixin` adds a `OneToOneField` foreign key to the **visit_model** that points to `edc_appointment.Appointment`.\n\n Important: A **visit model** is a special model in the EDC. A model declared with the model mixin, `VisitModelMixin`, is the definition of a **visit model**. CRFs and Requisitions have a foreign key pointing to a **visit model**. A number of methods on CRFs and Requisitions detect their **visit model** foreign key name, model class and value by looking for the FK declared with `VisitModelMixin`.\n\n\nFor a subject that requires ICF the **visit model** would use the `RequiresConsentModelMixin`:\n\n.. code-block:: python\n\n    class SubjectVisit(\n        VisitModelMixin,\n        RequiresConsentFieldsModelMixin,\n        BaseUuidModel,\n    ):\n\n        class Meta(VisitModelMixin.Meta, BaseUuidModel.Meta):\n            pass\n\n\nIf the subject does not require ICF, such as an infant, don't include the `RequiresConsentModelMixin`:\n\n.. code-block:: python\n\n    class InfantVisit(\n        VisitModelMixin,\n        BaseUuidModel\n    ):\n\n        class Meta(VisitModelMixin.Meta, , BaseUuidModel.Meta):\n            pass\n\n\nA more complete declaration will include model mixins from other libraries. For example:\n\n.. code-block:: python\n\n    from edc_consent.model_mixins import RequiresConsentFieldsModelMixin\n    from edc_metadata.model_mixins.creates import CreatesMetadataModelMixin\n    from edc_model.models import BaseUuidModel\n    from edc_offstudy.model_mixins import OffstudyVisitModelMixin\n    from edc_sites.managers import CurrentSiteManager\n    from edc_sites.model_mixins import SiteModelMixin\n    from edc_visit_tracking.managers import VisitModelManager\n    from edc_visit_tracking.model_mixins import VisitModelMixin\n\n    class SubjectVisit(\n        SiteModelMixin,\n        VisitModelMixin,\n        CreatesMetadataModelMixin,\n        RequiresConsentFieldsModelMixin,\n        OffstudyNonCrfModelMixin,\n        BaseUuidModel,\n    ):\n\n        objects = VisitModelManager()\n\n        on_site = CurrentSiteManager()\n\n        history = edc_models.HistoricalRecords()\n\n    class Meta(VisitModelMixin.Meta, BaseUuidModel.Meta):\n        pass\n\nDeclaring a CRF\n+++++++++++++++\n\nThe `CrfModelMixin` is required for all CRF models. CRF models have a `OneToOneField` key to a **visit model**.\n\n.. code-block:: python\n\n    class CrfOne(CrfModelMixin, OffstudyCrfModelMixin, RequiresConsentModelMixin,\n                 UpdatesCrfMetadataModelMixin, BaseUuidModel):\n\n        subject_visit = models.OneToOneField(SubjectVisit)\n\n        f1 = models.CharField(max_length=10, default='erik')\n\n        vl = models.CharField(max_length=10, default=NO)\n\n        rdb = models.CharField(max_length=10, default=NO)\n\n        class Meta:\n            consent_model = 'myapp.subjectconsent'  # for RequiresConsentModelMixin\n\nDeclaring forms:\n++++++++++++++++\nThe `VisitFormMixin` includes a number of common validations in the `clean` method:\n\n.. code-block:: python\n\n    class SubjectVisitForm(VisitFormMixin, FormValidatorMixin, forms.ModelForm):\n\n        form_validator_cls = VisitFormValidator\n\n        class Meta:\n            model = SubjectVisit\n\n`PreviousVisitModelMixin`\n+++++++++++++++++++++++++\n\nThe `PreviousVisitModelMixin` ensures that visits are entered in sequence. It is included with the `VisitModelMixin`.\n\n`VisitTrackingModelFormMixin`\n+++++++++++++++++++++++++++++\n\n    see `DEFAULT_REPORT_DATETIME_ALLOWANCE`\n\n\nMissed Visit Report\n+++++++++++++++++++\n\nA detail report should be submitted for scheduled visits that are missed.\nBy selecting the reason ``missed visit`` on ``SubjectVisit``, only the missed visit CRF will be required\nfor the timepoint. All other CRFs and requisitions will be excluded.\n\nUnscheduled visits cannot be missed. (To change this behaviour see `settings` attrubute `EDC_VISIT_TRACKING_ALLOW_MISSED_UNSCHEDULED`)\n\nThe model mixin ``SubjectVisitMissedModelMixin`` provides the basic features of a `SubjectVisitMissed` model.\n\nIn your subject app declare:\n\n.. code-block:: python\n\n    from django.db.models import PROTECT\n    from edc_crf.model_mixins import CrfWithActionModelMixin\n    from edc_model import models as edc_models\n    from edc_visit_tracking.model_mixins import SubjectVisitMissedModelMixin\n\n    class SubjectVisitMissed(SubjectVisitMissedModelMixin, edc_models.BaseUuidModel):\n\n        missed_reasons = models.ManyToManyField(\n            SubjectVisitMissedReasons, blank=True, related_name=\"+\"\n        )\n\n        class Meta(CrfWithActionModelMixin.Meta, edc_models.BaseUuidModel.Meta):\n            verbose_name = \"Missed Visit Report\"\n            verbose_name_plural = \"Missed Visit Report\"\n\nIn your list model app, e.g. ``meta_lists``, declare the list model:\n\n.. code-block:: python\n\n    class SubjectVisitMissedReasons(ListModelMixin):\n        class Meta(ListModelMixin.Meta):\n            verbose_name = \"Subject Missed Visit Reasons\"\n            verbose_name_plural = \"Subject Missed Visit Reasons\"\n\n... and update the ``list_data`` dictionary, for example:\n\n.. code-block:: python\n\n    list_data = {\n    ...\n    \"meta_lists.subjectvisitmissedreasons\": [\n        (\"forgot\", \"Forgot / Can\u2019t remember being told about appointment\"),\n        (\"family_emergency\", \"Family emergency (e.g. funeral) and was away\"),\n        (\"travelling\", \"Away travelling/visiting\"),\n        (\"working_schooling\", \"Away working/schooling\"),\n        (\"too_sick\", \"Too sick or weak to come to the centre\"),\n        (\"lack_of_transport\", \"Transportation difficulty\"),\n        (OTHER, \"Other reason (specify below)\",),\n    ],\n    ...\n    }\n\n\nWindow period\n+++++++++++++\n\nBy default, the visit `report_datetime` is validated to stay within the same window period as the appointment.\nThis may be too restrictive in some cases.\n\nTo bypass this override ```validate_visit_datetime_in_window_period``` in the ```VisitFormValidator```\n\n.. code-block:: python\n\n    from edc_visit_tracking.form_validators import VisitFormValidator as BaseVisitFormValidator\n\n    class VisitFormValidator(BaseVisitFormValidator):\n\n        ...\n\n        def validate_visit_datetime_in_window_period():\n            pass\n\n        ...\n\nBe sure that your appointment form validator is enforcing window periods before\nbypassing this check.\n\nSee also `edc_appointment`.\n\n\n.. |pypi| image:: https://img.shields.io/pypi/v/edc-visit-tracking.svg\n    :target: https://pypi.python.org/pypi/edc-visit-tracking\n\n.. |actions| image:: https://github.com/clinicedc/edc-visit-tracking/actions/workflows/build.yml/badge.svg\n  :target: https://github.com/clinicedc/edc-visit-tracking/actions/workflows/build.yml\n\n.. |codecov| image:: https://codecov.io/gh/clinicedc/edc-visit-tracking/branch/develop/graph/badge.svg\n  :target: https://codecov.io/gh/clinicedc/edc-visit-tracking\n\n.. |downloads| image:: https://pepy.tech/badge/edc-visit-tracking\n   :target: https://pepy.tech/project/edc-visit-tracking\n",
    "bugtrack_url": null,
    "license": "GPL license, see LICENSE",
    "summary": "Base classes for visit reports/tracking in clinicedc/edc",
    "version": "0.3.91",
    "project_urls": {
        "Homepage": "https://github.com/clinicedc/edc-visit-tracking"
    },
    "split_keywords": [
        "django",
        " edc",
        " visit tracking",
        " clinicedc",
        " clinical trials"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "194593200171cc0a85b06c374dd2470b14430d22d2e43ae5b9846a0585d15859",
                "md5": "6b4cbe84ac7756c81f1e8b4cf629835d",
                "sha256": "b5591b98ade130782ebc6fd3ce14e3fcc583f6274f233c03e335f36c9e29f317"
            },
            "downloads": -1,
            "filename": "edc_visit_tracking-0.3.91-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "6b4cbe84ac7756c81f1e8b4cf629835d",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 87270,
            "upload_time": "2024-03-27T03:56:00",
            "upload_time_iso_8601": "2024-03-27T03:56:00.373232Z",
            "url": "https://files.pythonhosted.org/packages/19/45/93200171cc0a85b06c374dd2470b14430d22d2e43ae5b9846a0585d15859/edc_visit_tracking-0.3.91-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "0681883dd2c2717ae15ee22f4e40fa59bd419939d769bd8f3029914ab45e852d",
                "md5": "2728f48e764180a3f98e587e36373cbd",
                "sha256": "0726288981e4c962400a5ae3012cec37f63d7ac50a9fee7f984e53346e7e9d76"
            },
            "downloads": -1,
            "filename": "edc-visit-tracking-0.3.91.tar.gz",
            "has_sig": false,
            "md5_digest": "2728f48e764180a3f98e587e36373cbd",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 65673,
            "upload_time": "2024-03-27T03:56:03",
            "upload_time_iso_8601": "2024-03-27T03:56:03.859235Z",
            "url": "https://files.pythonhosted.org/packages/06/81/883dd2c2717ae15ee22f4e40fa59bd419939d769bd8f3029914ab45e852d/edc-visit-tracking-0.3.91.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-27 03:56:03",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "clinicedc",
    "github_project": "edc-visit-tracking",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "edc-visit-tracking"
}
        
Elapsed time: 0.42150s