diff --git a/CHANGELOG.md b/CHANGELOG.md index 04c4472..9759ee2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + +- Extend new oneOf validation messages to OCDS 1.0 files [cove#895](https://github.com/OpenDataServices/cove/issues/895#issuecomment-558721218) + ## [0.12.2] - 2019-11-26 ### Fixed diff --git a/libcove/lib/common.py b/libcove/lib/common.py index 0b3c398..7545e04 100644 --- a/libcove/lib/common.py +++ b/libcove/lib/common.py @@ -144,7 +144,11 @@ def oneOf_draft4(validator, oneOf, instance, schema): # as it lives in the parent. # It will not match the releases array in a release package, because # there is no oneOf. - if schema.get("title") == "Releases": + if ( + schema.get("title") == "Releases" + or schema.get("description") + == "An array of linking identifiers or releases" + ): # If instance is not a list, or is a list of zero length, then # validating against either subschema will work. # Assume instance is an array of Linked releases, if there are no diff --git a/tests/lib/fixtures/common/1-0/record-package-schema.json b/tests/lib/fixtures/common/1-0/record-package-schema.json new file mode 100644 index 0000000..fc7cd8b --- /dev/null +++ b/tests/lib/fixtures/common/1-0/record-package-schema.json @@ -0,0 +1,146 @@ +{ + "id": "http://standard.open-contracting.org/schema/1__0__3/record-package-schema.json", + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Schema for an Open Contracting Record package", + "description": "The record package contains a list of records along with some publishing meta data. The records pull together all the releases under a single Open Contracting ID and compile them into the latest version of the information along with the history of any data changes.", + "type": "object", + "properties": { + "uri": { + "title": "Package Identifier", + "description": "The URI of this package that identifies it uniquely in the world.", + "type": "string", + "format": "uri" + }, + "publisher": { + "description": "Information to uniquely identify the publisher of this package.", + "type": "object", + "properties": { + "name": { + "title":"Name", + "description":"The name of the organisation or department responsible for publishing this data.", + "type": "string" + }, + "scheme": { + "title":"Scheme", + "description": "The scheme that holds the unique identifiers used to identify the item being identified.", + "type": ["string", "null"] + }, + "uid": { + "title":"uid", + "description": "The unique ID for this entity under the given ID scheme. Note the use of 'uid' rather than 'id'. See issue #245.", + "type": ["string", "null"] + }, + "uri": { + "title":"URI", + "description":"A URI to identify the publisher.", + "type": ["string", "null"], + "format" : "uri" + } + }, + "required": ["name"] + }, + "license": { + "description": "A link to the license that applies to the data in this datapackage. [Open Definition Conformant](http://opendefinition.org/licenses/) licenses are strongly recommended. The canonical URI of the license should be used. Documents linked from this file may be under other license conditions. ", + "type": ["string", "null"], + "format": "uri" + }, + "publicationPolicy": { + "description": "A link to a document describing the publishers publication policy.", + "type": ["string", "null"], + "format": "uri" + }, + "publishedDate": { + "description": "The date that this package was published.", + "type": "string", + "format": "date-time" + }, + "packages": { + "description": "A list of URIs of all the release packages that were used to create this record package.", + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "format": "uri" + }, + "uniqueItems": true + }, + "records": { + "description": "The records for this data package.", + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/record" }, + "uniqueItems": true + } + }, + "required": ["uri", "publisher", "publishedDate", "packages", "records"], + "definitions": { + "record": { + "type": "object", + "properties": { + "ocid": { + "title": "Open Contracting ID", + "description": "A unique identifier that identifies the unique Open Contracting Process. For more information see: http://standard.open-contracting.org/1.0/en/getting_started/contracting_process/", + "type": "string" + }, + "releases": { + "description": "An array of linking identifiers or releases", + "oneOf": [ + { + "title": "Linked releases", + "description": "A list of objects that identify the releases associated with this Open Contracting ID. The releases MUST be sorted into date order in the array, from oldest (at position 0) to newest (last).", + "type": "array", + "items": { + "description": "Information to uniquely identify the release.", + "type": "object", + "properties": { + "url": { + "description": "The url of the release which contains the url of the package with the releaseID appended using a fragment identifier e.g. http://standard.open-contracting.org/1.0/en/examples/tender.json#ocds-213czf-000-00001", + "type": ["string", "null"], + "format" : "uri" + }, + "date": { + "title": "Release Date", + "description": "The date of the release, should match `date` at the root level of the release. This is used to sort the releases in the list into date order.", + "type": "string", + "format": "date-time" + }, + "tag": { + "title": "Release Tag", + "description": "The tag should match the tag in the release. This provides additional context when reviewing a record to see what types of releases are included for this ocid.", + "type": "array", + "items": { + "type": "string", + "enum": ["planning", "tender", "tenderAmendment", "tenderUpdate", "tenderCancellation", "award", "awardUpdate", "awardCancellation", "contract", "contractUpdate", "contractAmendment", "implementation", "implementationUpdate", "contractTermination", "compiled"] + } + } + }, + "required": ["url", "date"] + }, + "minItems": 1 + }, + { + "title": "Embedded releases", + "description": "A list of releases, with all the data. The releases MUST be sorted into date order in the array, from oldest (at position 0) to newest (last).", + "type": "array", + "items": { + "$ref": "http://standard.open-contracting.org/schema/1__0__3/release-schema.json" + }, + "minItems": 1 + } + ] + }, + "compiledRelease": { + "title": "Compiled release", + "description": "This is the latest version of all the contracting data, it has the same schema as an open contracting release.", + "$ref": "http://standard.open-contracting.org/schema/1__0__3/release-schema.json" + }, + "versionedRelease": { + "title": "Versioned release", + "description": "This contains the history of the data in the compiledRecord. With all versions of the information and the release they came from.", + "$ref": "http://standard.open-contracting.org/schema/1__0__3/versioned-release-validation-schema.json" + } + }, + "required": ["ocid", "releases"] + } + } +} diff --git a/tests/lib/fixtures/common/1-0/records_invalid_releases.json b/tests/lib/fixtures/common/1-0/records_invalid_releases.json new file mode 100644 index 0000000..cc5f42a --- /dev/null +++ b/tests/lib/fixtures/common/1-0/records_invalid_releases.json @@ -0,0 +1,58 @@ +{ + "uri": "http://example.org/invalid_record.json", + "publishedDate": "2019-09-18T17:56:21.078Z", + "publisher": { + "name": "A Publisher Name" + }, + "packages": ["http://example.org/release_package.json"], + "records": [ + { + "ocid": "EXAMPLE-0", + "releases": [] + }, + { + "ocid": "EXAMPLE-1", + "releases": [ + {} + ] + }, + { + "ocid": "EXAMPLE-2", + "releases": [ + {"id": "EXAMPLE-2-1"} + ] + }, + { + "ocid": "EXAMPLE-3", + "releases": [ + {"url": "http://example.org/releases.json#EXAMPLE-3-1"} + ] + }, + { + "ocid": "EXAMPLE-4", + "releases": [ + {"id": "EXAMPLE-4-1"}, + {} + ] + }, + { + "ocid": "EXAMPLE-5", + "releases": [ + {"id": "EXAMPLE-5-1"}, + {"url": "http://example.org/releases.json#EXAMPLE-5-2"} + ] + }, + { + "ocid": "EXAMPLE-97", + "releases": "a string" + }, + { + "ocid": "EXAMPLE-98", + "releases": null + }, + { + "ocid": "EXAMPLE-99", + "releases": {} + } + ] +} diff --git a/tests/lib/fixtures/common/1-0/release-schema.json b/tests/lib/fixtures/common/1-0/release-schema.json new file mode 100644 index 0000000..d24158a --- /dev/null +++ b/tests/lib/fixtures/common/1-0/release-schema.json @@ -0,0 +1,1096 @@ +{ + "id": "http://standard.open-contracting.org/schema/1__0__3/release-schema.json", + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Schema for an Open Contracting Release", + "type": "object", + "properties": { + "ocid": { + "title": "Open Contracting ID", + "description": "A globally unique identifier for this Open Contracting Process. Composed of a publisher prefix and an identifier for the contracting process. For more information see the [Open Contracting Identifier guidance](http://standard.open-contracting.org/1.0/en/schema/identifiers/)", + "type": "string", + "mergeStrategy": "ocdsOmit" + }, + "id": { + "title": "Release ID", + "description": "A unique identifier that identifies this release. A release ID must be unique within a release-package and must not contain the # character.", + "type": "string", + "mergeStrategy": "ocdsOmit" + }, + "date": { + "title": "Release Date", + "description": "The date this information is released, it may well be the same as the parent publishedDate, it must not be later than the publishedDate from the parent package. It is used to determine merge order.", + "type": "string", + "format": "date-time", + "mergeStrategy": "ocdsOmit" + }, + "tag": { + "title": "Release Tag", + "description": "A value from the [releaseTag codelist](http://standard.open-contracting.org/1.0/en/schema/codelists/#release-tag) that identifies the nature of the release being made. Tags may be used to filter release, or, in future, for advanced validation when certain kinds of releases should contain certain fields.", + "type": "array", + "items": { + "type": "string", + "enum": ["planning", "tender", "tenderAmendment", "tenderUpdate", "tenderCancellation", "award", "awardUpdate", "awardCancellation", "contract", "contractUpdate", "contractAmendment", "implementation", "implementationUpdate", "contractTermination", "compiled"] + }, + "mergeStrategy": "ocdsOmit" + }, + "initiationType": { + "title": "Initiation type", + "description": "String specifying the type of initiation process used for this contract, taken from the [initiationType](http://standard.open-contracting.org/1.0/en/schema/codelists/#initiation-type) codelist. Currently only tender is supported.", + "type": "string", + "enum": ["tender"], + "mergeStrategy": "ocdsVersion" + }, + "planning": { + "title": "Planning", + "description": "Information from the planning phase of the contracting process. This includes information related to the process of deciding what to contract for, when and how.", + "$ref": "#/definitions/Planning" + }, + "tender": { + "title": "Tender", + "description": "The activities undertaken in order to enter into a contract.", + "$ref": "#/definitions/Tender" + }, + "buyer": { + "title": "Buyer", + "description": "The buyer is the entity whose budget will be used to purchase the goods. This may be different from the procuring agency who may be specified in the tender data.", + "$ref": "#/definitions/Organization" + }, + "awards": { + "title": "Awards", + "description": "Information from the award phase of the contracting process. There may be more than one award per contracting process e.g. because the contract is split amongst different providers, or because it is a standing offer.", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": {"idRef": "id"}, + "items": { "$ref": "#/definitions/Award" }, + "uniqueItems": true + }, + "contracts": { + "title": "Contracts", + "description": "Information from the contract creation phase of the procurement process.", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": {"idRef": "id"}, + "items": {"$ref": "#/definitions/Contract" }, + "uniqueItems": true + }, + "language": { + "title": "Release language", + "description": "Specifies the default language of the data using either two-digit ISO 639-1, or extended BCP47 language tags. The use of two-letter codes from ISO 639-1 is strongly recommended.", + "type": ["string", "null"], + "default": "en", + "mergeStrategy": "ocdsVersion" + } + }, + "required": ["ocid", "id", "date", "tag", "initiationType"], + "definitions": { + "Planning": { + "title": "Planning", + "description": "Information from the planning phase of the contracting process. Note that many other fields may be filled in a planning release, in the appropriate fields in other schema sections, these would likely be estimates at this stage e.g. totalValue in tender", + "type": "object", + "properties": { + "budget": { "$ref": "#/definitions/Budget" }, + "rationale": { + "title":"Rationale", + "description": "The rationale for the procurement provided in free text. More detail can be provided in an attached document.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "documents": { + "title":"Documents", + "description": "A list of documents related to the planning process.", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Document" } + } + }, + "patternProperties": { + "^(rationale_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Tender": { + "title": "Tender", + "description": "Data regarding tender process - publicly inviting prospective contractors to submit bids for evaluation and selecting a winner or winners.", + "type": "object", + "required": ["id"], + "properties": { + "id": { + "title": "Tender ID", + "description": "An identifier for this tender process. This may be the same as the ocid, or may be drawn from an internally held identifier for this tender.", + "type": ["string", "integer"], + "mergeStrategy": "ocdsVersion" + }, + "title": { + "title":"Tender Title", + "description": "Tender title", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "description": { + "title":"Tender description", + "description": "Tender description", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "status": { + "title": "Tender status", + "description": "The current status of the tender based on the [tenderStatus codelist](http://standard.open-contracting.org/1.0/en/schema/codelists/#tender-status)", + "type": ["string", "null"], + "enum": ["planned", "active", "cancelled", "unsuccessful", "complete", null], + "mergeStrategy": "ocdsVersion" + }, + "items": { + "title": "Items to be procured", + "description": "The goods and services to be purchased, broken into line items wherever possible. Items should not be duplicated, but a quantity of 2 specified instead.", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Item" }, + "uniqueItems": true + }, + "minValue": { + "title":"Minimum value", + "description": "The minimum estimated value of the procurement.", + "$ref": "#/definitions/Value" + }, + "value": { + "title":"Value", + "description": "The total upper estimated value of the procurement.", + "$ref": "#/definitions/Value" + }, + "procurementMethod": { + "title":"Procurement method", + "description": "Specify tendering method against the [method codelist](http://standard.open-contracting.org/1.0/en/schema/codelists/#method) as per [GPA definitions](http://www.wto.org/english/docs_e/legal_e/rev-gpr-94_01_e.htm) of Open, Selective, Limited", + "type": ["string", "null"], + "enum": ["open", "selective", "limited", null], + "mergeStrategy": "ocdsVersion" + }, + "procurementMethodDetails": { + "title":"Procurement method details", + "description": "Additional detail on the procurement method used. This field may be used to provide the local name of the particular procurement method used.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "procurementMethodRationale": { + "title":"Procurement method rationale", + "description": "Rationale of procurement method, especially in the case of Limited tendering.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "awardCriteria": { + "title":"Award criteria", + "description": "Specify the award criteria for the procurement, using the [award criteria codelist](http://standard.open-contracting.org/1.0/en/schema/codelists/#award-criteria)", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "awardCriteriaDetails": { + "title":"Award criteria details", + "description": "Any detailed or further information on the award or selection criteria.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "submissionMethod": { + "title":"Submission method", + "description": "Specify the method by which bids must be submitted, in person, written, or electronic auction. Using the [submission method codelist](http://standard.open-contracting.org/1.0/en/schema/codelists/#submission-method)", + "type": ["array", "null"], + "items": { + "type": "string" + }, + "mergeStrategy": "ocdsVersion" + }, + "submissionMethodDetails" : { + "title":"Submission method details", + "description": "Any detailed or further information on the submission method. This may include the address, e-mail address or online service to which bids should be submitted, and any special requirements to be followed for submissions.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "tenderPeriod": { + "title":"Tender period", + "description": "The period when the tender is open for submissions. The end date is the closing date for tender submissions.", + "$ref": "#/definitions/Period" + }, + "enquiryPeriod": { + "title":"Enquiry period", + "description": "The period during which enquiries may be made and answered.", + "$ref": "#/definitions/Period" + }, + "hasEnquiries": { + "title":"Has enquiries?", + "description": "A Yes/No field to indicate whether enquiries were part of tender process.", + "type": ["boolean", "null"], + "mergeStrategy": "ocdsVersion" + }, + "eligibilityCriteria": { + "title":"Eligibility criteria", + "description": "A description of any eligibility criteria for potential suppliers.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "awardPeriod": { + "title":"Award period", + "description": "The date or period on which an award is anticipated to be made.", + "$ref": "#/definitions/Period" + }, + "numberOfTenderers": { + "title":"Number of tenderers", + "description": "The number of entities who submit a tender.", + "type": ["integer", "null"], + "mergeStrategy": "ocdsVersion" + }, + "tenderers": { + "title":"Tenderers", + "description": "All entities who submit a tender.", + "type": "array", + "items": { "$ref": "#/definitions/Organization" }, + "uniqueItems": true, + "mergeStrategy": "ocdsVersion" + }, + "procuringEntity": { + "title":"Procuring entity", + "description": "The entity managing the procurement, which may be different from the buyer who is paying / using the items being procured.", + "$ref": "#/definitions/Organization" + }, + "documents": { + "title":"Documents", + "description": "All documents and attachments related to the tender, including any notices. See the [documentType codelist](http://standard.open-contracting.org/1.0/en/schema/codelists/#document-type) for details of potential documents to include.", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Document" } + }, + "milestones": { + "title":"Milestones", + "description": "A list of milestones associated with the tender.", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Milestone" } + }, + "amendment": { "$ref": "#/definitions/Amendment" } + }, + "patternProperties": { + "^(title_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + }, + "^(description_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + }, + "^(procurementMethodRationale_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + }, + "^(awardCriteriaDetails_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + }, + "^(submissionMethodDetails_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + }, + "^(eligibilityCriteria_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Award": { + "title": "Award", + "description": "An award for the given procurement. There may be more than one award per contracting process e.g. because the contract is split amongst different providers, or because it is a standing offer.", + "type": "object", + "required": ["id"], + "properties": { + "id": { + "title": "Award ID", + "description": "The identifier for this award. It must be unique and cannot change within the Open Contracting Process it is part of (defined by a single ocid). See the [identifier guidance](http://standard.open-contracting.org/1.0/en/schema/identifiers/) for further details.", + "type": ["string", "integer"], + "mergeStrategy": "overwrite" + }, + "title": { + "title":"Title", + "description": "Award title", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "description": { + "title":"Description", + "description": "Award description", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "status": { + "title": "Award status", + "description": "The current status of the award drawn from the [awardStatus codelist](http://standard.open-contracting.org/1.0/en/schema/codelists/#award-status)", + "type": ["string", "null"], + "enum": ["pending", "active", "cancelled", "unsuccessful", null], + "mergeStrategy": "ocdsVersion" + }, + "date": { + "title": "Award date", + "description": "The date of the contract award. This is usually the date on which a decision to award was made.", + "type": ["string", "null"], + "format": "date-time", + "mergeStrategy": "ocdsVersion" + }, + "value": { + "title":"Value", + "description": "The total value of this award. In the case of a framework contract this may be the total estimated lifetime value, or maximum value, of the agreement. There may be more than one award per procurement.", + "$ref": "#/definitions/Value" + }, + "suppliers": { + "title":"Suppliers", + "description": "The suppliers awarded this award. If different suppliers have been awarded different items of values, these should be split into separate award blocks.", + "type": "array", + "items": { "$ref": "#/definitions/Organization" }, + "uniqueItems": true, + "mergeStrategy": "ocdsVersion" + }, + "items": { + "title": "Items awarded", + "description": "The goods and services awarded in this award, broken into line items wherever possible. Items should not be duplicated, but the quantity specified instead.", + "type": "array", + "minItems": 1, + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Item" }, + "uniqueItems": true + }, + "contractPeriod": { + "title":"Contract period", + "description": "The period for which the contract has been awarded.", + "$ref": "#/definitions/Period" + }, + "documents": { + "title":"Documents", + "description": "All documents and attachments related to the award, including any notices.", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Document" }, + "uniqueItems": true + }, + "amendment": { + "$ref": "#/definitions/Amendment" + } + }, + "patternProperties": { + "^(title_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + }, + "^(description_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Contract": { + "type": "object", + "title": "Contract", + "description": "Information regarding the signed contract between the buyer and supplier(s).", + "required": ["id", "awardID"], + "properties": { + "id": { + "title": "Contract ID", + "description": "The identifier for this contract. It must be unique and cannot change within its Open Contracting Process (defined by a single ocid). See the [identifier guidance](http://standard.open-contracting.org/1.0/en/schema/identifiers/) for further details.", + "type": ["string", "integer"], + "mergeStrategy": "overwrite" + }, + "awardID": { + "title": "Award ID", + "description": "The award.id against which this contract is being issued.", + "type": ["string", "integer"], + "mergeStrategy": "ocdsVersion" + }, + "title": { + "description": "Contract title", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "description": { + "title":"Contract description", + "description": "Contract description", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "status": { + "title": "Contract status", + "description": "The current status of the contract. Drawn from the [contractStatus codelist](http://standard.open-contracting.org/1.0/en/schema/codelists/#contract-status)", + "type": ["string", "null"], + "enum": ["pending", "active", "cancelled", "terminated", null], + "mergeStrategy": "ocdsVersion" + }, + "period": { + "title":"Period", + "description": "The start and end date for the contract.", + "$ref": "#/definitions/Period" + }, + "value": { + "title":"Value", + "description": "The total value of this contract.", + "$ref": "#/definitions/Value" + }, + "items": { + "title": "Items contracted", + "description": "The goods, services, and any intangible outcomes in this contract. Note: If the items are the same as the award do not repeat.", + "type": "array", + "minItems": 1, + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Item" }, + "uniqueItems": true + }, + "dateSigned": { + "title":"Date signed", + "description": "The date the contract was signed. In the case of multiple signatures, the date of the last signature.", + "type": ["string", "null"], + "format": "date-time", + "mergeStrategy": "ocdsVersion" + }, + "documents": { + "title":"Documents", + "description": "All documents and attachments related to the contract, including any notices.", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Document" }, + "uniqueItems": true + }, + "amendment": { + "$ref": "#/definitions/Amendment" + }, + "implementation": { + "title": "Implementation", + "description": "Information related to the implementation of the contract in accordance with the obligations laid out therein.", + "$ref": "#/definitions/Implementation" + } + }, + "patternProperties": { + "^(title_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + }, + "^(description_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Implementation": { + "type": "object", + "title": "Implementation", + "description": "Information during the performance / implementation stage of the contract.", + "properties": { + "transactions": { + "title":"Transactions", + "description": "A list of the spending transactions made against this contract", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Transaction" }, + "uniqueItems": true + }, + "milestones": { + "title":"Milestones", + "description": "As milestones are completed, milestone completions should be documented.", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Milestone" }, + "uniqueItems": true + }, + "documents":{ + "title":"Documents", + "description": "Documents and reports that are part of the implementation phase e.g. audit and evaluation reports.", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Document" }, + "uniqueItems": true + } + } + }, + "Milestone": { + "title":"Milestone", + "type": "object", + "required": ["id"], + "properties": { + "id": { + "title":"ID", + "description": "A local identifier for this milestone, unique within this block. This field is used to keep track of multiple revisions of a milestone through the compilation from release to record mechanism.", + "type": ["string", "integer"], + "mergeStrategy": "overwrite" + }, + "title": { + "title":"Title", + "description": "Milestone title", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "description": { + "title":"Description", + "description": "A description of the milestone.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "dueDate": { + "title":"Due date", + "description": "The date the milestone is due.", + "type": ["string", "null"], + "format": "date-time", + "mergeStrategy": "ocdsVersion" + }, + "dateModified" : { + "title":"Date modified", + "description": "The date the milestone was last reviewed or modified and the status was altered or confirmed to still be correct.", + "type": ["string", "null"], + "format": "date-time", + "mergeStrategy": "ocdsVersion" + }, + "status": { + "title":"Status", + "description": "The status that was realized on the date provided in dateModified, drawn from the [milestoneStatus codelist](http://standard.open-contracting.org/1.0/en/schema/codelists/#milestone-status).", + "type": ["string", "null"], + "enum": ["met", "notMet", "partiallyMet", null], + "mergeStrategy": "ocdsVersion" + }, + "documents": { + "title":"Documents", + "description": "List of documents associated with this milestone.", + "type": "array", + "mergeStrategy": "arrayMergeById", + "mergeOptions": { "idRef": "id" }, + "items": { "$ref": "#/definitions/Document" }, + "uniqueItems": true + } + }, + "patternProperties": { + "^(title_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + }, + "^(description_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Document": { + "type": "object", + "title": "Document", + "description": "Links to, or descriptions of, external documents can be attached at various locations within the standard. Documents may be supporting information, formal notices, downloadable forms, or any other kind of resource that should be made public as part of full open contracting.", + "required": ["id"], + "properties": { + "id": { + "title":"ID", + "description": "A local, unique identifier for this document. This field is used to keep track of multiple revisions of a document through the compilation from release to record mechanism.", + "type": ["string", "integer"], + "mergeStrategy": "overwrite" + }, + "documentType": { + "title":"Document type", + "description": "A classification of the document described taken from the [documentType codelist](http://standard.open-contracting.org/1.0/en/schema/codelists/#document-type). Values from the provided codelist should be used wherever possible, though extended values can be provided if the codelist does not have a relevant code.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "title": { + "title":"Title", + "description": "The document title.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "description": { + "title":"Description", + "description": "A short description of the document. We recommend descriptions do not exceed 250 words. In the event the document is not accessible online, the description field can be used to describe arrangements for obtaining a copy of the document.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "url": { + "title":"URL", + "description": " direct link to the document or attachment. The server providing access to this document should be configured to correctly report the document mime type.", + "type": ["string", "null"], + "format": "uri", + "mergeStrategy": "ocdsVersion" + }, + "datePublished": { + "title":"Date published", + "description": "The date on which the document was first published. This is particularly important for legally important documents such as notices of a tender.", + "type": ["string", "null"], + "format": "date-time", + "mergeStrategy": "ocdsVersion" + }, + "dateModified": { + "title":"Date modified", + "description": "Date that the document was last modified", + "type": ["string", "null"], + "format": "date-time", + "mergeStrategy": "ocdsVersion" + }, + "format": { + "title":"Format", + "description": "The format of the document taken from the [IANA Media Types code list](http://www.iana.org/assignments/media-types/), with the addition of one extra value for 'offline/print', used when this document entry is being used to describe the offline publication of a document. Use values from the template column. Links to web pages should be tagged 'text/html'.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "language": { + "title":"Language", + "description": "Specifies the language of the linked document using either two-digit [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes), or extended [BCP47 language tags](http://www.w3.org/International/articles/language-tags/). The use of two-letter codes from [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) is strongly recommended unless there is a clear user need for distinguishing the language subtype.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + } + }, + "patternProperties": { + "^(title_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + }, + "^(description_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Budget": { + "type": "object", + "title": "Budget information", + "description": "This section contain information about the budget line, and associated projects, through which this contracting process is funded. It draws upon data model of the [Fiscal Data Package](http://fiscal.dataprotocols.org/), and should be used to cross-reference to more detailed information held using a Budget Data Package, or, where no linked Budget Data Package is available, to provide enough information to allow a user to manually or automatically cross-reference with another published source of budget and project information.", + "properties": { + "source": { + "title": "Data Source", + "description": "Used to point either to a corresponding Budget Data Package, or to a machine or human-readable source where users can find further information on the budget line item identifiers, or project identifiers, provided here.", + "type":["string", "null"], + "mergeStrategy": "ocdsVersion", + "format": "uri" + }, + "id":{ + "title":"ID", + "description": "An identifier for the budget line item which provides funds for this contracting process. This identifier should be possible to cross-reference against the provided data source.", + "mergeStrategy": "ocdsVersion", + "type":["string", "integer", "null"] + }, + "description": { + "title": "Budget Source", + "description": "A short free text description of the budget source. May be used to provide the title of the budget line, or the programme used to fund this project.", + "mergeStrategy": "ocdsVersion", + "type": ["string", "null"] + }, + "amount": { + "title":"Amount", + "description": "The value of the budget line item.", + "$ref": "#/definitions/Value" + }, + "project": { + "title": "Project title", + "description": "The name of the project that through which this contracting process is funded (if applicable). Some organizations maintain a registry of projects, and the data should use the name by which the project is known in that registry. No translation option is offered for this string, as translated values can be provided in third-party data, linked from the data source above.", + "mergeStrategy": "ocdsVersion", + "type": ["string", "null"] + }, + "projectID": { + "title": "Project identifier", + "description": "An external identifier for the project that this contracting process forms part of, or is funded via (if applicable). Some organizations maintain a registry of projects, and the data should use the identifier from the relevant registry of projects.", + "mergeStrategy": "ocdsVersion", + "type": ["string", "integer", "null"] + }, + "uri":{ + "title": "Linked budget information", + "description": "A URI pointing directly to a machine-readable record about the related budget or projects for this contracting process.", + "mergeStrategy": "ocdsVersion", + "type": ["string", "null"], + "format": "uri" + } + }, + "patternProperties": { + "^(source_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + }, + "^(description_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + }, + "^(project_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Transaction": { + "type": "object", + "title": "Transaction information", + "description": "A spending transaction related to the contracting process. Draws upon the data models of the [Fiscal Data Package](http://fiscal.dataprotocols.org/) and the [International Aid Transpareny Initiative](http://iatistandard.org/activity-standard/iati-activities/iati-activity/transaction/) and should be used to cross-reference to more detailed information held using a Budget Data Package, IATI file, or to provide enough information to allow a user to manually or automatically cross-reference with some other published source of transactional spending data.", + "required": ["id"], + "properties": { + "id":{ + "title":"ID", + "description": "A unique identifier for this transaction. This identifier should be possible to cross-reference against the provided data source. For the budget data package this is the id, for IATI, the transaction reference.", + "type": ["string", "integer"], + "mergeStrategy": "overwrite" + }, + "source": { + "title": "Data source", + "description": "Used to point either to a corresponding Budget Data Package, IATI file, or machine or human-readable source where users can find further information on the budget line item identifiers, or project identifiers, provided here.", + "mergeStrategy": "ocdsVersion", + "type": ["string", "null"], + "format": "uri" + }, + "date": { + "title":"Date", + "description": "The date of the transaction", + "mergeStrategy": "ocdsVersion", + "type": ["string", "null"], + "format": "date-time" + }, + "amount": { + "title":"Amount", + "description": "The value of the transaction.", + "$ref": "#/definitions/Value" + }, + "providerOrganization":{ + "title":"Provider organization", + "description": "The Organization Identifier for the organization from which the funds in this transaction originate. Expressed following the Organizational Identifier standard - consult the documentation and the codelist.", + "$ref": "#/definitions/Identifier" + }, + "receiverOrganization":{ + "title":"Receiver organization", + "description": "The Organization Identifier for the organization which receives the funds in this transaction. Expressed following the Organizational Identifier standard - consult the documentation and the codelist.", + "$ref": "#/definitions/Identifier" + }, + "uri":{ + "title":"Linked spending information", + "description":"A URI pointing directly to a machine-readable record about this spending transaction.", + "mergeStrategy": "ocdsVersion", + "type":["string", "null"], + "format":"uri" + } + } + }, + "Organization": { + "title": "Organization", + "description": "An organization.", + "type": "object", + "properties": { + "identifier": { + "title":"Primary identifier", + "description": "The primary identifier for this organization. Identifiers that uniquely pick out a legal entity should be preferred. Consult the [organization identifier guidance](http://standard.open-contracting.org/1.0/en/schema/identifiers/) for the preferred scheme and identifier to use.", + "$ref": "#/definitions/Identifier" + }, + "additionalIdentifiers": { + "title":"Additional identifiers", + "description": "A list of additional / supplemental identifiers for the organization, using the [organization identifier guidance](http://standard.open-contracting.org/1.0/en/schema/identifiers/). This could be used to provide an internally used identifier for this organization in addition to the primary legal entity identifier.", + "type": "array", + "mergeStrategy": "ocdsVersion", + "items": { "$ref": "#/definitions/Identifier" }, + "uniqueItems": true + }, + "name": { + "title":"Common name", + "description": "The common name of the organization. The ID property provides an space for the formal legal name, and so this may either repeat that value, or could provide the common name by which this organization is known. This field could also include details of the department or sub-unit involved in this contracting process.", + "mergeStrategy": "ocdsVersion", + "type": ["string", "null"] + }, + "address": { "$ref": "#/definitions/Address" }, + "contactPoint": { "$ref": "#/definitions/ContactPoint" } + }, + "patternProperties": { + "^(name_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Item": { + "title":"Item", + "type": "object", + "description": "A good, service, or work to be contracted.", + "required": ["id"], + "properties": { + "id": { + "title":"ID", + "description": "A local identifier to reference and merge the items by. Must be unique within a given array of items.", + "type": ["string", "integer"], + "mergeStrategy": "overwrite" + }, + "description": { + "title":"Description", + "description": "A description of the goods, services to be provided.", + "mergeStrategy": "ocdsVersion", + "type": ["string", "null"] + }, + "classification": { + "title":"Classification", + "description": "The primary classification for the item. See the [itemClassificationScheme](http://standard.open-contracting.org/1.0/en/schema/codelists/#item-classification-scheme) to identify preferred classification lists, including CPV and GSIN.", + "$ref": "#/definitions/Classification" + }, + "additionalClassifications": { + "title":"Additional classifications", + "description": "An array of additional classifications for the item. See the [itemClassificationScheme](http://standard.open-contracting.org/1.0/en/schema/codelists/#item-classification-scheme) codelist for common options to use in OCDS. This may also be used to present codes from an internal classification scheme.", + "type": "array", + "mergeStrategy": "ocdsVersion", + "items": { "$ref": "#/definitions/Classification" }, + "uniqueItems": true + }, + "quantity": { + "title":"Quantity", + "description": "The number of units required", + "mergeStrategy": "ocdsVersion", + "minimum": 0, + "type": ["number", "null"] + }, + "unit": { + "title":"Unit", + "description": "Description of the unit which the good comes in e.g. hours, kilograms. Made up of a unit name, and the value of a single unit.", + "type": "object", + "properties": { + "name": { + "title":"Name", + "description": "Name of the unit", + "mergeStrategy": "ocdsVersion", + "type": ["string", "null"] + }, + "value": { + "title":"Value", + "description": "The monetary value of a single unit.", + "$ref": "#/definitions/Value" + } + }, + "patternProperties": { + "^(name_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + } + }, + "patternProperties": { + "^(description_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Amendment": { + "title":"Amendment", + "type": "object", + "title": "Amendment information", + "properties": { + "date": { + "title": "Amendment date", + "description":"The data of this amendment.", + "type": ["string", "null"], + "format": "date-time", + "mergeStrategy": "ocdsVersion" + }, + "changes": { + "title": "Amended fields", + "description": "An array change objects describing the fields changed, and their former values.", + "mergeStrategy": "ocdsVersion", + "type": "array", + "items": { + "type": "object", + "properties": { + "property": { + "title":"Property", + "description": "The property name that has been changed relative to the place the amendment is. For example if the contract value has changed, then the property under changes within the contract.amendment would be value.amount. ", + "type": "string" + }, + "former_value": { + "title":"Former Value", + "description": "The previous value of the changed property, in whatever type the property is.", + "type": ["string", "number", "integer", "array", "object", "null"] + } + } + } + }, + "rationale": { + "title":"Rationale", + "description": "An explanation for the amendment.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + } + }, + "patternProperties": { + "^(rationale_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Classification": { + "title":"Classification", + "type": "object", + "properties": { + "scheme": { + "title":"Scheme", + "description": "An classification should be drawn from an existing scheme or list of codes. This field is used to indicate the scheme/codelist from which the classification is drawn. For line item classifications, this value should represent an known [Item Classification Scheme](http://standard.open-contracting.org/1.0/en/schema/codelists/#item-classification-scheme) wherever possible.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "id": { + "title":"ID", + "description": "The classification code drawn from the selected scheme.", + "type": ["string", "integer", "null"], + "mergeStrategy": "ocdsVersion" + }, + "description": { + "title":"Description", + "description": "A textual description or title for the code.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "uri": { + "title":"URI", + "description": "A URI to identify the code. In the event individual URIs are not available for items in the identifier scheme this value should be left blank.", + "type": ["string", "null"], + "format" : "uri", + "mergeStrategy": "ocdsVersion" + } + }, + "patternProperties": { + "^(description_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Identifier": { + "title":"Identifier", + "type": "object", + "properties": { + "scheme": { + "title":"Scheme", + "description": "Organization identifiers be drawn from an existing identification scheme. This field is used to indicate the scheme or codelist in which the identifier will be found. This value should be drawn from the [Organization Identifier Scheme](http://standard.open-contracting.org/1.0/en/schema/codelists/#organization-identifier-scheme).", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "id": { + "title":"ID", + "description": "The identifier of the organization in the selected scheme.", + "type": ["string", "integer", "null"], + "mergeStrategy": "ocdsVersion" + }, + "legalName": { + "title":"Legal Name", + "description": "The legally registered name of the organization.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + }, + "uri": { + "title":"URI", + "description": "A URI to identify the organization, such as those provided by [Open Corporates](http://www.opencorporates.com) or some other relevant URI provider. This is not for listing the website of the organization: that can be done through the url field of the Organization contact point.", + "type": ["string", "null"], + "format" : "uri", + "mergeStrategy": "ocdsVersion" + } + }, + "patternProperties": { + "^(legalName_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Address": { + "title":"Address", + "description": "An address. This may be the legally registered address of the organization, or may be a correspondence address for this particular contracting process.", + "type": "object", + "properties": { + "streetAddress": { + "title":"Street address", + "type": ["string", "null"], + "description": "The street address. For example, 1600 Amphitheatre Pkwy.", + "mergeStrategy": "ocdsVersion" + }, + "locality":{ + "title":"Locality", + "type": ["string", "null"], + "description": "The locality. For example, Mountain View.", + "mergeStrategy": "ocdsVersion" + }, + "region": { + "title":"Region", + "type": ["string", "null"], + "description":"The region. For example, CA.", + "mergeStrategy": "ocdsVersion" + }, + "postalCode": { + "title":"Postalcode", + "type": ["string", "null"], + "description":"The postal code. For example, 94043.", + "mergeStrategy": "ocdsVersion" + }, + "countryName": { + "title":"Country name", + "type": ["string", "null"], + "description":"The country name. For example, United States.", + "mergeStrategy": "ocdsVersion" + } + }, + "patternProperties": { + "^(countryName_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "ContactPoint": { + "title":"Contact point", + "type": "object", + "description": "An person, contact point or department to contact in relation to this contracting process.", + "properties": { + "name": { + "title":"Name", + "type": ["string", "null"], + "description":"The name of the contact person, department, or contact point, for correspondence relating to this contracting process.", + "mergeStrategy": "ocdsVersion" + }, + "email":{ + "title":"Email", + "type": ["string", "null"], + "description":"The e-mail address of the contact point/person.", + "mergeStrategy": "ocdsVersion" + }, + "telephone": { + "title":"Telephone", + "type": ["string", "null"], + "description":"The telephone number of the contact point/person. This should include the international dialling code.", + "mergeStrategy": "ocdsVersion" + }, + "faxNumber": { + "title":"Fax number", + "type": ["string", "null"], + "description":"The fax number of the contact point/person. This should include the international dialling code.", + "mergeStrategy": "ocdsVersion" + }, + "url": { + "title":"URL", + "type": ["string", "null"], + "description":"A web address for the contact point/person.", + "format": "uri", + "mergeStrategy": "ocdsVersion" + } + }, + "patternProperties": { + "^(name_(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)))$": { + "type": ["string", "null"] + } + } + }, + "Value": { + "title":"Value", + "type": "object", + "properties": { + "amount": { + "title":"Amount", + "description": "Amount as a number.", + "type": ["number", "null"], + "mergeStrategy": "ocdsVersion" + }, + "currency": { + "title":"Currency", + "description": "The currency in 3-letter ISO 4217 format.", + "type": ["string", "null"], + "mergeStrategy": "ocdsVersion" + } + } + }, + "Period": { + "title":"Period", + "type": "object", + "properties": { + "startDate": { + "title":"Start date", + "description": "The start date for the period.", + "type": ["string", "null"], + "format": "date-time", + "mergeStrategy": "ocdsVersion" + }, + "endDate": { + "title":"End date", + "description": "The end date for the period.", + "type": ["string", "null"], + "format": "date-time", + "mergeStrategy": "ocdsVersion" + } + } + } + } +} diff --git a/tests/lib/test_common.py b/tests/lib/test_common.py index f738022..d931964 100644 --- a/tests/lib/test_common.py +++ b/tests/lib/test_common.py @@ -409,13 +409,148 @@ def get_release_pkg_schema_obj(self): @pytest.mark.parametrize( - "package_schema_filename,filename,validation_error_jsons_expected", + "package_schema_filename,filename,schema_subdir,validation_error_jsons_expected", [ - ("release-package-schema.json", "releases_no_validation_errors.json", []), - ("record-package-schema.json", "records_no_validation_errors.json", []), + ("release-package-schema.json", "releases_no_validation_errors.json", "", []), + ("record-package-schema.json", "records_no_validation_errors.json", "", []), ( "record-package-schema.json", "records_invalid_releases.json", + "", + [ + { + "error_id": None, + "message": "'date' is missing but required within 'releases'", + "message_safe": "date is missing but required within releases", + "validator": "required", + "assumption": "embedded_releases", + "message_type": "required", + "path_no_number": "records/releases", + "header": "date", + "header_extra": "releases/0", + "null_clause": "", + "values": [{"path": "records/2/releases/0"}], + }, + { + "error_id": None, + "message": "'date' is missing but required within 'releases'", + "message_safe": "date is missing but required within releases", + "validator": "required", + "assumption": "linked_releases", + "message_type": "required", + "path_no_number": "records/releases", + "header": "date", + "header_extra": "releases/0", + "null_clause": "", + "values": [ + {"path": "records/1/releases/0"}, + {"path": "records/3/releases/0"}, + ], + }, + { + "error_id": None, + "message": "'initiationType' is missing but required within 'releases'", + "message_safe": "initiationType is missing but required within releases", + "validator": "required", + "assumption": "embedded_releases", + "message_type": "required", + "path_no_number": "records/releases", + "header": "initiationType", + "header_extra": "releases/0", + "null_clause": "", + "values": [{"path": "records/2/releases/0"}], + }, + { + "error_id": None, + "message": "'ocid' is missing but required within 'releases'", + "message_safe": "ocid is missing but required within releases", + "validator": "required", + "assumption": "embedded_releases", + "message_type": "required", + "path_no_number": "records/releases", + "header": "ocid", + "header_extra": "releases/0", + "null_clause": "", + "values": [{"path": "records/2/releases/0"}], + }, + { + "error_id": None, + "message": "'releases' is not a JSON array", + "message_safe": "releases is not a JSON array", + "validator": "type", + "assumption": "linked_releases", + "message_type": "array", + "path_no_number": "records/releases", + "header": "releases", + "header_extra": "releases", + "null_clause": "is not null, and", + "values": [ + {"path": "records/6/releases", "value": "a string"}, + {"path": "records/7/releases", "value": None}, + {"path": "records/8/releases"}, + ], + }, + { + "error_id": None, + "message": "'tag' is missing but required within 'releases'", + "message_safe": "tag is missing but required within releases", + "validator": "required", + "assumption": "embedded_releases", + "message_type": "required", + "path_no_number": "records/releases", + "header": "tag", + "header_extra": "releases/0", + "null_clause": "", + "values": [{"path": "records/2/releases/0"}], + }, + { + "error_id": None, + "message": "'url' is missing but required within 'releases'", + "message_safe": "url is missing but required within releases", + "validator": "required", + "assumption": "linked_releases", + "message_type": "required", + "path_no_number": "records/releases", + "header": "url", + "header_extra": "releases/0", + "null_clause": "", + "values": [{"path": "records/1/releases/0"}], + }, + { + "error_id": "releases_both_embedded_and_linked", + "message": "This array should contain either entirely embedded releases or linked releases. Embedded releases contain an 'id' whereas linked releases do not. Your releases contain a mixture.", + "message_safe": "This array should contain either entirely embedded releases or linked releases. Embedded releases contain an 'id' whereas linked releases do not. Your releases contain a mixture.", + "validator": "oneOf", + "assumption": None, + "message_type": "oneOf", + "path_no_number": "records/releases", + "header": "releases", + "header_extra": "releases", + "null_clause": "", + "values": [ + {"path": "records/4/releases"}, + {"path": "records/5/releases"}, + ], + }, + { + "error_id": None, + "message": "[] is too short", + "message_safe": "[] is too short. You must supply at least one value, or remove the item entirely (unless it’s required).", + "validator": "minItems", + "assumption": "linked_releases", + "message_type": "minItems", + "path_no_number": "records/releases", + "header": "releases", + "header_extra": "releases", + "null_clause": "", + "values": [{"path": "records/0/releases"}], + }, + ], + ), + ( + "record-package-schema.json", + "records_invalid_releases.json", + "1-0", [ { "error_id": None, @@ -549,6 +684,7 @@ def get_release_pkg_schema_obj(self): ( "release-package-schema.json", "releases_non_unique.json", + "", [ { "message": "Non-unique id values", @@ -571,6 +707,7 @@ def get_release_pkg_schema_obj(self): ( "record-package-schema.json", "records_non_unique.json", + "", [ { "message": "Non-unique ocid values", @@ -593,6 +730,7 @@ def get_release_pkg_schema_obj(self): ( "release-package-schema.json", "releases_non_unique_no_id.json", + "", [ { "message": "'id' is missing but required", @@ -638,6 +776,7 @@ def get_release_pkg_schema_obj(self): ( "record-package-schema.json", "records_non_unique_no_ocid.json", + "", [ { "message": "'ocid' is missing but required", @@ -685,6 +824,7 @@ def get_release_pkg_schema_obj(self): ( "release-package-schema.json", "releases_unique.json", + "", [ { "message": "'id' is missing but required", @@ -717,6 +857,7 @@ def get_release_pkg_schema_obj(self): ( "record-package-schema.json", "records_unique.json", + "", [ { "message": "'ocid' is missing but required", @@ -749,10 +890,14 @@ def get_release_pkg_schema_obj(self): ], ) def test_validation_release_or_record_package( - package_schema_filename, filename, validation_error_jsons_expected + package_schema_filename, filename, validation_error_jsons_expected, schema_subdir ): schema_host = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "fixtures", "common", "" + os.path.dirname(os.path.realpath(__file__)), + "fixtures", + "common", + schema_subdir, + "", ) with open(os.path.join(schema_host, filename)) as fp: invalid_record_package = json.load(fp)