Compiled release-level

  1. Determine the type of check (see Repository structure).

  2. Find a check under the corresponding contracting_process/resource_level sub-directory to copy as a starting point.

  3. Add the check to the contracting_process/resource_level/definitions.py file. For example:

        "consistent.number_of_tenderers": number_of_tenderers.calculate,
        "consistent.tender_value": tender_value.calculate,
        "consistent.contracts_value": contracts_value.calculate,
        "consistent.parties_roles": parties_role.calculate,
    

Each check is a function, named calculate by convention, that:

  1. Accepts one argument, named item by convention

  2. Creates an empty result dict

  3. Determines whether the check can be calculated

  4. If not, sets result["meta"] = {"reason": "..."} and returns the result dict

  5. Determines whether the check passes

    Note

    Some ID fields allow both integer and string values. When resolving references by comparing IDs, the check should fail if the IDs are different types. It should neither succeed nor N/A (it is likely to N/A if IDs are not coerced to string).

  6. Sets these keys on the result object:

    result (boolean)

    Whether the check passes

    application_count (integer)

    The number of times the check was applied (for example, once per array entry)

    pass_count (integer)

    The number of times the check passed

    meta (object)

    Any additional data to help interpret the result. For example, some coherence checks store pairs of incoherent dates, like:

      {
          "path_1": first_date["path"],
          "value_1": first_date["value"],
          "path_2": second_date["path"],
          "value_2": second_date["value"],
      }
    
    If storing input data, store raw data, not processed data.
    
  7. Returns the result dict

An empty result dict looks like:

    """
    Initialize a compiled release-level check result.

    :param version: the check's version
    """
    return {
        "result": None,
        "meta": None,
        "application_count": None,
        "pass_count": None,
        "version": version,
    }

Example

"""
If a milestone's ``status`` is unmet ('scheduled' or 'notMet'), then its ``dateMet`` is blank.
"""

from pelican.util.checks import complete_result_resource, get_empty_result_resource
from pelican.util.getter import deep_get, get_values

version = 1.0
applicable_statuses = {"scheduled", "notMet"}


def calculate(item):
    result = get_empty_result_resource(version)

    milestones = []
    for path in ("planning", "tender", "contracts", "contracts.implementation"):
        milestones.extend(
            v for v in get_values(item, f"{path}.milestones") if deep_get(v["value"], "status") in applicable_statuses
        )

    if not milestones:
        result["meta"] = {"reason": "no milestone is unmet"}
        return result

    application_count = 0
    pass_count = 0
    failed_paths = []
    for milestone in milestones:
        passed = not deep_get(milestone["value"], "dateMet")

        application_count += 1
        if passed:
            pass_count += 1
        else:
            failed_paths.append({"path": milestone["path"], "status": milestone["value"]["status"]})

    return complete_result_resource(result, application_count, pass_count, failed_paths=failed_paths)

Storage

The results for each item are stored in a single row in the resource_level_check table. A stored result value looks like (only two entries in the checks array are shown):

{
    "meta": {
        "ocid": "ocds-lcuori-0rw29R-003-2011-1",
        "item_id": 8477481
    },
    "checks": {
        "coherent.dates": {
            "meta": {
                "reason": "no pairs of dates are set"
            },
            "result": null,
            "version": 1,
            "pass_count": null,
            "application_count": null
        },
        "coherent.period": {
            "meta": {
                "failed_paths": [
                    {
                        "path_1": "contracts[0].period.startDate",
                        "path_2": "contracts[0].period.endDate",
                        "value_1": "2007-12-05T00:00:00-05:00",
                        "value_2": "2007-06-05T00:00:00-05:00"
                    }
                ]
            },
            "result": false,
            "version": 1,
            "pass_count": 1,
            "application_count": 2
        }
    }
}