def test_migrate_from1to2(self): set_db_schema_version(self.db, 1) with open(os.path.join(os.path.dirname(__file__), 'data/tender-contract-complete.json'), 'r') as df: data = json.loads(df.read()) t = Tender(data) t.store(self.db) tender = self.db.get(t.id) self.assertEqual(tender['awards'][0]['value'], data['awards'][0]['value']) self.assertEqual(tender['awards'][0]['suppliers'], data['awards'][0]['suppliers']) contract_data = deepcopy(tender['contracts'][0]) del contract_data['value'] del contract_data['suppliers'] contract_data['tender_id'] = tender['_id'] contract_data['tender_token'] = 'xxx' contract_data['procuringEntity'] = tender['procuringEntity'] contract = Contract(contract_data) contract.store(self.db) contract_data = self.db.get(contract.id) self.assertNotIn("value", contract_data) self.assertNotIn("suppliers", contract_data) migrate_data(self.app.app.registry, 2) migrated_item = self.db.get(contract.id) self.assertIn("value", migrated_item) self.assertEqual(migrated_item['value'], tender['awards'][0]['value']) self.assertIn("suppliers", migrated_item) self.assertEqual(migrated_item['suppliers'], tender['awards'][0]['suppliers'])
def test_migrate_from22to23(self): set_db_schema_version(self.db, 22) u = Tender(test_tender_data) u.tenderID = "UA-X" u.store(self.db) data = self.db.get(u.id) data["documents"] = [{ "id": "ebcb5dd7f7384b0fbfbed2dc4252fa6e", "title": "name.txt", "documentOf": "tender", "url": "/tenders/{}/documents/ebcb5dd7f7384b0fbfbed2dc4252fa6e?download=10367238a2964ee18513f209d9b6d1d3" .format(u.id), "datePublished": "2016-06-01T00:00:00+03:00", "dateModified": "2016-06-01T00:00:00+03:00", "format": "text/plain", }] _id, _rev = self.db.save(data) self.app.app.registry.docservice_url = 'http://localhost' migrate_data(self.app.app.registry, 23) migrated_item = self.db.get(u.id) self.assertIn('http://localhost/get/10367238a2964ee18513f209d9b6d1d3?', migrated_item['documents'][0]['url']) self.assertIn( 'Prefix={}%2Febcb5dd7f7384b0fbfbed2dc4252fa6e'.format(u.id), migrated_item['documents'][0]['url']) self.assertIn('KeyID=', migrated_item['documents'][0]['url']) self.assertIn('Signature=', migrated_item['documents'][0]['url'])
def factory(request): request.validated['tender_src'] = {} root = Root(request) if not request.matchdict or not request.matchdict.get('tender_id'): return root request.validated['tender_id'] = request.matchdict['tender_id'] tender = Tender.load(root.db, request.matchdict['tender_id']) if not tender: request.errors.add('url', 'tender_id', 'Not Found') request.errors.status = 404 raise error_handler(request.errors) tender.__parent__ = root request.validated['tender'] = tender request.validated['tender_status'] = tender.status if request.method != 'GET': request.validated['tender_src'] = tender.serialize('plain') if request.matchdict.get('award_id'): award = get_item(tender, 'award', request, root) if request.matchdict.get('complaint_id'): complaint = get_item(award, 'complaint', request, root) if request.matchdict.get('document_id'): return get_item(complaint, 'document', request, root) else: return complaint elif request.matchdict.get('document_id'): return get_item(award, 'document', request, root) else: return award elif request.matchdict.get('contract_id'): contract = get_item(tender, 'contract', request, root) if request.matchdict.get('document_id'): return get_item(contract, 'document', request, root) else: return contract elif request.matchdict.get('bid_id'): bid = get_item(tender, 'bid', request, root) if request.matchdict.get('document_id'): return get_item(bid, 'document', request, root) else: return bid elif request.matchdict.get('complaint_id'): complaint = get_item(tender, 'complaint', request, root) if request.matchdict.get('document_id'): return get_item(complaint, 'document', request, root) else: return complaint elif request.matchdict.get('cancellation_id'): cancellation = get_item(tender, 'cancellation', request, root) if request.matchdict.get('document_id'): return get_item(cancellation, 'document', request, root) else: return cancellation elif request.matchdict.get('document_id'): return get_item(tender, 'document', request, root) elif request.matchdict.get('question_id'): return get_item(tender, 'question', request, root) elif request.matchdict.get('lot_id'): return get_item(tender, 'lot', request, root) request.validated['id'] = request.matchdict['tender_id'] return tender
def validate_tender_exists(request, key='id'): tender = request.matchdict.get(key) and Tender.load(request.registry.db, request.matchdict[key]) if tender: request.validated[key] = request.matchdict[key] request.validated['tender'] = tender return tender else: request.errors.add('url', key, 'Not Found') request.errors.status = 404
def factory(request): request.validated['tender_src'] = {} root = Root(request) if not request.matchdict or not request.matchdict.get('tender_id'): return root request.validated['tender_id'] = request.matchdict['tender_id'] tender = Tender.load(root.db, request.matchdict['tender_id']) if not tender: request.errors.add('url', 'tender_id', 'Not Found') request.errors.status = 404 raise error_handler(request.errors) tender.__parent__ = root request.validated['tender'] = tender request.validated['tender_status'] = tender.status if request.method != 'GET': request.validated['tender_src'] = tender.serialize('plain') if request.matchdict.get('award_id'): award = get_item(tender, 'award', request, root) if request.matchdict.get('complaint_id'): complaint = get_item(award, 'complaint', request, root) if request.matchdict.get('document_id'): return get_item(complaint, 'document', request, root) else: return complaint elif request.matchdict.get('contract_id'): contract = get_item(award, 'contract', request, root) if request.matchdict.get('document_id'): return get_item(contract, 'document', request, root) else: return contract elif request.matchdict.get('document_id'): return get_item(award, 'document', request, root) else: return award elif request.matchdict.get('bid_id'): bid = get_item(tender, 'bid', request, root) if request.matchdict.get('document_id'): return get_item(bid, 'document', request, root) else: return bid elif request.matchdict.get('complaint_id'): complaint = get_item(tender, 'complaint', request, root) if request.matchdict.get('document_id'): return get_item(complaint, 'document', request, root) else: return complaint elif request.matchdict.get('document_id'): return get_item(tender, 'document', request, root) elif request.matchdict.get('question_id'): return get_item(tender, 'question', request, root) request.validated['id'] = request.matchdict['tender_id'] return tender
def test_simple_add_tender(self): u = Tender() u.tenderID = "UA-X" assert u.id is None assert u.rev is None u.store(self.db) assert u.id is not None assert u.rev is not None fromdb = self.db.get(u.id) assert u.tenderID == fromdb['tenderID'] assert u.doc_type == "Tender" u.delete_instance(self.db)
def test_simple_add_tender(self): u = Tender(test_tender_data) u.tenderID = "UA-X" assert u.id is None assert u.rev is None u.store(self.db) assert u.id is not None assert u.rev is not None fromdb = self.db.get(u.id) assert u.tenderID == fromdb['tenderID'] assert u.doc_type == "Tender" u.delete_instance(self.db)
def collection_get(self): """Tenders List Get Tenders List ---------------- Example request to get tenders list: .. sourcecode:: http GET /tenders HTTP/1.1 Host: example.com Accept: application/json This is what one should expect in response: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "data": [ { "id": "64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:06:58.158Z" } ] } """ # http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options params = {} fields = self.request.params.get('opt_fields', '') if fields: params['opt_fields'] = fields limit = self.request.params.get('limit', '') if limit: params['limit'] = limit limit = int(limit) if limit.isdigit() else 100 descending = self.request.params.get('descending') offset = self.request.params.get('offset', '9' if descending else '0') if descending: params['descending'] = descending next_offset = datetime.min.isoformat() if descending else get_now().isoformat() if fields: LOGGER.info('Used custom fields for tenders list: {}'.format(','.join(sorted(fields.split(','))))) fields = fields.split(',') + ['dateModified', 'id'] results = [ tender_serialize(i, fields) for i in Tender.view(self.db, 'tenders/by_dateModified', limit=limit + 1, startkey=offset, descending=bool(descending), include_docs=True) ] else: results = [ {'id': i.id, 'dateModified': i.key} for i in tenders_by_dateModified_view(self.db, limit=limit + 1, startkey=offset, descending=bool(descending)) ] if len(results) > limit: results, last = results[:-1], results[-1] params['offset'] = last['dateModified'] else: params['offset'] = next_offset return { 'data': results, 'next_page': { "offset": params['offset'], "path": self.request.route_path('collection_Tender', _query=params), "uri": self.request.route_url('collection_Tender', _query=params) } }
def collection_post(self): """This API request is targeted to creating new Tenders by procuring organizations. Creating new Tender ------------------- Example request to create tender: .. sourcecode:: http POST /tenders HTTP/1.1 Host: example.com Accept: application/json { "data": { "procuringEntity": { "id": { "name": "Державне управління справами", "scheme": "https://ns.openprocurement.org/ua/edrpou", "uid": "00037256", "uri": "http://www.dus.gov.ua/" }, "address": { "countryName": "Україна", "postalCode": "01220", "region": "м. Київ", "locality": "м. Київ", "streetAddress": "вул. Банкова, 11, корпус 1" } }, "value": { "amount": 500, "currency": "UAH", "valueAddedTaxIncluded": true }, "itemsToBeProcured": [ { "description": "футляри до державних нагород", "primaryClassification": { "scheme": "CPV", "id": "44617100-9", "description": "Cartons" }, "additionalClassification": [ { "scheme": "ДКПП", "id": "17.21.1", "description": "папір і картон гофровані, паперова й картонна тара" } ], "unitOfMeasure": "item", "quantity": 5 } ], "enquiryPeriod": { "endDate": "2014-10-31T00:00:00" }, "tenderPeriod": { "startDate": "2014-11-03T00:00:00", "endDate": "2014-11-06T10:00:00" }, "awardPeriod": { "endDate": "2014-11-13T00:00:00" }, "deliveryDate": { "endDate": "2014-11-20T00:00:00" }, "minimalStep": { "amount": 35, "currency": "UAH" } } } This is what one should expect in response: .. sourcecode:: http HTTP/1.1 201 Created Location: http://localhost/api/0.1/tenders/64e93250be76435397e8c992ed4214d1 Content-Type: application/json { "data": { "id": "64e93250be76435397e8c992ed4214d1", "tenderID": "UA-64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:06:58.158Z", "procuringEntity": { "id": { "name": "Державне управління справами", "scheme": "https://ns.openprocurement.org/ua/edrpou", "uid": "00037256", "uri": "http://www.dus.gov.ua/" }, "address": { "countryName": "Україна", "postalCode": "01220", "region": "м. Київ", "locality": "м. Київ", "streetAddress": "вул. Банкова, 11, корпус 1" } }, "value": { "amount": 500, "currency": "UAH", "valueAddedTaxIncluded": true }, "itemsToBeProcured": [ { "description": "футляри до державних нагород", "primaryClassification": { "scheme": "CPV", "id": "44617100-9", "description": "Cartons" }, "additionalClassification": [ { "scheme": "ДКПП", "id": "17.21.1", "description": "папір і картон гофровані, паперова й картонна тара" } ], "unitOfMeasure": "item", "quantity": 5 } ], "enquiryPeriod": { "endDate": "2014-10-31T00:00:00" }, "tenderPeriod": { "startDate": "2014-11-03T00:00:00", "endDate": "2014-11-06T10:00:00" }, "awardPeriod": { "endDate": "2014-11-13T00:00:00" }, "deliveryDate": { "endDate": "2014-11-20T00:00:00" }, "minimalStep": { "amount": 35, "currency": "UAH" } } } """ tender_data = self.request.validated['data'] tender_id = generate_id() tender = Tender(tender_data) tender.id = tender_id tender.enquiryPeriod.startDate = get_now() tender.tenderID = generate_tender_id(tender.enquiryPeriod.startDate, self.db) if not tender.tenderPeriod.startDate: tender.tenderPeriod.startDate = tender.enquiryPeriod.endDate set_ownership(tender, self.request) self.request.validated['tender'] = tender self.request.validated['tender_src'] = {} save_tender(self.request) self.request.response.status = 201 self.request.response.headers[ 'Location'] = self.request.route_url('Tender', tender_id=tender_id) return { 'data': tender.serialize(tender.status), 'access': { 'token': tender.owner_token } }
def test_migrate_from23to24(self): set_db_schema_version(self.db, 23) self.app.app.registry.docservice_url = 'http://localhost.ds' u = Tender(test_tender_data) u.tenderID = "UA-X" u.dateModified = get_now().isoformat() u.store(self.db) tender_raw = self.db.get(u.id) date_modified_before = tender_raw['dateModified'] bid = {"id": "1b4da15470e84c4d948a5d1660d29776"} bid["documents"] = [ { # non-ds url. should be fixed (correct id in url) by migrator "id": "1801ca2749bd40b0944e58adc3e09c46", "title": "name.txt", "documentOf": "tender", "url": "/tenders/{}/bids/{}/documents/63073ea0da17414db9f13e1a8c347e11?download=a65ef5c688884931aed1a472620d3a00" .format(u.id, bid['id']), "datePublished": "2016-06-01T00:00:00+03:00", "dateModified": "2016-06-01T00:00:00+03:00", "format": "text/plain", "language": "uk", }, { # non-ds url. should be fixed (correct id in url) by migrator "id": "f3e5470b76f84c66a89fd52ed871f645", "title": "name.txt", "documentOf": "tender", "url": "/tenders/{}/bids/{}/documents/512bd84155b145b99e0ac80894fe2b8f?download=d48723d7b4014599ac8d94fb0ac958b4" .format(u.id, bid['id']), "datePublished": "2016-06-01T00:00:00+03:00", "dateModified": "2016-06-01T00:00:00+03:00", "format": "text/plain", }, { # ds url. should NOT be rewrited by migrator "id": "352d774608d749c996fc0e798ffef433", "title": "name.txt", "documentOf": "tender", "url": "http://localhost.ds/get/b893bf5d2fb44a26bd6896178afe5953?KeyID=i_am_ds_url_lalalalala", # DS url "datePublished": "2016-06-01T00:00:00+03:00", "dateModified": "2016-06-01T00:00:00+03:00", "format": "text/plain", } ] # dirty eligibility documents container simulation bid["eligibilityDocuments"] = [{ # non-ds url. should be fixed (correct id in url) by migrator "id": "73e728784f924518b07f16e34750df1b", "title": "name.txt", "documentOf": "tender", "url": "/tenders/{}/bids/{}/eligibility_documents/a109ca6b04d24bd5bccf57917a65e835?download=5443af5e910f46debe412fc36e69f1ad" .format(u.id, bid['id']), "datePublished": "2016-06-01T00:00:00+03:00", "dateModified": "2016-06-01T00:00:00+03:00", "format": "text/plain", "language": "uk", "confidentiality": "buyerOnly" # documents for which url is not rewrited to ds url }] tender_raw['bids'] = [ bid, ] _id, _rev = self.db.save(tender_raw) migrate_data(self.app.app.registry, 24) migrated = self.db.get(u.id) migrated_bid = migrated['bids'][0] self.assertGreater(migrated['dateModified'], date_modified_before) # url should be corrected self.assertIn( "/tenders/{}/bids/1b4da15470e84c4d948a5d1660d29776/documents/1801ca2749bd40b0944e58adc3e09c46?download=a65ef5c688884931aed1a472620d3a00" .format(u.id), migrated_bid['documents'][0]['url']) self.assertIn( "/tenders/{}/bids/1b4da15470e84c4d948a5d1660d29776/documents/f3e5470b76f84c66a89fd52ed871f645?download=d48723d7b4014599ac8d94fb0ac958b4" .format(u.id), migrated_bid['documents'][1]['url']) self.assertIn( "/tenders/{}/bids/1b4da15470e84c4d948a5d1660d29776/eligibility_documents/73e728784f924518b07f16e34750df1b?download=5443af5e910f46debe412fc36e69f1ad" .format(u.id), migrated_bid['eligibilityDocuments'][0]['url']) # url remained the same as before migration self.assertIn( "http://localhost.ds/get/b893bf5d2fb44a26bd6896178afe5953?KeyID=i_am_ds_url_lalalalala", migrated_bid['documents'][2]['url'])
def collection_get(self): """Tenders List Get Tenders List ---------------- Example request to get tenders list: .. sourcecode:: http GET /tenders HTTP/1.1 Host: example.com Accept: application/json This is what one should expect in response: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "data": [ { "id": "64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:06:58.158Z" } ] } """ # http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options params = {} fields = self.request.params.get('opt_fields', '') if fields: params['opt_fields'] = fields limit = self.request.params.get('limit', '') if limit: params['limit'] = limit limit = int(limit) if limit.isdigit() else 100 descending = self.request.params.get('descending') offset = self.request.params.get('offset', '9' if descending else '0') if descending: params['descending'] = descending next_offset = datetime.min.isoformat() if descending else get_now( ).isoformat() results = Tender.view(self.db, 'tenders/by_dateModified', limit=limit + 1, startkey=offset, descending=bool(descending)) results = [tender_serialize(i, fields) for i in results] if len(results) > limit: results, last = results[:-1], results[-1] params['offset'] = last['dateModified'] else: params['offset'] = next_offset next_url = self.request.route_url('collection_Tender', _query=params) next_path = self.request.route_path('collection_Tender', _query=params) return { 'data': results, 'next_page': { "offset": params['offset'], "path": next_path, "uri": next_url } }
def collection_post(self): """This API request is targeted to creating new Tenders by procuring organizations. Creating new Tender ------------------- Example request to create tender: .. sourcecode:: http POST /tenders HTTP/1.1 Host: example.com Accept: application/json { "data": { "procuringEntity": { "id": { "name": "Державне управління справами", "scheme": "https://ns.openprocurement.org/ua/edrpou", "uid": "00037256", "uri": "http://www.dus.gov.ua/" }, "address": { "countryName": "Україна", "postalCode": "01220", "region": "м. Київ", "locality": "м. Київ", "streetAddress": "вул. Банкова, 11, корпус 1" } }, "value": { "amount": 500, "currency": "UAH", "valueAddedTaxIncluded": true }, "itemsToBeProcured": [ { "description": "футляри до державних нагород", "primaryClassification": { "scheme": "CPV", "id": "44617100-9", "description": "Cartons" }, "additionalClassification": [ { "scheme": "ДКПП", "id": "17.21.1", "description": "папір і картон гофровані, паперова й картонна тара" } ], "unitOfMeasure": "item", "quantity": 5 } ], "enquiryPeriod": { "endDate": "2014-10-31T00:00:00" }, "tenderPeriod": { "startDate": "2014-11-03T00:00:00", "endDate": "2014-11-06T10:00:00" }, "awardPeriod": { "endDate": "2014-11-13T00:00:00" }, "deliveryDate": { "endDate": "2014-11-20T00:00:00" }, "minimalStep": { "amount": 35, "currency": "UAH" } } } This is what one should expect in response: .. sourcecode:: http HTTP/1.1 201 Created Location: http://localhost/api/0.1/tenders/64e93250be76435397e8c992ed4214d1 Content-Type: application/json { "data": { "id": "64e93250be76435397e8c992ed4214d1", "tenderID": "UA-64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:06:58.158Z", "procuringEntity": { "id": { "name": "Державне управління справами", "scheme": "https://ns.openprocurement.org/ua/edrpou", "uid": "00037256", "uri": "http://www.dus.gov.ua/" }, "address": { "countryName": "Україна", "postalCode": "01220", "region": "м. Київ", "locality": "м. Київ", "streetAddress": "вул. Банкова, 11, корпус 1" } }, "value": { "amount": 500, "currency": "UAH", "valueAddedTaxIncluded": true }, "itemsToBeProcured": [ { "description": "футляри до державних нагород", "primaryClassification": { "scheme": "CPV", "id": "44617100-9", "description": "Cartons" }, "additionalClassification": [ { "scheme": "ДКПП", "id": "17.21.1", "description": "папір і картон гофровані, паперова й картонна тара" } ], "unitOfMeasure": "item", "quantity": 5 } ], "enquiryPeriod": { "endDate": "2014-10-31T00:00:00" }, "tenderPeriod": { "startDate": "2014-11-03T00:00:00", "endDate": "2014-11-06T10:00:00" }, "awardPeriod": { "endDate": "2014-11-13T00:00:00" }, "deliveryDate": { "endDate": "2014-11-20T00:00:00" }, "minimalStep": { "amount": 35, "currency": "UAH" } } } """ tender_data = filter_data(self.request.validated['data']) tender_id = generate_id() tender_data['doc_id'] = tender_id tender_data['tenderID'] = generate_tender_id(tender_id) tender = Tender(tender_data) if tender.enquiryPeriod: tender.enquiryPeriod.startDate = tender.dateModified else: tender.enquiryPeriod = {'startDate': tender.dateModified} save_tender(tender, None, self.request) self.request.response.status = 201 self.request.response.headers['Location'] = self.request.route_url( 'Tender', id=tender_id) return { 'data': tender.serialize(tender.status), 'access': { 'token': tender.owner_token } }
def collection_get(self): """Tenders List Get Tenders List ---------------- Example request to get tenders list: .. sourcecode:: http GET /tenders HTTP/1.1 Host: example.com Accept: application/json This is what one should expect in response: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "data": [ { "id": "64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:06:58.158Z" } ] } """ # http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options params = {} fields = self.request.params.get('opt_fields', '') if fields: params['opt_fields'] = fields limit = self.request.params.get('limit', '') if limit: params['limit'] = limit limit = int(limit) if limit.isdigit() else 100 descending = self.request.params.get('descending') offset = self.request.params.get('offset', '9' if descending else '0') if descending: params['descending'] = descending mode = self.request.params.get('mode') if mode: params['mode'] = mode list_view = VIEW_MAP.get(mode, tenders_real_by_dateModified_view) next_offset = datetime.min.isoformat() if descending else get_now( ).isoformat() if fields: LOGGER.info('Used custom fields for tenders list: {}'.format( ','.join(sorted(fields.split(',')))), extra={'MESSAGE_ID': 'tender_list_custom'}) fields = fields.split(',') + ['dateModified', 'id'] list_view_name = '/'.join([list_view.design, list_view.name]) results = [ tender_serialize(i, fields) for i in Tender.view(self.db, list_view_name, limit=limit + 1, startkey=offset, descending=bool(descending), include_docs=True) ] else: results = [{ 'id': i.id, 'dateModified': i.key } for i in list_view(self.db, limit=limit + 1, startkey=offset, descending=bool(descending))] if len(results) > limit: results, last = results[:-1], results[-1] params['offset'] = last['dateModified'] else: params['offset'] = next_offset return { 'data': results, 'next_page': { "offset": params['offset'], "path": self.request.route_path('collection_Tender', _query=params), "uri": self.request.route_url('collection_Tender', _query=params) } }