diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a281fc6..a54f8043 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ New options: * combine-release-packages: `--uri`, `--published-date` * compile: `--schema`, `--uri`, `--published-date`, `--linked-releases` +New commands: + +* upgrade + Removed commands: * measure diff --git a/README.rst b/README.rst index 00ee8ae9..6c71b14b 100644 --- a/README.rst +++ b/README.rst @@ -77,6 +77,17 @@ Optional arguments: cat tests/fixtures/realdata/release-package-1.json | ocdskit compile > out.json +upgrade +~~~~~~~ + +Upgrades packages and releases from an old version of OCDS to a new version. + +OCDS 1.0 `describes `__ an organization's ``name``, ``identifier``, ``address`` and ``contactPoint`` as relevant to identifying it. OCDS 1.1 `moves `__ organization data into a ``parties`` array. To upgrade from OCDS 1.0 to 1.1, we create an ``id`` for each organization, based on those identifying fields. This can result in duplicates in the ``parties`` array, if the same organization has different or missing ``name``, ``identifier``, ``address`` or ``contactPoint`` values in different contexts. This can also lead to data loss if the same organization has different values for other fields across occurrences; the command prints warnings in such cases. + +:: + + cat tests/fixtures/realdata/release-package-1.json | ocdskit upgrade 1.0:1.1 > out.json + package-releases ~~~~~~~~~~~~~~~~ diff --git a/ocdskit/cli/__main__.py b/ocdskit/cli/__main__.py index 58b5c1e2..7bd83f26 100644 --- a/ocdskit/cli/__main__.py +++ b/ocdskit/cli/__main__.py @@ -21,6 +21,7 @@ 'ocdskit.cli.commands.split_record_packages', 'ocdskit.cli.commands.split_release_packages', 'ocdskit.cli.commands.tabulate', + 'ocdskit.cli.commands.upgrade', 'ocdskit.cli.commands.validate', ) diff --git a/ocdskit/cli/commands/upgrade.py b/ocdskit/cli/commands/upgrade.py new file mode 100644 index 00000000..eb68a64e --- /dev/null +++ b/ocdskit/cli/commands/upgrade.py @@ -0,0 +1,30 @@ +from ocdskit import upgrade +from ocdskit.cli.commands.base import BaseCommand +from ocdskit.exceptions import CommandError + + +class Command(BaseCommand): + name = 'upgrade' + help = 'upgrades packages and releases from an old version of OCDS to a new version' + + def add_arguments(self): + self.add_argument('versions', help='the colon-separated old and new versions') + + def handle(self): + versions = self.args.versions + + version_from, version_to = versions.split(':') + if version_from < version_to: + direction = 'up' + else: + direction = 'down' + + try: + upgrade_method = getattr(upgrade, 'upgrade_{}'.format(versions.replace('.', '').replace(':', '_'))) + except AttributeError: + raise CommandError('{}grade from {} is not supported'.format(direction, versions.replace(':', ' to '))) + + for line in self.buffer(): + data = self.json_loads(line) + upgrade_method(data) + self.print(data) diff --git a/ocdskit/upgrade.py b/ocdskit/upgrade.py new file mode 100644 index 00000000..9910e5ed --- /dev/null +++ b/ocdskit/upgrade.py @@ -0,0 +1,211 @@ +import json +import logging +from collections import OrderedDict +from copy import deepcopy +from hashlib import md5 + +logger = logging.getLogger('ocdskit') + +# See http://standard.open-contracting.org/1.0/en/schema/reference/#identifier +organization_identification_1_0 = ( + (None, ('name',)), + ('identifier', ('scheme', 'id', 'legalName', 'uri')), + ('address', ('streetAddress', 'locality', 'region', 'postalCode', 'countryName')), + ('contactPoint', ('name', 'email', 'telephone', 'faxNumber', 'url')), +) + + +def _move_to_top(data, fields): + for field in reversed(fields): + if field in data: + data.move_to_end(field, last=False) + + +def upgrade_10_10(data): + pass + + +def upgrade_11_11(data): + pass + + +def upgrade_10_11(data): + """ + Upgrades a record package, release package or release from 1.0 to 1.1. + + Retains the deprecated Amendment.changes, Budget.source and Milestone.documents fields. + + Note: Versioned releases within a record package are not upgraded. + """ + if 'records' in data or 'releases' in data: # package + data['version'] = '1.1' + _move_to_top(data, ('uri', 'version')) + + if 'records' in data: # record package + for record in data['records']: + if 'releases' in record: + for release in record['releases']: + upgrade_release_10_11(release) + if 'compiledRelease' in record: + upgrade_release_10_11(record['compiledRelease']) + elif 'releases' in data: # release package + for release in data['releases']: + upgrade_release_10_11(release) + else: # release + upgrade_release_10_11(data) + + +def upgrade_release_10_11(release): + """ + Applies upgrades for organization handling, amendment handling and transactions terminology. + """ + upgrade_parties_10_to_11(release) + upgrade_amendments_10_11(release) + upgrade_transactions_10_11(release) + + +def upgrade_parties_10_to_11(release): + """ + Converts organizations to organization references and fills in the ``parties`` array. + """ + parties = _get_parties(release) + + if 'buyer' in release: + release['buyer'] = _add_party(parties, release['buyer'], 'buyer') + + if 'tender' in release: + if 'procuringEntity' in release['tender']: + release['tender']['procuringEntity'] = _add_party(parties, release['tender']['procuringEntity'], 'procuringEntity') # noqa: E501 + if 'tenderers' in release['tender']: + for i, tenderer in enumerate(release['tender']['tenderers']): + release['tender']['tenderers'][i] = _add_party(parties, tenderer, 'tenderer') + + if 'awards' in release: + for award in release['awards']: + if 'suppliers' in award: + for i, supplier in enumerate(award['suppliers']): + award['suppliers'][i] = _add_party(parties, supplier, 'supplier') + + if parties: + if 'parties' not in release: + release['parties'] = [] + _move_to_top(release, ('ocid', 'id', 'date', 'tag', 'initiationType', 'parties')) + + for party in parties.values(): + if party not in release['parties']: + release['parties'].append(party) + + +def _get_parties(release): + parties = OrderedDict() + + if 'parties' in release: + for party in release['parties']: + parties[party['id']] = party + + return parties + + +def _add_party(parties, party, role): + """ + Adds an ``id`` to the party, adds the party to the ``parties`` array, and returns an OrganizationReference. + """ + party = deepcopy(party) + + if 'id' not in party: + parts = [] + for parent, fields in organization_identification_1_0: + if not parent: + for field in fields: + parts.append(_get_bytes(party, field)) + elif parent in party: + for field in fields: + parts.append(_get_bytes(party[parent], field)) + + party['id'] = md5(b'-'.join(parts)).hexdigest() + _move_to_top(party, ('id')) + + _id = party['id'] + + if _id not in parties: + parties[_id] = party + else: + # Warn about information loss. + other = deepcopy(parties[_id]) + roles = other.pop('roles') + if dict(party) != dict(other): + logger.warning('party differs in "{}" role than in "{}" roles:\n{}\n{}'.format( + role, ', '.join(roles), json.dumps(party), json.dumps(other))) + + if 'roles' not in parties[_id]: + parties[_id]['roles'] = [] + _move_to_top(parties[_id], ('id', 'roles')) + + if role not in parties[_id]['roles']: + # Update the `roles` of the party in the `parties` array. + parties[_id]['roles'].append(role) + + # Create the OrganizationReference. + organization_reference = OrderedDict([ + ('id', _id), + ]) + if 'name' in party: + organization_reference['name'] = party['name'] + + return organization_reference + + +def _get_bytes(obj, field): + return bytes(obj.get(field) or '', 'utf-8') + + +def upgrade_amendments_10_11(release): + """ + Renames ``amendment`` to ``amendments`` under ``tender``, ``awards`` and ``contracts``. If ``amendments`` already + exists, it appends the ``amendment`` value to the ``amendments`` array, unless it already contains it. + """ + if 'tender' in release: + _upgrade_amendment_10_11(release['tender']) + for field in ('awards', 'contracts'): + if field in release: + for block in release[field]: + _upgrade_amendment_10_11(block) + + +def _upgrade_amendment_10_11(block): + if 'amendment' in block: + if 'amendments' not in block: + block['amendments'] = [] + if block['amendment'] not in block['amendments']: + block['amendments'].append(block['amendment']) + del block['amendment'] + + +def upgrade_transactions_10_11(release): + """ + Renames ``providerOrganization`` to ``payer``, ``receiverOrganization`` to ``payee``, and ``amount`` to ``value`` + under ``contracts.implementation.transactions``, unless they already exist. + + Converts ``providerOrganization`` and ``receiverOrganization`` from an Identifier to an OrganizationReference and + fills in the ``parties`` array. + """ + parties = _get_parties(release) + + if 'contracts' in release: + for contract in release['contracts']: + if 'implementation' in contract and 'transactions' in contract['implementation']: + for transaction in contract['implementation']['transactions']: + if 'value' not in transaction: + transaction['value'] = transaction['amount'] + del transaction['amount'] + + for old, new in (('providerOrganization', 'payer'), ('receiverOrganization', 'payee')): + if old in transaction and new not in transaction: + party = OrderedDict([ + ('identifier', transaction[old]), + ]) + if 'legalName' in transaction[old]: + party['name'] = transaction[old]['legalName'] + + transaction[new] = _add_party(parties, party, new) + del transaction[old] diff --git a/tests/commands/test_compile.py b/tests/commands/test_compile.py index 3e0ab1fa..a6891cf6 100644 --- a/tests/commands/test_compile.py +++ b/tests/commands/test_compile.py @@ -31,7 +31,7 @@ def test_command_versioned(monkeypatch): assert actual.getvalue() == read('realdata/versioned-release-1.json') + read('realdata/versioned-release-2.json') -def test_command_package(monkeypatch, caplog): +def test_command_package(monkeypatch): stdin = read('realdata/release-package-1.json', 'rb') + read('realdata/release-package-2.json', 'rb') with patch('sys.stdin', TextIOWrapper(BytesIO(stdin))), patch('sys.stdout', new_callable=StringIO) as actual: diff --git a/tests/commands/test_upgrade.py b/tests/commands/test_upgrade.py new file mode 100644 index 00000000..2f1d1a66 --- /dev/null +++ b/tests/commands/test_upgrade.py @@ -0,0 +1,81 @@ +import sys +from io import StringIO, TextIOWrapper, BytesIO +from unittest.mock import patch + +import pytest + +from ocdskit.cli.__main__ import main +from tests import read + + +def test_command_record_package(monkeypatch): + stdin = read('realdata/record-package_1.0.json', 'rb') + + with patch('sys.stdin', TextIOWrapper(BytesIO(stdin))), patch('sys.stdout', new_callable=StringIO) as actual: + monkeypatch.setattr(sys, 'argv', ['ocdskit', 'upgrade', '1.0:1.1']) + main() + + assert actual.getvalue() == read('realdata/record-package_1.1.json') + + +def test_command_release_package_buyer_procuring_entity_suppliers(monkeypatch): + stdin = read('realdata/release-package_1.0-1.json', 'rb') + + with patch('sys.stdin', TextIOWrapper(BytesIO(stdin))), patch('sys.stdout', new_callable=StringIO) as actual: + monkeypatch.setattr(sys, 'argv', ['ocdskit', 'upgrade', '1.0:1.1']) + main() + + assert actual.getvalue() == read('realdata/release-package_1.1-1.json') + + +def test_command_release_package_transactions(monkeypatch): + stdin = read('realdata/release-package_1.0-2.json', 'rb') + + with patch('sys.stdin', TextIOWrapper(BytesIO(stdin))), patch('sys.stdout', new_callable=StringIO) as actual: + monkeypatch.setattr(sys, 'argv', ['ocdskit', 'upgrade', '1.0:1.1']) + main() + + assert actual.getvalue() == read('realdata/release-package_1.1-2.json') + + +def test_command_release_tenderers_amendment(monkeypatch, caplog): + stdin = read('release_1.0.json', 'rb') + + with patch('sys.stdin', TextIOWrapper(BytesIO(stdin))), patch('sys.stdout', new_callable=StringIO) as actual: + monkeypatch.setattr(sys, 'argv', ['ocdskit', 'upgrade', '1.0:1.1']) + main() + + assert actual.getvalue() == read('release_1.1.json') + + assert len(caplog.records) == 1 + assert caplog.records[0].levelname == 'WARNING' + assert caplog.records[0].message == 'party differs in "supplier" role than in "tenderer" roles:\n' \ + '{"name": "Acme Inc.", "additionalIdentifiers": [{"id": 1}], "id": "6760c32d3e2e5499d51a709f563ed39a"}\n' \ + '{"id": "6760c32d3e2e5499d51a709f563ed39a", "name": "Acme Inc."}' + + +def test_command_identity(monkeypatch): + stdin = b'{}' + + for versions in ('1.0:1.0', '1.1:1.1'): + with patch('sys.stdin', TextIOWrapper(BytesIO(stdin))), patch('sys.stdout', new_callable=StringIO) as actual: + monkeypatch.setattr(sys, 'argv', ['ocdskit', 'upgrade', versions]) + main() + + assert actual.getvalue() == '{}\n' + + +def test_command_downgrade(monkeypatch, caplog): + stdin = b'{}' + + with pytest.raises(SystemExit) as excinfo: + with patch('sys.stdin', TextIOWrapper(BytesIO(stdin))), patch('sys.stdout', new_callable=StringIO) as actual: + monkeypatch.setattr(sys, 'argv', ['ocdskit', 'upgrade', '1.1:1.0']) + main() + + assert actual.getvalue() == '' + + assert len(caplog.records) == 1 + assert caplog.records[0].levelname == 'CRITICAL' + assert caplog.records[0].message == 'downgrade from 1.1 to 1.0 is not supported' + assert excinfo.value.code == 1 diff --git a/tests/fixtures/realdata/record-package_1.0.json b/tests/fixtures/realdata/record-package_1.0.json new file mode 100644 index 00000000..54d0879d --- /dev/null +++ b/tests/fixtures/realdata/record-package_1.0.json @@ -0,0 +1 @@ +{"uri":"https://www.contrataciones.gov.py/datos/record-package/193399.json","publishedDate":"2018-12-17T17:31:43Z","records":[{"ocid":"ocds-03ad3f-193399","releases":[{"date":"2013-03-05T08:24:59Z","tag":["planning"],"url":"https://www.contrataciones.gov.py/datos/id/planning/193399-adquisicion-scanner.json"}],"compiledRelease":{"language":"es","ocid":"ocds-03ad3f-193399","id":"193399-adquisicion-scanner","date":"2018-12-17T17:31:43Z","tag":["compiled"],"initiationType":"tender","tender":{"id":"193399-adquisicion-scanner","title":"Adquisición de Scanner","status":"complete","value":{"currency":"PYG","amount":null},"procuringEntity":{"name":"Dirección Nacional de Contrataciones Públicas (DNCP)","contactPoint":{"name":"Abog. Cynthia Leite de Lezcano","email":"uoc@contrataciones.gov.py","telephone":"415-4000"}},"tenderPeriod":{"endDate":null,"startDate":"2010-03-15T09:00:00Z"},"submissionMethod":["electronicAuction"],"url":"https://www.contrataciones.gov.py/datos/id/convocatorias/193399-adquisicion-scanner"},"buyer":{"name":"Dirección Nacional de Contrataciones Públicas (DNCP)","contactPoint":{"name":"Abog. Cynthia Leite de Lezcano","email":"uoc@contrataciones.gov.py","telephone":"415-4000"}},"awards":[{"id":"193399-adquisicion-scanner","title":"Adquisición de Scanner","status":"active","date":"2010-04-23T12:25:10Z","value":{"amount":135630400,"currency":"PYG"},"suppliers":[{"name":"MASTER SOFT SRL","identifier":{"id":"80007525-0","legalName":"MASTER SOFT SRL","scheme":"Registro Único de Contribuyente emitido por el Ministerio de Hacienda"},"address":{"streetAddress":"CHOFERES DEL CHACO Nº1956","postalCode":"","locality":"ASUNCION","countryName":"Paraguay","region":"Asunción"},"contactPoint":{"name":"LUIS MARIA OREGGIONI, GISELA SELMA WEIBERLEN DE OREGGIONI","faxNumber":"","telephone":"662831","email":"mastersoft@tigo.com.py","url":null}}],"url":"https://www.contrataciones.gov.py/datos/id/adjudicaciones/193399-adquisicion-scanner"}]}}]} diff --git a/tests/fixtures/realdata/record-package_1.1.json b/tests/fixtures/realdata/record-package_1.1.json new file mode 100644 index 00000000..47b3ce1c --- /dev/null +++ b/tests/fixtures/realdata/record-package_1.1.json @@ -0,0 +1 @@ +{"uri":"https://www.contrataciones.gov.py/datos/record-package/193399.json","version":"1.1","publishedDate":"2018-12-17T17:31:43Z","records":[{"ocid":"ocds-03ad3f-193399","releases":[{"date":"2013-03-05T08:24:59Z","tag":["planning"],"url":"https://www.contrataciones.gov.py/datos/id/planning/193399-adquisicion-scanner.json"}],"compiledRelease":{"ocid":"ocds-03ad3f-193399","id":"193399-adquisicion-scanner","date":"2018-12-17T17:31:43Z","tag":["compiled"],"initiationType":"tender","parties":[{"id":"a02fc5eae413fa29686db8eff8b439b6","roles":["buyer","procuringEntity"],"name":"Dirección Nacional de Contrataciones Públicas (DNCP)","contactPoint":{"name":"Abog. Cynthia Leite de Lezcano","email":"uoc@contrataciones.gov.py","telephone":"415-4000"}},{"id":"d20b2b5c7a36a5350d9304793414b85c","roles":["supplier"],"name":"MASTER SOFT SRL","identifier":{"id":"80007525-0","legalName":"MASTER SOFT SRL","scheme":"Registro Único de Contribuyente emitido por el Ministerio de Hacienda"},"address":{"streetAddress":"CHOFERES DEL CHACO Nº1956","postalCode":"","locality":"ASUNCION","countryName":"Paraguay","region":"Asunción"},"contactPoint":{"name":"LUIS MARIA OREGGIONI, GISELA SELMA WEIBERLEN DE OREGGIONI","faxNumber":"","telephone":"662831","email":"mastersoft@tigo.com.py","url":null}}],"language":"es","tender":{"id":"193399-adquisicion-scanner","title":"Adquisición de Scanner","status":"complete","value":{"currency":"PYG","amount":null},"procuringEntity":{"id":"a02fc5eae413fa29686db8eff8b439b6","name":"Dirección Nacional de Contrataciones Públicas (DNCP)"},"tenderPeriod":{"endDate":null,"startDate":"2010-03-15T09:00:00Z"},"submissionMethod":["electronicAuction"],"url":"https://www.contrataciones.gov.py/datos/id/convocatorias/193399-adquisicion-scanner"},"buyer":{"id":"a02fc5eae413fa29686db8eff8b439b6","name":"Dirección Nacional de Contrataciones Públicas (DNCP)"},"awards":[{"id":"193399-adquisicion-scanner","title":"Adquisición de Scanner","status":"active","date":"2010-04-23T12:25:10Z","value":{"amount":135630400,"currency":"PYG"},"suppliers":[{"id":"d20b2b5c7a36a5350d9304793414b85c","name":"MASTER SOFT SRL"}],"url":"https://www.contrataciones.gov.py/datos/id/adjudicaciones/193399-adquisicion-scanner"}]}}]} diff --git a/tests/fixtures/realdata/release-package_1.0-1.json b/tests/fixtures/realdata/release-package_1.0-1.json new file mode 100644 index 00000000..b0268dd6 --- /dev/null +++ b/tests/fixtures/realdata/release-package_1.0-1.json @@ -0,0 +1 @@ +{"uri":"https://www.contrataciones.gov.py/datos/record-package/193399.json","publishedDate":"2018-12-17T17:31:43Z","releases":[{"language":"es","ocid":"ocds-03ad3f-193399","id":"193399-adquisicion-scanner","date":"2018-12-17T17:31:43Z","tag":["compiled"],"initiationType":"tender","tender":{"id":"193399-adquisicion-scanner","title":"Adquisición de Scanner","status":"complete","value":{"currency":"PYG","amount":null},"procuringEntity":{"name":"Dirección Nacional de Contrataciones Públicas (DNCP)","contactPoint":{"name":"Abog. Cynthia Leite de Lezcano","email":"uoc@contrataciones.gov.py","telephone":"415-4000"}},"tenderPeriod":{"endDate":null,"startDate":"2010-03-15T09:00:00Z"},"submissionMethod":["electronicAuction"],"url":"https://www.contrataciones.gov.py/datos/id/convocatorias/193399-adquisicion-scanner"},"buyer":{"name":"Dirección Nacional de Contrataciones Públicas (DNCP)","contactPoint":{"name":"Abog. Cynthia Leite de Lezcano","email":"uoc@contrataciones.gov.py","telephone":"415-4000"}},"awards":[{"id":"193399-adquisicion-scanner","title":"Adquisición de Scanner","status":"active","date":"2010-04-23T12:25:10Z","value":{"amount":135630400,"currency":"PYG"},"suppliers":[{"name":"MASTER SOFT SRL","identifier":{"id":"80007525-0","legalName":"MASTER SOFT SRL","scheme":"Registro Único de Contribuyente emitido por el Ministerio de Hacienda"},"address":{"streetAddress":"CHOFERES DEL CHACO Nº1956","postalCode":"","locality":"ASUNCION","countryName":"Paraguay","region":"Asunción"},"contactPoint":{"name":"LUIS MARIA OREGGIONI, GISELA SELMA WEIBERLEN DE OREGGIONI","faxNumber":"","telephone":"662831","email":"mastersoft@tigo.com.py","url":null}}],"url":"https://www.contrataciones.gov.py/datos/id/adjudicaciones/193399-adquisicion-scanner"}]}]} diff --git a/tests/fixtures/realdata/release-package_1.0-2.json b/tests/fixtures/realdata/release-package_1.0-2.json new file mode 100644 index 00000000..4e1d2478 --- /dev/null +++ b/tests/fixtures/realdata/release-package_1.0-2.json @@ -0,0 +1 @@ +{"uri":"https://contrataciones.gov.py/datos/id/contratos/246807-11-setiembre-srl-4","publishedDate":"2018-12-18T13:20:42Z","publisher":{"name":"DNCP - Paraguay","legalName":"Dirección Nacional de Contrataciones Públicas, Paraguay","uri":"https://contrataciones.gov.py/datos"},"license":"https://creativecommons.org/licenses/by/4.0/","publicationPolicy":"https://www.contrataciones.gov.py/datos/legal","releases":[{"language":"es","ocid":"ocds-03ad3f-246807","id":"246807-11-setiembre-srl-4-contract","date":"2018-12-18T13:20:42Z","tag":["contract"],"initiationType":"tender","contracts":[{"implementation":{"transactions":[{"id":"92885","amount":{"amount":2372509,"currency":"PYG"},"date":"2012-05-31T00:00:00Z","providerOrganization":{"id":"PY-PGN-12-8-1000000","legalName":"1000000-DIRECCION GENERAL DE ADMINISTRACION Y FINANZAS","scheme":"PY-PGN"},"receiverOrganization":{"id":"PY-RUC-80017437-2","legalName":"NUCLEO S.A.","scheme":"PY-PGN"}}]},"id":"246807-11-setiembre-srl-4","awardID":"246807-adqusicion-guantes-otros-insumos-medicos","dncpContractCode":"CD-13003-13-70684","title":"ADQUSICION DE GUANTES Y OTROS INSUMOS MEDICOS ","status":"active","value":{"amount":17875000,"currency":"PYG"},"items":[{"description":"Placa Radiografica Medica","id":"M99UFYWPOag%3D","classification":{"schema":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","id":"42201810-001","description":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","uri":"https://www.contrataciones.gov.py/datos/api/v2/doc/catalogo/nivel-5/42201810-001"},"quantity":10,"unit":{"name":"Unidad","value":{"amount":445000,"currency":"PYG"}}},{"description":"Placa Radiografica Medica","id":"7SmiEGR9Oes%3D","classification":{"schema":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","id":"42201810-001","description":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","uri":"https://www.contrataciones.gov.py/datos/api/v2/doc/catalogo/nivel-5/42201810-001"},"quantity":10,"unit":{"name":"Unidad","value":{"amount":850000,"currency":"PYG"}}},{"description":"Liquido fijador para placas radiograficas","id":"hPQ2FURf7RQ%3D","classification":{"schema":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","id":"42203708-002","description":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","uri":"https://www.contrataciones.gov.py/datos/api/v2/doc/catalogo/nivel-5/42203708-002"},"quantity":20,"unit":{"name":"Unidad","value":{"amount":110000,"currency":"PYG"}}},{"description":"Liquido revelador para placas radiograficas","id":"3LykMkkFXqU%3D","classification":{"schema":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","id":"42203704-001","description":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","uri":"https://www.contrataciones.gov.py/datos/api/v2/doc/catalogo/nivel-5/42203704-001"},"quantity":20,"unit":{"name":"Unidad","value":{"amount":110000,"currency":"PYG"}}},{"description":"Pinza para laboratorio","id":"2iT5c5pHD9g%3D","classification":{"schema":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","id":"41122404-001","description":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","uri":"https://www.contrataciones.gov.py/datos/api/v2/doc/catalogo/nivel-5/41122404-001"},"quantity":5,"unit":{"name":"Unidad","value":{"amount":105000,"currency":"PYG"}}}],"dateSigned":"2013-04-05T12:00:00Z","suppliers":[{"name":"11 DE SETIEMBRE SRL"}],"period":{"endDate":"2013-04-15T12:00:00Z","startDate":"2013-04-05T12:00:00Z"},"url":"https://www.contrataciones.gov.py/datos/id/contratos/246807-11-setiembre-srl-4"}]}]} diff --git a/tests/fixtures/realdata/release-package_1.1-1.json b/tests/fixtures/realdata/release-package_1.1-1.json new file mode 100644 index 00000000..4f510487 --- /dev/null +++ b/tests/fixtures/realdata/release-package_1.1-1.json @@ -0,0 +1 @@ +{"uri":"https://www.contrataciones.gov.py/datos/record-package/193399.json","version":"1.1","publishedDate":"2018-12-17T17:31:43Z","releases":[{"ocid":"ocds-03ad3f-193399","id":"193399-adquisicion-scanner","date":"2018-12-17T17:31:43Z","tag":["compiled"],"initiationType":"tender","parties":[{"id":"a02fc5eae413fa29686db8eff8b439b6","roles":["buyer","procuringEntity"],"name":"Dirección Nacional de Contrataciones Públicas (DNCP)","contactPoint":{"name":"Abog. Cynthia Leite de Lezcano","email":"uoc@contrataciones.gov.py","telephone":"415-4000"}},{"id":"d20b2b5c7a36a5350d9304793414b85c","roles":["supplier"],"name":"MASTER SOFT SRL","identifier":{"id":"80007525-0","legalName":"MASTER SOFT SRL","scheme":"Registro Único de Contribuyente emitido por el Ministerio de Hacienda"},"address":{"streetAddress":"CHOFERES DEL CHACO Nº1956","postalCode":"","locality":"ASUNCION","countryName":"Paraguay","region":"Asunción"},"contactPoint":{"name":"LUIS MARIA OREGGIONI, GISELA SELMA WEIBERLEN DE OREGGIONI","faxNumber":"","telephone":"662831","email":"mastersoft@tigo.com.py","url":null}}],"language":"es","tender":{"id":"193399-adquisicion-scanner","title":"Adquisición de Scanner","status":"complete","value":{"currency":"PYG","amount":null},"procuringEntity":{"id":"a02fc5eae413fa29686db8eff8b439b6","name":"Dirección Nacional de Contrataciones Públicas (DNCP)"},"tenderPeriod":{"endDate":null,"startDate":"2010-03-15T09:00:00Z"},"submissionMethod":["electronicAuction"],"url":"https://www.contrataciones.gov.py/datos/id/convocatorias/193399-adquisicion-scanner"},"buyer":{"id":"a02fc5eae413fa29686db8eff8b439b6","name":"Dirección Nacional de Contrataciones Públicas (DNCP)"},"awards":[{"id":"193399-adquisicion-scanner","title":"Adquisición de Scanner","status":"active","date":"2010-04-23T12:25:10Z","value":{"amount":135630400,"currency":"PYG"},"suppliers":[{"id":"d20b2b5c7a36a5350d9304793414b85c","name":"MASTER SOFT SRL"}],"url":"https://www.contrataciones.gov.py/datos/id/adjudicaciones/193399-adquisicion-scanner"}]}]} diff --git a/tests/fixtures/realdata/release-package_1.1-2.json b/tests/fixtures/realdata/release-package_1.1-2.json new file mode 100644 index 00000000..c875b0db --- /dev/null +++ b/tests/fixtures/realdata/release-package_1.1-2.json @@ -0,0 +1 @@ +{"uri":"https://contrataciones.gov.py/datos/id/contratos/246807-11-setiembre-srl-4","version":"1.1","publishedDate":"2018-12-18T13:20:42Z","publisher":{"name":"DNCP - Paraguay","legalName":"Dirección Nacional de Contrataciones Públicas, Paraguay","uri":"https://contrataciones.gov.py/datos"},"license":"https://creativecommons.org/licenses/by/4.0/","publicationPolicy":"https://www.contrataciones.gov.py/datos/legal","releases":[{"language":"es","ocid":"ocds-03ad3f-246807","id":"246807-11-setiembre-srl-4-contract","date":"2018-12-18T13:20:42Z","tag":["contract"],"initiationType":"tender","contracts":[{"implementation":{"transactions":[{"id":"92885","date":"2012-05-31T00:00:00Z","value":{"amount":2372509,"currency":"PYG"},"payer":{"id":"b57cc866981fddc03d7928d923463fb1","name":"1000000-DIRECCION GENERAL DE ADMINISTRACION Y FINANZAS"},"payee":{"id":"745a4d642e0e904e935a8fbd93b5366e","name":"NUCLEO S.A."}}]},"id":"246807-11-setiembre-srl-4","awardID":"246807-adqusicion-guantes-otros-insumos-medicos","dncpContractCode":"CD-13003-13-70684","title":"ADQUSICION DE GUANTES Y OTROS INSUMOS MEDICOS ","status":"active","value":{"amount":17875000,"currency":"PYG"},"items":[{"description":"Placa Radiografica Medica","id":"M99UFYWPOag%3D","classification":{"schema":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","id":"42201810-001","description":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","uri":"https://www.contrataciones.gov.py/datos/api/v2/doc/catalogo/nivel-5/42201810-001"},"quantity":10,"unit":{"name":"Unidad","value":{"amount":445000,"currency":"PYG"}}},{"description":"Placa Radiografica Medica","id":"7SmiEGR9Oes%3D","classification":{"schema":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","id":"42201810-001","description":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","uri":"https://www.contrataciones.gov.py/datos/api/v2/doc/catalogo/nivel-5/42201810-001"},"quantity":10,"unit":{"name":"Unidad","value":{"amount":850000,"currency":"PYG"}}},{"description":"Liquido fijador para placas radiograficas","id":"hPQ2FURf7RQ%3D","classification":{"schema":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","id":"42203708-002","description":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","uri":"https://www.contrataciones.gov.py/datos/api/v2/doc/catalogo/nivel-5/42203708-002"},"quantity":20,"unit":{"name":"Unidad","value":{"amount":110000,"currency":"PYG"}}},{"description":"Liquido revelador para placas radiograficas","id":"3LykMkkFXqU%3D","classification":{"schema":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","id":"42203704-001","description":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","uri":"https://www.contrataciones.gov.py/datos/api/v2/doc/catalogo/nivel-5/42203704-001"},"quantity":20,"unit":{"name":"Unidad","value":{"amount":110000,"currency":"PYG"}}},{"description":"Pinza para laboratorio","id":"2iT5c5pHD9g%3D","classification":{"schema":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","id":"41122404-001","description":"Catálogo de productos, bienes y servicios de la Dirección Nacional de Contrataciones","uri":"https://www.contrataciones.gov.py/datos/api/v2/doc/catalogo/nivel-5/41122404-001"},"quantity":5,"unit":{"name":"Unidad","value":{"amount":105000,"currency":"PYG"}}}],"dateSigned":"2013-04-05T12:00:00Z","suppliers":[{"name":"11 DE SETIEMBRE SRL"}],"period":{"endDate":"2013-04-15T12:00:00Z","startDate":"2013-04-05T12:00:00Z"},"url":"https://www.contrataciones.gov.py/datos/id/contratos/246807-11-setiembre-srl-4"}]}]} diff --git a/tests/fixtures/release_1.0.json b/tests/fixtures/release_1.0.json new file mode 100644 index 00000000..2988040b --- /dev/null +++ b/tests/fixtures/release_1.0.json @@ -0,0 +1 @@ +{"ocid":"ocds-213czf-1","id":"1","date":"2001-02-03T04:05:06Z","tag":["planning"],"initiationType":"tender","tender":{"id":1,"tenderers":[{"name":"Acme Inc."}],"amendment":{"date":"2001-10-10T00:00:00Z"}},"awards":[{"id":1,"suppliers":[{"name":"Acme Inc.","additionalIdentifiers":[{"id":1}]}]}]} diff --git a/tests/fixtures/release_1.1.json b/tests/fixtures/release_1.1.json new file mode 100644 index 00000000..154fcda9 --- /dev/null +++ b/tests/fixtures/release_1.1.json @@ -0,0 +1 @@ +{"ocid":"ocds-213czf-1","id":"1","date":"2001-02-03T04:05:06Z","tag":["planning"],"initiationType":"tender","parties":[{"id":"6760c32d3e2e5499d51a709f563ed39a","roles":["tenderer","supplier"],"name":"Acme Inc."}],"tender":{"id":1,"tenderers":[{"id":"6760c32d3e2e5499d51a709f563ed39a","name":"Acme Inc."}],"amendments":[{"date":"2001-10-10T00:00:00Z"}]},"awards":[{"id":1,"suppliers":[{"id":"6760c32d3e2e5499d51a709f563ed39a","name":"Acme Inc."}]}]}