|pypi| |actions| |codecov| |downloads|

edc-form-runners
----------------

Classes to manually run modelform validation for clinicedc/edc projects.


The ``FormRunner`` class
++++++++++++++++++++++++

You can use the ``FormRunner`` to rerun modelform validation on all instances of a model.

For example:

.. code-block:: python

    runner = FormRunner("intecomm_subject.vitals")
    runner.run_all()

This re-runs ``form.is_valid()`` for all instances of ``intecomm_subject.vitals``.
If the Vitals ``ModelForm`` does not validate, the ``FormRunner`` stores each error
of the ``form.errors`` dictionary in model ``Issue`` where
one instance of ``Issue`` is created per field_name:error_message pair.

`Note: Your model must be registered with Admin.`

``run_form_runners``
++++++++++++++++++++

You can run a ``FormRunner`` for every CRF/Requisition model in a module:

.. code-block:: python

    from django.apps import apps as django_apps
    from edc_form_runners.run_form_runners import run_form_runners

    run_form_runners(app_labels=["intecomm_subject"])


or just list a few models explicitly:

.. code-block:: python

    from django.apps import apps as django_apps
    from edc_form_runners.run_form_runners import run_form_runners

    run_form_runners(model_names=["intecomm_subject.vitals", "intecomm_subject.medications"])


Custom FormRunners
++++++++++++++++++

You may wish to ignore some errors; that is, prevent ``FormRunner`` from creating an ``Issue`` instance
for specific fields that do not validate. To do this create a custom ``FormRunner`` for your model
and list the field names to exclude:

.. code-block:: python

    class HtnMedicationAdherenceFormRunner(FormRunner):
        model_name = "intecomm_subject.htnmedicationadherence"
        exclude_formfields = ["pill_count"]


Now you can use the custom ``FormRunner``:

.. code-block:: python

    runner = HtnMedicationAdherenceFormRunner()
    runner.run_all()

if field ``pill_count`` does not validate, the error message will not be written to the ``Issues`` table.

Registering Custom FormRunners
++++++++++++++++++++++++++++++

A custom ``FormRunner`` must be registered to be used by ``edc_form_runners``.

Declare your custom ``FormRunnners`` in module ``form_runners.py`` in the root of your app:

.. code-block:: python

    # form_runners.py
    from edc_form_runners.decorators import register
    from edc_form_runners.form_runner import FormRunner


    @register
    class HtnMedicationAdherenceFormRunner(FormRunner):
        model_name = "intecomm_subject.htnmedicationadherence"
        exclude_formfields = ["pill_count"]

    @register
    class DmMedicationAdherenceFormRunner(FormRunner):
        model_name = "intecomm_subject.dmmedicationadherence"
        exclude_formfields = ["pill_count"]


The ``register`` decorator registers the custom classes with ``site_form_runners``.


``get_form_runner``
+++++++++++++++++++

``edc_form_runners`` gets ``FormRunners`` using ``get_form_runner``.
Given a model name in ``label_lower`` format, ``get_form_runner`` checks the site global (``site_form_runners``) and returns
a custom ``FormRunner``, if it exists, otherwise returns the default ``FormRunner``.

In your code you should use ``get_form_runner``:

.. code-block:: python

    # good, returned DmMedicationAdherenceFormRunner instead of the default FormRunner
    runner = get_form_runner("intecomm_subject.dmmedicationadherence")
    runner.run()

    # works but does not use your custom form runner
    runner = FormRunner("intecomm_subject.dmmedicationadherence")
    runner.run_all()


Management Command ``run_form_runners``
+++++++++++++++++++++++++++++++++++++++

You can use the management command ``run_form_runners`` to run form runners for some or
all CRF/Requisitions. Run this command to initially populate ``Issue`` table and whenever you
change validation logic for a form.

Pass the management command one or more app_labels separated by comma:

.. code-block:: bash

    >>> python manage.py run_form_runners -a intecomm_subject

or pass one or more model names (label_lower format) separated by comma:

.. code-block:: bash

    >>> python manage.py run_form_runners -m intecomm_subject.vitals,intecomm_subject.dmmedicationadherence

You can skip a model as well:

.. code-block:: bash

    >>> python manage.py run_form_runners -a intecomm_subject -s intecomm_subject.medicationadherence

``Issue`` ChangeList
++++++++++++++++++++

The ``ChangeList`` for the ``Issue model`` is available in ``edc_data_manager`` and ``edc_form_runners``.
You would typically use the one in ``edc_data_manager``.

From the change list you can:

* search, filter and re-order
* refresh selected ``Issue`` instances from the action menu.
* navigate to a subject`s dashboard

Integrated with the Subject Dashboard
+++++++++++++++++++++++++++++++++++++

The subject dashboard shows an "Issues" badge next to a CRF or Requisition if one exists. You can
hover over the badge to see some of the error messages detected when the ``FormRunner`` last ran.

If a user edits a CRF with a detected issue and the corrected form validates withour error, the
``Issue`` instance is deleted and the badge is no longer displayed.
(See also ``signals.py``)


``FormRunner`` is ``clinicedc`` specific
++++++++++++++++++++++++++++++++++++++++
At the moment, the ``FormRunner`` class is currently ``clinicedc`` specific in that it only works for models with a
``subject_identifier`` or related_visit FK (e.g. ``subject_visit``).

The ``post_save`` signal that updates Issues listens for ``clinicedc`` CRFs and Requisitions by testing if the model instance
is an instance of ``CrfModelMixin``, ``CrfNoManagerModelMixin`` or ``RequisitionModelMixin``.


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

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

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

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

