edc-consent


Nameedc-consent JSON
Version 0.3.83 PyPI version JSON
download
home_pagehttps://github.com/clinicedc/edc-consent
SummaryBase models, forms and admin for participant ICF for clinicedc/edc projects
upload_time2024-04-18 02:26:00
maintainerNone
docs_urlNone
authorErik van Widenfelt
requires_python>=3.11
licenseGPL license, see LICENSE
keywords django edc participant icf clinicedc clinical trials
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            |pypi| |actions| |codecov| |downloads|

edc-consent
-----------

Add classes for the Informed Consent form and process.

Installation
============

Declare the consent model:

.. code-block:: python

    class SubjectConsent(
        ConsentModelMixin,
        SiteModelMixin,
        UpdatesOrCreatesRegistrationModelMixin,
        NonUniqueSubjectIdentifierModelMixin,
        IdentityFieldsMixin,
        PersonalFieldsMixin,
        SampleCollectionFieldsMixin,
        ReviewFieldsMixin,
        VulnerabilityFieldsMixin,
        SearchSlugModelMixin,
        BaseUuidModel,
    ):

        """A model completed by the user that captures the ICF."""

        subject_identifier_cls = SubjectIdentifier

        subject_screening_model = "edc_example.subjectscreening"

        objects = ConsentObjectsManager()
        on_site = CurrentSiteManager()
        history = HistoricalRecords()

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


    class SubjectConsentV1(SubjectConsent):
        """A proxy model completed by the user that captures version 1
         of the ICF.
         """
        objects = ConsentObjectsByCdefManager()
        on_site = CurrentSiteByCdefManager()
        history = HistoricalRecords()

        class Meta:
            proxy = True
            verbose_name = "Consent V1"
            verbose_name_plural = "Consent V1"

The next step is to declare and register a ``ConsentDefinition``. A consent definition is a class that represents an
approved Informed Consent. It is linked to a proxy of the consent model, for example ``SubjectConsent`` from above,
using the class attribute
``model``. We use a proxy model since over time each subject may need to submit more than one
version of the consent. Each version of a subject's consent is represented by an instance of the Each version is paird with a proxy model. The approved Informed Consent
also includes a validity period (start=datetime1 to end=datetime2) and a version number
(version=1). There are other attributes of a ``ConsentDefinition`` to consider but lets focus
on the ``start`` date, ``end`` date, ``version`` and ``model`` for now.

``ConsentDefinitions`` are declared in the root of your app in module ``consents.py``. A typical declaration looks something like this:

.. code-block:: python

    from datetime import datetime
    from zoneifo import ZoneInfo

    from edc_consent.consent_definition import ConsentDefinition
    from edc_consent.site_consents import site_consents
    from edc_constants.constants import MALE, FEMALE

    consent_v1 = ConsentDefinition(
        'edc_example.subjectconsentv1',
        version='1',
        start=datetime(2013, 10, 15, tzinfo=ZoneInfo("UTC")),
        end=datetime(2016, 10, 15, 23, 59, 999999, tzinfo=ZoneInfo("UTC")),
        age_min=16,
        age_is_adult=18,
        age_max=64,
        gender=[MALE, FEMALE])

    site_consents.register(consent_v1)


add to settings:

.. code-block:: bash

    INSTALLED_APPS = [
        ...
        'edc_consent.apps.AppConfig',
        ...
    ]

On bootup ``site_consents`` will ``autodiscover`` the ``consents.py`` and register the ``ConsentDefinition``.

To create an instance of the consent for a subject, find the ``ConsentDefinitions`` and use
``model_cls``.


