Compiled release-level¶
Determine the type of check (see Repository structure).
Find a check under the corresponding
contracting_process/resource_levelsub-directory to copy as a starting point.Add the check to the
contracting_process/resource_level/definitions.pyfile. 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:
Accepts one argument, named
itemby conventionCreates an empty
resultdictDetermines whether the check can be calculated
If not, sets
result["meta"] = {"reason": "..."}and returns theresultdictDetermines 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).
Sets these keys on the
resultobject: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.
Returns the
resultdict
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
}
}
}