Compiled release-level¶
Determine the type of check (see Repository structure).
Find a check under the corresponding
contracting_process/resource_level
sub-directory to copy as a starting point.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:
Accepts one argument, named
item
by conventionCreates an empty
result
dictDetermines whether the check can be calculated
If not, sets
result["meta"] = {"reason": "..."}
and returns theresult
dictDetermines 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
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.
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
}
}
}