.. code-block:: python

    cdef = site_consents.get_consent_definition(
        report_datetime=datetime(2013, 10, 16, tzinfo=ZoneInfo("UTC"))
    )

    assert cdef.version == "1"
    assert cdef.model == "edc_example.subjectconsentv1"

    consent_obj = cdef.model_cls.objects.create(
        subject_identifier="123456789",
        consent_datetime=datetime(2013, 10, 16, tzinfo=ZoneInfo("UTC"),
        ...)

    assert consent_obj.consent_version == "1"
    assert consent_obj.consent_model == "edc_example.subjectconsentv1"



Add a second ``ConsentDefinition`` to ``your consents.py`` for version 2:

.. code-block:: python

    class SubjectConsentV2(SubjectConsent):
        """A proxy model completed by the user that captures version 2
         of the ICF.
         """
        objects = ConsentObjectsByCdefManager()
        on_site = CurrentSiteByCdefManager()
        history = HistoricalRecords()

        class Meta:
            proxy = True
            verbose_name = "Consent V2"
            verbose_name_plural = "Consent V2"




.. code-block:: python

    consent_v1 = ConsentDefinition(...)

    consent_v2 = ConsentDefinition(
        'edc_example.subjectconsentv2',
        version='2',
        start=datetime(2016, 10, 16, 0,0,0, tzinfo=ZoneInfo("UTC")),
        end=datetime(2020, 10, 15, 23, 59, 999999, tzinfo=ZoneInfo("UTC")),
        age_min=16,
        age_is_adult=18,
        age_max=64,
        gender=[MALE, FEMALE])

    site_consents.register(consent_v1)
    site_consents.register(consent_v2)



.. code-block:: python

    cdef = site_consents.get_consent_definition(
        report_datetime=datetime(2016, 10, 17, tzinfo=ZoneInfo("UTC"))
    )

    assert cdef.version == "2"
    assert cdef.model == "edc_example.subjectconsentv2"

    consent_obj = cdef.model_cls.objects.create(
        subject_identifier="123456789",
        consent_datetime=datetime(2016, 10, 17, tzinfo=ZoneInfo("UTC"),
        ...)

    assert consent_obj.consent_version == "2"
    assert consent_obj.consent_model == "edc_example.subjectconsentv2"


``edc_consent`` is coupled with ``edc_visit_schedule``. In fact, a data collection schedule is declared with one or more ``ConsentDefinitions``. CRFs and Requisitions listed in a schedule may only be submitted if the subject has consented.

.. code-block:: python

    schedule = Schedule(
        name=SCHEDULE,
        verbose_name="Day 1 to Month 6 Follow-up",
        onschedule_model="effect_prn.onschedule",
        offschedule_model="effect_prn.endofstudy",
        consent_definitions=[consent_v1, consent_v2],
    )

When a CRF is saved, the CRF model will check the ``schedule`` to find the ``ConsentDefinition`` with a validity period that contains the ``crf.report_datetime``. Using the located ``ConsentDefinitions``, the CRF model will confirm the subject has a saved ``subject_consent`` with this ``consent_definition.version``.

The ConsentDefinitions above assume that consent version 1 is completed for a subject
consenting on or before 2016/10/15 and version 2 for those consenting after 2016/10/15.

Sometimes when version 2 is introduced, those subjects who consented for version 1 need
to update their version 1 consent to version 2. For example, a question may have been added
in version 2 to allow a subject to opt-out of having their specimens put into longterm
storage. The subjects who are already consented under version 1 need to indicate their
preference as well by submitting a version 2 consent. (To make things simple, we would
programatically carry-over and validate duplicate data from the subject's version 1 consent.)

To allow this, we would add ``update_versions`` to the version 2 ``ConsentDefinition``.

.. code-block:: python

    consent_v1 = ConsentDefinition(
        'edc_example.subjectconsentv1',
        version='1', ...)

    consent_v2 = ConsentDefinition(
        'edc_example.subjectconsentv2',
        version='2',
        update_versions=[UpdateVersion(consent_v1.version, consent_v1.end)],

    site_consents.register(consent_v1)
    site_consents.register(consent_v2)

As the trial continues past 2016/10/15, there will three categories of subjects:

* Subjects who completed version 1 only
* Subjects who completed version 1 and version 2
* Subjects who completed version 2 only

If the report date is after 2016/10/15, data entry for "Subjects who completed version 1 only"
will be blocked until the version 2 consent is submitted.





Features
========

* base class for an informed consent document
* data for models that require consent cannot be add until the consent is added
* consents have a version number and validity period
* maximum number of consented subjects can be controlled.
* data collection is only allowed within the validity period of the consent per consented participant
* data for models that require consent are tagged with the consent version

TODO
====

- link subject type to the consent model. e.g. maternal, infant, adult, etc.
- version at model field level (e.g. a new consent period adds additional questions to a form)
- allow a different subject's consent to cover for another, for example mother and infant.

Usage
=====



Declare the ModelForm:

.. code-block:: python

    class SubjectConsentForm(BaseConsentForm):

        class Meta:
            model = SubjectConsent


Now that you have a consent model class, declare the models that will require this consent:

.. code-block:: python

    class Questionnaire(RequiresConsentMixin, models.Model):

        report_datetime = models.DateTimeField(default=timezone.now)

        question1 = models.CharField(max_length=10)

        question2 = models.CharField(max_length=10)

        question3 = models.CharField(max_length=10)

    @property
    def subject_identifier(self):
        """Returns the subject identifier from ..."""
        return subject_identifier

    class Meta:
        app_label = 'my_app'
        verbose_name = 'My Questionnaire'


* report_datetime: a required field used to lookup the correct ``ConsentDefinition`` and to find, together with ``subject_identifier``,  a valid instance of ``SubjectConsent``;
* subject_identifier: a required field or may be a property that knows how to find the ``subject_identifier`` for the instance of ``Questionnaire``.

Once all is declared you need to:

* define the consent version and validity period for the consent version in ``ConsentDefinition``;
* add a Quota for the consent model.

As subjects are identified:

* add a consent
* add the models (e.g. ``Questionnaire``)

If a consent version cannot be found given the consent model class and report_datetime a ``ConsentDefinitionError`` is raised.

If a consent for this subject_identifier cannot be found that matches the ``ConsentDefinition`` a ``NotConsentedError`` is raised.

Specimen Consent
================

A participant may consent to the study but not agree to have specimens stored long term. A specimen consent is administered separately to clarify the participant\'s intention.

The specimen consent is declared using the base class ``BaseSpecimenConsent``. This is an abridged version of ``BaseConsent``. The specimen consent also uses the ``RequiresConsentMixin`` as it cannot stand alone as an ICF. The ``RequiresConsentMixin`` ensures the specimen consent is administered after the main study ICF, in this case ``MyStudyConsent``.

A specimen consent is declared in your app like this:

.. code-block:: python

        class SpecimenConsent(
            BaseSpecimenConsent, SampleCollectionFieldsMixin, RequiresConsentMixin,
            VulnerabilityFieldsMixin, AppointmentMixin, BaseUuidModel
        ):

            consent_model = MyStudyConsent

            registered_subject = models.OneToOneField(RegisteredSubject, null=True)

            objects = models.Manager()

            history = AuditTrail()

        class Meta:
            app_label = 'my_app'
            verbose_name = 'Specimen Consent'


Validators
==========

The ``ConsentAgeValidator`` validates the date of birth to within a given age range, for example:

.. code-block:: python

    from edc_consent.validtors import ConsentAgeValidator

    class MyConsent(ConsentQuotaMixin, BaseConsent):

        dob = models.DateField(
            validators=[ConsentAgeValidator(16, 64)])

        quota = QuotaManager()

        class Meta:
            app_label = 'my_app'

The ``PersonalFieldsMixin`` includes a date of birth field and you can set the age bounds like this:

.. code-block:: python

    from edc_consent.validtors import ConsentAgeValidator
    from edc_consent.models.fields import PersonalFieldsMixin

    class MyConsent(ConsentQuotaMixin, PersonalFieldsMixin, BaseConsent):

        quota = QuotaManager()

        MIN_AGE_OF_CONSENT = 18
        MAX_AGE_OF_CONSENT = 64

        class Meta:
            app_label = 'my_app'


Common senarios
===============

Tracking the consent version with collected data
++++++++++++++++++++++++++++++++++++++++++++++++

All model data is tagged with the consent version identified in ``ConsentDefinition`` for the consent model class and report_datetime.

Reconsenting consented subjects when the consent changes
++++++++++++++++++++++++++++++++++++++++++++++++++++++++

The consent model is unique on subject_identifier, identity and version. If a new consent version is added to ``ConsentDefinition``, a new consent will be required for each subject as data is reported within the validity period of the new consent.

Some care must be taken to ensure that the consent model is queried with an understanding of the unique constraint.


Linking the consent version to added or removed model fields on models that require consent
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

TODO

Infants use mother's consent
++++++++++++++++++++++++++++

TODO

By adding the property ``consenting_subject_identifier`` to the consent


Patient names
=============
If patient names need to be removed from the data collection, there are a few helper
attributes and methods to consider.

``settings.EDC_CONSENT_REMOVE_PATIENT_NAMES_FROM_COUNTRIES: list[str]``

If given a list of country names, name fields will be removed from any admin.fieldset.

See also edc_sites.all_sites

``ConsentModelAdminMixin.get_fieldsets``

.. code-block:: python

    def get_fieldsets(self, request, obj=None):
        fieldsets = super().get_fieldsets(request, obj)
        for country in get_remove_patient_names_from_countries():
            site = getattr(request, "site", None)
            if site and site.id in [s.site_id for s in self.all_sites.get(country)]:
                return self.fieldsets_without_names(fieldsets)
        return fieldsets

This method could be added to any ModeLadmin with names.



using


Other TODO
==========

* ``Timepoint`` model update in ``save`` method of models requiring consent
* handle added or removed model fields (questions) because of consent version change
* review verification actions
* management command to update version on models that require consent (if edc_consent added after instances were created)
* handle re-consenting issues, for example, if original consent was restricted by age (16-64) but the re-consent is not. May need to open upper bound.



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

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

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

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

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/clinicedc/edc-consent",
    "name": "edc-consent",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "django Edc participant ICF, clinicedc, clinical trials",
    "author": "Erik van Widenfelt",
    "author_email": "ew2789@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/87/f7/6bf47f8dd737c3dc3bc2283f819d0516069ef906c8c9c8e8f68dca2b4372/edc_consent-0.3.83.tar.gz",
    "platform": null,
    "description": "|pypi| |actions| |codecov| |downloads|\n\nedc-consent\n-----------\n\nAdd classes for the Informed Consent form and process.\n\nInstallation\n============\n\nDeclare the consent model:\n\n.. code-block:: python\n\n    class SubjectConsent(\n        ConsentModelMixin,\n        SiteModelMixin,\n        UpdatesOrCreatesRegistrationModelMixin,\n        NonUniqueSubjectIdentifierModelMixin,\n        IdentityFieldsMixin,\n        PersonalFieldsMixin,\n        SampleCollectionFieldsMixin,\n        ReviewFieldsMixin,\n        VulnerabilityFieldsMixin,\n        SearchSlugModelMixin,\n        BaseUuidModel,\n    ):\n\n        \"\"\"A model completed by the user that captures the ICF.\"\"\"\n\n        subject_identifier_cls = SubjectIdentifier\n\n        subject_screening_model = \"edc_example.subjectscreening\"\n\n        objects = ConsentObjectsManager()\n        on_site = CurrentSiteManager()\n        history = HistoricalRecords()\n\n        class Meta(ConsentModelMixin.Meta, BaseUuidModel.Meta):\n            pass\n\n\n    class SubjectConsentV1(SubjectConsent):\n        \"\"\"A proxy model completed by the user that captures version 1\n         of the ICF.\n         \"\"\"\n        objects = ConsentObjectsByCdefManager()\n        on_site = CurrentSiteByCdefManager()\n        history = HistoricalRecords()\n\n        class Meta:\n            proxy = True\n            verbose_name = \"Consent V1\"\n            verbose_name_plural = \"Consent V1\"\n\nThe next step is to declare and register a ``ConsentDefinition``. A consent definition is a class that represents an\napproved Informed Consent. It is linked to a proxy of the consent model, for example ``SubjectConsent`` from above,\nusing the class attribute\n``model``. We use a proxy model since over time each subject may need to submit more than one\nversion of the consent. Each version of a subject's consent is represented by an instance of the Each version is paird with a proxy model. The approved Informed Consent\nalso includes a validity period (start=datetime1 to end=datetime2) and a version number\n(version=1). There are other attributes of a ``ConsentDefinition`` to consider but lets focus\non the ``start`` date, ``end`` date, ``version`` and ``model`` for now.\n\n``ConsentDefinitions`` are declared in the root of your app in module ``consents.py``. A typical declaration looks something like this:\n\n.. code-block:: python\n\n    from datetime import datetime\n    from zoneifo import ZoneInfo\n\n    from edc_consent.consent_definition import ConsentDefinition\n    from edc_consent.site_consents import site_consents\n    from edc_constants.constants import MALE, FEMALE\n\n    consent_v1 = ConsentDefinition(\n        'edc_example.subjectconsentv1',\n        version='1',\n        start=datetime(2013, 10, 15, tzinfo=ZoneInfo(\"UTC\")),\n        end=datetime(2016, 10, 15, 23, 59, 999999, tzinfo=ZoneInfo(\"UTC\")),\n        age_min=16,\n        age_is_adult=18,\n        age_max=64,\n        gender=[MALE, FEMALE])\n\n    site_consents.register(consent_v1)\n\n\nadd to settings:\n\n.. code-block:: bash\n\n    INSTALLED_APPS = [\n        ...\n        'edc_consent.apps.AppConfig',\n        ...\n    ]\n\nOn bootup ``site_consents`` will ``autodiscover`` the ``consents.py`` and register the ``ConsentDefinition``.\n\nTo create an instance of the consent for a subject, find the ``ConsentDefinitions`` and use\n``model_cls``.\n\n\n.. code-block:: python\n\n    cdef = site_consents.get_consent_definition(\n        report_datetime=datetime(2013, 10, 16, tzinfo=ZoneInfo(\"UTC\"))\n    )\n\n    assert cdef.version == \"1\"\n    assert cdef.model == \"edc_example.subjectconsentv1\"\n\n    consent_obj = cdef.model_cls.objects.create(\n        subject_identifier=\"123456789\",\n        consent_datetime=datetime(2013, 10, 16, tzinfo=ZoneInfo(\"UTC\"),\n        ...)\n\n    assert consent_obj.consent_version == \"1\"\n    assert consent_obj.consent_model == \"edc_example.subjectconsentv1\"\n\n\n\nAdd a second ``ConsentDefinition`` to ``your consents.py`` for version 2:\n\n.. code-block:: python\n\n    class SubjectConsentV2(SubjectConsent):\n        \"\"\"A proxy model completed by the user that captures version 2\n         of the ICF.\n         \"\"\"\n        objects = ConsentObjectsByCdefManager()\n        on_site = CurrentSiteByCdefManager()\n        history = HistoricalRecords()\n\n        class Meta:\n            proxy = True\n            verbose_name = \"Consent V2\"\n            verbose_name_plural = \"Consent V2\"\n\n\n\n\n.. code-block:: python\n\n    consent_v1 = ConsentDefinition(...)\n\n    consent_v2 = ConsentDefinition(\n        'edc_example.subjectconsentv2',\n        version='2',\n        start=datetime(2016, 10, 16, 0,0,0, tzinfo=ZoneInfo(\"UTC\")),\n        end=datetime(2020, 10, 15, 23, 59, 999999, tzinfo=ZoneInfo(\"UTC\")),\n        age_min=16,\n        age_is_adult=18,\n        age_max=64,\n        gender=[MALE, FEMALE])\n\n    site_consents.register(consent_v1)\n    site_consents.register(consent_v2)\n\n\n\n.. code-block:: python\n\n    cdef = site_consents.get_consent_definition(\n        report_datetime=datetime(2016, 10, 17, tzinfo=ZoneInfo(\"UTC\"))\n    )\n\n    assert cdef.version == \"2\"\n    assert cdef.model == \"edc_example.subjectconsentv2\"\n\n    consent_obj = cdef.model_cls.objects.create(\n        subject_identifier=\"123456789\",\n        consent_datetime=datetime(2016, 10, 17, tzinfo=ZoneInfo(\"UTC\"),\n        ...)\n\n    assert consent_obj.consent_version == \"2\"\n    assert consent_obj.consent_model == \"edc_example.subjectconsentv2\"\n\n\n``edc_consent`` is coupled with ``edc_visit_schedule``. In fact, a data collection schedule is declared with one or more ``ConsentDefinitions``. CRFs and Requisitions listed in a schedule may only be submitted if the subject has consented.\n\n.. code-block:: python\n\n    schedule = Schedule(\n        name=SCHEDULE,\n        verbose_name=\"Day 1 to Month 6 Follow-up\",\n        onschedule_model=\"effect_prn.onschedule\",\n        offschedule_model=\"effect_prn.endofstudy\",\n        consent_definitions=[consent_v1, consent_v2],\n    )\n\nWhen a CRF is saved, the CRF model will check the ``schedule`` to find the ``ConsentDefinition`` with a validity period that contains the ``crf.report_datetime``. Using the located ``ConsentDefinitions``, the CRF model will confirm the subject has a saved ``subject_consent`` with this ``consent_definition.version``.\n\nThe ConsentDefinitions above assume that consent version 1 is completed for a subject\nconsenting on or before 2016/10/15 and version 2 for those consenting after 2016/10/15.\n\nSometimes when version 2 is introduced, those subjects who consented for version 1 need\nto update their version 1 consent to version 2. For example, a question may have been added\nin version 2 to allow a subject to opt-out of having their specimens put into longterm\nstorage. The subjects who are already consented under version 1 need to indicate their\npreference as well by submitting a version 2 consent. (To make things simple, we would\nprogramatically carry-over and validate duplicate data from the subject's version 1 consent.)\n\nTo allow this, we would add ``update_versions`` to the version 2 ``ConsentDefinition``.\n\n.. code-block:: python\n\n    consent_v1 = ConsentDefinition(\n        'edc_example.subjectconsentv1',\n        version='1', ...)\n\n    consent_v2 = ConsentDefinition(\n        'edc_example.subjectconsentv2',\n        version='2',\n        update_versions=[UpdateVersion(consent_v1.version, consent_v1.end)],\n\n    site_consents.register(consent_v1)\n    site_consents.register(consent_v2)\n\nAs the trial continues past 2016/10/15, there will three categories of subjects:\n\n* Subjects who completed version 1 only\n* Subjects who completed version 1 and version 2\n* Subjects who completed version 2 only\n\nIf the report date is after 2016/10/15, data entry for \"Subjects who completed version 1 only\"\nwill be blocked until the version 2 consent is submitted.\n\n\n\n\n\nFeatures\n========\n\n* base class for an informed consent document\n* data for models that require consent cannot be add until the consent is added\n* consents have a version number and validity period\n* maximum number of consented subjects can be controlled.\n* data collection is only allowed within the validity period of the consent per consented participant\n* data for models that require consent are tagged with the consent version\n\nTODO\n====\n\n- link subject type to the consent model. e.g. maternal, infant, adult, etc.\n- version at model field level (e.g. a new consent period adds additional questions to a form)\n- allow a different subject's consent to cover for another, for example mother and infant.\n\nUsage\n=====\n\n\n\nDeclare the ModelForm:\n\n.. code-block:: python\n\n    class SubjectConsentForm(BaseConsentForm):\n\n        class Meta:\n            model = SubjectConsent\n\n\nNow that you have a consent model class, declare the models that will require this consent:\n\n.. code-block:: python\n\n    class Questionnaire(RequiresConsentMixin, models.Model):\n\n        report_datetime = models.DateTimeField(default=timezone.now)\n\n        question1 = models.CharField(max_length=10)\n\n        question2 = models.CharField(max_length=10)\n\n        question3 = models.CharField(max_length=10)\n\n    @property\n    def subject_identifier(self):\n        \"\"\"Returns the subject identifier from ...\"\"\"\n        return subject_identifier\n\n    class Meta:\n        app_label = 'my_app'\n        verbose_name = 'My Questionnaire'\n\n\n* report_datetime: a required field used to lookup the correct ``ConsentDefinition`` and to find, together with ``subject_identifier``,  a valid instance of ``SubjectConsent``;\n* subject_identifier: a required field or may be a property that knows how to find the ``subject_identifier`` for the instance of ``Questionnaire``.\n\nOnce all is declared you need to:\n\n* define the consent version and validity period for the consent version in ``ConsentDefinition``;\n* add a Quota for the consent model.\n\nAs subjects are identified:\n\n* add a consent\n* add the models (e.g. ``Questionnaire``)\n\nIf a consent version cannot be found given the consent model class and report_datetime a ``ConsentDefinitionError`` is raised.\n\nIf a consent for this subject_identifier cannot be found that matches the ``ConsentDefinition`` a ``NotConsentedError`` is raised.\n\nSpecimen Consent\n================\n\nA participant may consent to the study but not agree to have specimens stored long term. A specimen consent is administered separately to clarify the participant\\'s intention.\n\nThe specimen consent is declared using the base class ``BaseSpecimenConsent``. This is an abridged version of ``BaseConsent``. The specimen consent also uses the ``RequiresConsentMixin`` as it cannot stand alone as an ICF. The ``RequiresConsentMixin`` ensures the specimen consent is administered after the main study ICF, in this case ``MyStudyConsent``.\n\nA specimen consent is declared in your app like this:\n\n.. code-block:: python\n\n        class SpecimenConsent(\n            BaseSpecimenConsent, SampleCollectionFieldsMixin, RequiresConsentMixin,\n            VulnerabilityFieldsMixin, AppointmentMixin, BaseUuidModel\n        ):\n\n            consent_model = MyStudyConsent\n\n            registered_subject = models.OneToOneField(RegisteredSubject, null=True)\n\n            objects = models.Manager()\n\n            history = AuditTrail()\n\n        class Meta:\n            app_label = 'my_app'\n            verbose_name = 'Specimen Consent'\n\n\nValidators\n==========\n\nThe ``ConsentAgeValidator`` validates the date of birth to within a given age range, for example:\n\n.. code-block:: python\n\n    from edc_consent.validtors import ConsentAgeValidator\n\n    class MyConsent(ConsentQuotaMixin, BaseConsent):\n\n        dob = models.DateField(\n            validators=[ConsentAgeValidator(16, 64)])\n\n        quota = QuotaManager()\n\n        class Meta:\n            app_label = 'my_app'\n\nThe ``PersonalFieldsMixin`` includes a date of birth field and you can set the age bounds like this:\n\n.. code-block:: python\n\n    from edc_consent.validtors import ConsentAgeValidator\n    from edc_consent.models.fields import PersonalFieldsMixin\n\n    class MyConsent(ConsentQuotaMixin, PersonalFieldsMixin, BaseConsent):\n\n        quota = QuotaManager()\n\n        MIN_AGE_OF_CONSENT = 18\n        MAX_AGE_OF_CONSENT = 64\n\n        class Meta:\n            app_label = 'my_app'\n\n\nCommon senarios\n===============\n\nTracking the consent version with collected data\n++++++++++++++++++++++++++++++++++++++++++++++++\n\nAll model data is tagged with the consent version identified in ``ConsentDefinition`` for the consent model class and report_datetime.\n\nReconsenting consented subjects when the consent changes\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\nThe consent model is unique on subject_identifier, identity and version. If a new consent version is added to ``ConsentDefinition``, a new consent will be required for each subject as data is reported within the validity period of the new consent.\n\nSome care must be taken to ensure that the consent model is queried with an understanding of the unique constraint.\n\n\nLinking the consent version to added or removed model fields on models that require consent\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\nTODO\n\nInfants use mother's consent\n++++++++++++++++++++++++++++\n\nTODO\n\nBy adding the property ``consenting_subject_identifier`` to the consent\n\n\nPatient names\n=============\nIf patient names need to be removed from the data collection, there are a few helper\nattributes and methods to consider.\n\n``settings.EDC_CONSENT_REMOVE_PATIENT_NAMES_FROM_COUNTRIES: list[str]``\n\nIf given a list of country names, name fields will be removed from any admin.fieldset.\n\nSee also edc_sites.all_sites\n\n``ConsentModelAdminMixin.get_fieldsets``\n\n.. code-block:: python\n\n    def get_fieldsets(self, request, obj=None):\n        fieldsets = super().get_fieldsets(request, obj)\n        for country in get_remove_patient_names_from_countries():\n            site = getattr(request, \"site\", None)\n            if site and site.id in [s.site_id for s in self.all_sites.get(country)]:\n                return self.fieldsets_without_names(fieldsets)\n        return fieldsets\n\nThis method could be added to any ModeLadmin with names.\n\n\n\nusing\n\n\nOther TODO\n==========\n\n* ``Timepoint`` model update in ``save`` method of models requiring consent\n* handle added or removed model fields (questions) because of consent version change\n* review verification actions\n* management command to update version on models that require consent (if edc_consent added after instances were created)\n* handle re-consenting issues, for example, if original consent was restricted by age (16-64) but the re-consent is not. May need to open upper bound.\n\n\n\n.. |pypi| image:: https://img.shields.io/pypi/v/edc-consent.svg\n    :target: https://pypi.python.org/pypi/edc-consent\n\n.. |actions| image:: https://github.com/clinicedc/edc-consent/actions/workflows/build.yml/badge.svg\n  :target: https://github.com/clinicedc/edc-consent/actions/workflows/build.yml\n\n.. |codecov| image:: https://codecov.io/gh/clinicedc/edc-consent/branch/develop/graph/badge.svg\n  :target: https://codecov.io/gh/clinicedc/edc-consent\n\n.. |downloads| image:: https://pepy.tech/badge/edc-consent\n   :target: https://pepy.tech/project/edc-consent\n",
    "bugtrack_url": null,
    "license": "GPL license, see LICENSE",
    "summary": "Base models, forms and admin for participant ICF for clinicedc/edc projects",
    "version": "0.3.83",
    "project_urls": {
        "Homepage": "https://github.com/clinicedc/edc-consent"
    },
    "split_keywords": [
        "django edc participant icf",
        " clinicedc",
        " clinical trials"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6551f4644f8d32a7bf021470979ffa59ed4d92607679c8a40281b8ad74db401e",
                "md5": "34d476b8b50c127f163d371b17cb34b2",
                "sha256": "d89d8679529e625e46238af13296e6f0c0f9a6b739fe374c4d8dac101d647354"
            },
            "downloads": -1,
            "filename": "edc_consent-0.3.83-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "34d476b8b50c127f163d371b17cb34b2",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 87876,
            "upload_time": "2024-04-18T02:25:57",
            "upload_time_iso_8601": "2024-04-18T02:25:57.363885Z",
            "url": "https://files.pythonhosted.org/packages/65/51/f4644f8d32a7bf021470979ffa59ed4d92607679c8a40281b8ad74db401e/edc_consent-0.3.83-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "87f76bf47f8dd737c3dc3bc2283f819d0516069ef906c8c9c8e8f68dca2b4372",
                "md5": "a34c08c4b15122e980b65538a42e56a3",
                "sha256": "33dcb2c8c5ede0a4add0eca8859885731441389a78ac86b6f4c137147a438206"
            },
            "downloads": -1,
            "filename": "edc_consent-0.3.83.tar.gz",
            "has_sig": false,
            "md5_digest": "a34c08c4b15122e980b65538a42e56a3",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 72164,
            "upload_time": "2024-04-18T02:26:00",
            "upload_time_iso_8601": "2024-04-18T02:26:00.024106Z",
            "url": "https://files.pythonhosted.org/packages/87/f7/6bf47f8dd737c3dc3bc2283f819d0516069ef906c8c9c8e8f68dca2b4372/edc_consent-0.3.83.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-18 02:26:00",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "clinicedc",
    "github_project": "edc-consent",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "edc-consent"
}
        
Elapsed time: 0.50348s