def resync_tenders_back(request): next_url = request.params.get('url', '') if not next_url: next_url = request.registry.api_url + 'tenders?mode=_all_&feed=changes&descending=1&opt_fields=status%2CauctionPeriod%2Clots%2Cnext_check' scheduler = request.registry.scheduler api_token = request.registry.api_token callback_url = request.registry.callback_url request_id = request.environ.get('REQUEST_ID', '') LOGGER.info("Resync back started", extra=context_unpack(request, {'MESSAGE_ID': 'resync_back_started'})) while True: try: r = get_request(next_url, auth=(api_token, ''), headers={'X-Client-Request-ID': request_id}) if r.status_code == requests.codes.not_found: next_url = '' break elif r.status_code != requests.codes.ok: break json = r.json() next_url = json['next_page']['uri'] if not json['data']: LOGGER.info("Resync back stopped", extra=context_unpack(request, {'MESSAGE_ID': 'resync_back_stoped'})) return next_url process_listing(json['data'], scheduler, callback_url, request.registry.db, False) sleep(0.1) except Exception as e: LOGGER.error("Error on resync back: {}".format(repr(e)), extra=context_unpack(request, {'MESSAGE_ID': 'error_resync_back'})) break LOGGER.info("Resync back break", extra=context_unpack(request, {'MESSAGE_ID': 'resync_back_break'})) run_date = get_now() + timedelta(minutes=1) scheduler.add_job(push, 'date', run_date=run_date, timezone=TZ, id='resync_back', name="Resync back", misfire_grace_time=60 * 60, args=[callback_url + 'resync_back', {'url': next_url}], replace_existing=True) return next_url
def resync_tenders_back(request): next_url = request.params.get('url', '') if not next_url: next_url = request.registry.api_url + 'tenders?mode=_all_&feed=changes&descending=1&opt_fields=status%2CauctionPeriod%2Clots%2Cnext_check' scheduler = request.registry.scheduler api_token = request.registry.api_token callback_url = request.registry.callback_url request_id = request.environ.get('REQUEST_ID', '') LOGGER.info("Resync back started", extra=context_unpack(request, {'MESSAGE_ID': 'resync_back_started'})) while True: try: r = get_request(next_url, auth=(api_token, ''), headers={'X-Client-Request-ID': request_id}) if r.status_code == requests.codes.not_found: next_url = '' break elif r.status_code != requests.codes.ok: break json = r.json() next_url = json['next_page']['uri'] if not json['data']: LOGGER.info("Resync back stopped", extra=context_unpack(request, {'MESSAGE_ID': 'resync_back_stoped'})) return next_url process_listing(json['data'], scheduler, callback_url, request.registry.db, False) sleep(0.1) except Exception as e: LOGGER.error("Error on resync back: {}".format(repr(e)), extra=context_unpack(request, {'MESSAGE_ID': 'error_resync_back'})) break LOGGER.info("Resync back break", extra=context_unpack(request, {'MESSAGE_ID': 'resync_back_break'})) run_date = get_now() + timedelta(minutes=1) scheduler.add_job(push, 'date', run_date=run_date, timezone=TZ, id='resync_back', name="Resync back", misfire_grace_time=60 * 60, args=[callback_url + 'resync_back', {'url': next_url}], replace_existing=True) return next_url
def resync_tender(request): tender_id = request.matchdict['tender_id'] scheduler = request.registry.scheduler url = request.registry.api_url + 'tenders/' + tender_id api_token = request.registry.api_token resync_url = request.registry.callback_url + 'resync/' + tender_id recheck_url = request.registry.callback_url + 'recheck/' + tender_id db = request.registry.db request_id = request.environ.get('REQUEST_ID', '') next_check = None next_sync = None r = get_request(url, auth=(api_token, ''), headers={'X-Client-Request-ID': request_id}) if r.status_code != requests.codes.ok: LOGGER.error("Error {} on getting tender '{}': {}".format(r.status_code, url, r.text), extra=context_unpack(request, {'MESSAGE_ID': 'error_get_tender'}, {'ERROR_STATUS': r.status_code})) if r.status_code == requests.codes.not_found: return changes = None next_sync = get_now() + timedelta(seconds=randint(SMOOTHING_REMIN, SMOOTHING_MAX)) else: json = r.json() tender = json['data'] changes = check_tender(request, tender, db) if changes: data = dumps({'data': changes}) r = SESSION.patch(url, data=data, headers={'Content-Type': 'application/json', 'X-Client-Request-ID': request_id}, auth=(api_token, '')) if r.status_code != requests.codes.ok: LOGGER.error("Error {} on updating tender '{}' with '{}': {}".format(r.status_code, url, data, r.text), extra=context_unpack(request, {'MESSAGE_ID': 'error_patch_tender'}, {'ERROR_STATUS': r.status_code})) next_sync = get_now() + timedelta(seconds=randint(SMOOTHING_REMIN, SMOOTHING_MAX)) elif r.json(): if r.json()['data'].get('next_check'): next_check = parse_date(r.json()['data']['next_check'], TZ).astimezone(TZ) if next_check: check_args = dict(timezone=TZ, id="recheck_{}".format(tender_id), name="Recheck {}".format(tender_id), misfire_grace_time=60 * 60, replace_existing=True, args=[recheck_url, None]) if next_check < get_now(): scheduler.add_job(push, 'date', run_date=get_now()+timedelta(seconds=randint(SMOOTHING_MIN, SMOOTHING_MAX)), **check_args) else: scheduler.add_job(push, 'date', run_date=next_check+timedelta(seconds=randint(SMOOTHING_MIN, SMOOTHING_MAX)), **check_args) if next_sync: scheduler.add_job(push, 'date', run_date=next_sync+timedelta(seconds=randint(SMOOTHING_MIN, SMOOTHING_MAX)), timezone=TZ, id=tender_id, name="Resync {}".format(tender_id), misfire_grace_time=60 * 60, replace_existing=True, args=[resync_url, None]) return next_sync and next_sync.isoformat()
def resync_tender(request): tender_id = request.matchdict['tender_id'] scheduler = request.registry.scheduler url = request.registry.api_url + 'tenders/' + tender_id api_token = request.registry.api_token resync_url = request.registry.callback_url + 'resync/' + tender_id recheck_url = request.registry.callback_url + 'recheck/' + tender_id db = request.registry.db request_id = request.environ.get('REQUEST_ID', '') next_check = None next_sync = None r = get_request(url, auth=(api_token, ''), headers={'X-Client-Request-ID': request_id}) if r.status_code != requests.codes.ok: LOGGER.error("Error {} on getting tender '{}': {}".format(r.status_code, url, r.text), extra=context_unpack(request, {'MESSAGE_ID': 'error_get_tender'}, {'ERROR_STATUS': r.status_code})) if r.status_code in [requests.codes.not_found, requests.codes.gone]: return changes = None next_sync = get_now() + timedelta(seconds=randint(SMOOTHING_REMIN, SMOOTHING_MAX)) else: json = r.json() tender = json['data'] changes = check_tender(request, tender, db) if changes: data = dumps({'data': changes}) r = SESSION.patch(url, data=data, headers={'Content-Type': 'application/json', 'X-Client-Request-ID': request_id}, auth=(api_token, '')) if r.status_code != requests.codes.ok: LOGGER.error("Error {} on updating tender '{}' with '{}': {}".format(r.status_code, url, data, r.text), extra=context_unpack(request, {'MESSAGE_ID': 'error_patch_tender'}, {'ERROR_STATUS': r.status_code})) next_sync = get_now() + timedelta(seconds=randint(SMOOTHING_REMIN, SMOOTHING_MAX)) elif r.json(): if r.json()['data'].get('next_check'): next_check = parse_date(r.json()['data']['next_check'], TZ).astimezone(TZ) if next_check: check_args = dict(timezone=TZ, id="recheck_{}".format(tender_id), name="Recheck {}".format(tender_id), misfire_grace_time=60 * 60, replace_existing=True, args=[recheck_url, None]) if next_check < get_now(): scheduler.add_job(push, 'date', run_date=get_now()+timedelta(seconds=randint(SMOOTHING_MIN, SMOOTHING_MAX)), **check_args) else: scheduler.add_job(push, 'date', run_date=next_check+timedelta(seconds=randint(SMOOTHING_MIN, SMOOTHING_MAX)), **check_args) if next_sync: scheduler.add_job(push, 'date', run_date=next_sync+timedelta(seconds=randint(SMOOTHING_MIN, SMOOTHING_MAX)), timezone=TZ, id=tender_id, name="Resync {}".format(tender_id), misfire_grace_time=60 * 60, replace_existing=True, args=[resync_url, None]) return next_sync and next_sync.isoformat()
def recheck_tender(request): tender_id = request.matchdict['tender_id'] scheduler = request.registry.scheduler url = request.registry.api_url + 'tenders/' + tender_id api_token = request.registry.api_token recheck_url = request.registry.callback_url + 'recheck/' + tender_id request_id = request.environ.get('REQUEST_ID', '') next_check = None r = SESSION.patch(url, data=dumps({'data': {'id': tender_id}}), headers={'Content-Type': 'application/json', 'X-Client-Request-ID': request_id}, auth=(api_token, '')) if r.status_code != requests.codes.ok: LOGGER.error("Error {} on checking tender '{}': {}".format(r.status_code, url, r.text), extra=context_unpack(request, {'MESSAGE_ID': 'error_check_tender'}, {'ERROR_STATUS': r.status_code})) if r.status_code not in [requests.codes.forbidden, requests.codes.not_found]: next_check = get_now() + timedelta(minutes=1) elif r.json() and r.json()['data'].get('next_check'): next_check = parse_date(r.json()['data']['next_check'], TZ).astimezone(TZ) if next_check: check_args = dict(timezone=TZ, id="recheck_{}".format(tender_id), name="Recheck {}".format(tender_id), misfire_grace_time=60 * 60, replace_existing=True, args=[recheck_url, None]) if next_check < get_now(): scheduler.add_job(push, 'date', run_date=get_now()+timedelta(seconds=randint(SMOOTHING_MIN, SMOOTHING_MAX)), **check_args) else: scheduler.add_job(push, 'date', run_date=next_check+timedelta(seconds=randint(SMOOTHING_MIN, SMOOTHING_MAX)), **check_args) return next_check and next_check.isoformat()
def recheck_tender(request): tender_id = request.matchdict['tender_id'] scheduler = request.registry.scheduler url = request.registry.api_url + 'tenders/' + tender_id api_token = request.registry.api_token recheck_url = request.registry.callback_url + 'recheck/' + tender_id request_id = request.environ.get('REQUEST_ID', '') next_check = None r = SESSION.patch(url, data=dumps({'data': {'id': tender_id}}), headers={'Content-Type': 'application/json', 'X-Client-Request-ID': request_id}, auth=(api_token, '')) if r.status_code != requests.codes.ok: LOGGER.error("Error {} on checking tender '{}': {}".format(r.status_code, url, r.text), extra=context_unpack(request, {'MESSAGE_ID': 'error_check_tender'}, {'ERROR_STATUS': r.status_code})) if r.status_code not in [requests.codes.forbidden, requests.codes.not_found, requests.codes.gone]: next_check = get_now() + timedelta(minutes=1) elif r.json() and r.json()['data'].get('next_check'): next_check = parse_date(r.json()['data']['next_check'], TZ).astimezone(TZ) if next_check: check_args = dict(timezone=TZ, id="recheck_{}".format(tender_id), name="Recheck {}".format(tender_id), misfire_grace_time=60 * 60, replace_existing=True, args=[recheck_url, None]) if next_check < get_now(): scheduler.add_job(push, 'date', run_date=get_now()+timedelta(seconds=randint(SMOOTHING_MIN, SMOOTHING_MAX)), **check_args) else: scheduler.add_job(push, 'date', run_date=next_check+timedelta(seconds=randint(SMOOTHING_MIN, SMOOTHING_MAX)), **check_args) return next_check and next_check.isoformat()
def check_tender(request, tender, db): now = get_now() quick = environ.get('SANDBOX_MODE', False) and u'quick' in tender.get('submissionMethodDetails', '') if not tender.get('lots') and 'shouldStartAfter' in tender.get('auctionPeriod', {}) and tender['auctionPeriod']['shouldStartAfter'] > tender['auctionPeriod'].get('startDate'): period = tender.get('auctionPeriod') shouldStartAfter = max(parse_date(period.get('shouldStartAfter'), TZ).astimezone(TZ), now) planned = False while not planned: try: auctionPeriod, stream, skip_days = planning_auction(tender, shouldStartAfter, db, quick) planned = True except ResourceConflict: planned = False auctionPeriod = randomize(auctionPeriod).isoformat() planned = 'replanned' if period.get('startDate') else 'planned' LOGGER.info('{} auction for tender {} to {}. Stream {}.{}'.format(planned.title(), tender['id'], auctionPeriod, stream, skipped_days(skip_days)), extra=context_unpack(request, {'MESSAGE_ID': '{}_auction_tender'.format(planned)}, {'PLANNED_DATE': auctionPeriod, 'PLANNED_STREAM': stream, 'PLANNED_DAYS_SKIPPED': skip_days})) return {'auctionPeriod': {'startDate': auctionPeriod}} elif tender.get('lots'): lots = [] for lot in tender.get('lots', []): if lot['status'] != 'active' or 'shouldStartAfter' not in lot.get('auctionPeriod', {}) or lot['auctionPeriod']['shouldStartAfter'] < lot['auctionPeriod'].get('startDate'): lots.append({}) continue period = lot.get('auctionPeriod') shouldStartAfter = max(parse_date(period.get('shouldStartAfter'), TZ).astimezone(TZ), now) lot_id = lot['id'] planned = False while not planned: try: auctionPeriod, stream, skip_days = planning_auction(tender, shouldStartAfter, db, quick, lot_id) planned = True except ResourceConflict: planned = False auctionPeriod = randomize(auctionPeriod).isoformat() planned = 'replanned' if period.get('startDate') else 'planned' lots.append({'auctionPeriod': {'startDate': auctionPeriod}}) LOGGER.info('{} auction for lot {} of tender {} to {}. Stream {}.{}'.format(planned.title(), lot_id, tender['id'], auctionPeriod, stream, skipped_days(skip_days)), extra=context_unpack(request, {'MESSAGE_ID': '{}_auction_lot'.format(planned)}, {'PLANNED_DATE': auctionPeriod, 'PLANNED_STREAM': stream, 'PLANNED_DAYS_SKIPPED': skip_days, 'LOT_ID': lot_id})) if any(lots): return {'lots': lots} return None
def check_tender(request, tender, db): now = get_now() quick = environ.get('SANDBOX_MODE', False) and u'quick' in tender.get('submissionMethodDetails', '') if not tender.get('lots') and 'shouldStartAfter' in tender.get('auctionPeriod', {}) and tender['auctionPeriod']['shouldStartAfter'] > tender['auctionPeriod'].get('startDate'): period = tender.get('auctionPeriod') shouldStartAfter = max(parse_date(period.get('shouldStartAfter'), TZ).astimezone(TZ), now) planned = False while not planned: try: auctionPeriod, stream, skip_days = planning_auction(tender, shouldStartAfter, db, quick) planned = True except ResourceConflict: planned = False auctionPeriod = randomize(auctionPeriod).isoformat() planned = 'replanned' if period.get('startDate') else 'planned' LOGGER.info('{} auction for tender {} to {}. Stream {}.{}'.format(planned.title(), tender['id'], auctionPeriod, stream, skipped_days(skip_days)), extra=context_unpack(request, {'MESSAGE_ID': '{}_auction_tender'.format(planned)}, {'PLANNED_DATE': auctionPeriod, 'PLANNED_STREAM': stream, 'PLANNED_DAYS_SKIPPED': skip_days})) return {'auctionPeriod': {'startDate': auctionPeriod}} elif tender.get('lots'): lots = [] for lot in tender.get('lots', []): if lot['status'] != 'active' or 'shouldStartAfter' not in lot.get('auctionPeriod', {}) or lot['auctionPeriod']['shouldStartAfter'] < lot['auctionPeriod'].get('startDate'): lots.append({}) continue period = lot.get('auctionPeriod') shouldStartAfter = max(parse_date(period.get('shouldStartAfter'), TZ).astimezone(TZ), now) lot_id = lot['id'] planned = False while not planned: try: auctionPeriod, stream, skip_days = planning_auction(tender, shouldStartAfter, db, quick, lot_id) planned = True except ResourceConflict: planned = False auctionPeriod = randomize(auctionPeriod).isoformat() planned = 'replanned' if period.get('startDate') else 'planned' lots.append({'auctionPeriod': {'startDate': auctionPeriod}}) LOGGER.info('{} auction for lot {} of tender {} to {}. Stream {}.{}'.format(planned.title(), lot_id, tender['id'], auctionPeriod, stream, skipped_days(skip_days)), extra=context_unpack(request, {'MESSAGE_ID': '{}_auction_lot'.format(planned)}, {'PLANNED_DATE': auctionPeriod, 'PLANNED_STREAM': stream, 'PLANNED_DAYS_SKIPPED': skip_days, 'LOT_ID': lot_id})) if any(lots): return {'lots': lots} return None
def check_tender(request, tender, db): enquiryPeriodEnd = tender.get('enquiryPeriod', {}).get('endDate') enquiryPeriodEnd = enquiryPeriodEnd and parse_date(enquiryPeriodEnd, TZ).astimezone(TZ) tenderPeriodStart = tender.get('tenderPeriod', {}).get('startDate') tenderPeriodStart = tenderPeriodStart and parse_date(tenderPeriodStart, TZ).astimezone(TZ) tenderPeriodEnd = tender.get('tenderPeriod', {}).get('endDate') tenderPeriodEnd = tenderPeriodEnd and parse_date(tenderPeriodEnd, TZ).astimezone(TZ) now = get_now() if tender['status'] == 'active.enquiries' and not tenderPeriodStart and enquiryPeriodEnd and enquiryPeriodEnd <= now: LOG.info('Switched tender {} to {}'.format(tender['id'], 'active.tendering')) return {'status': 'active.tendering'}, now elif tender['status'] == 'active.enquiries' and tenderPeriodStart and tenderPeriodStart <= now: LOG.info('Switched tender {} to {}'.format(tender['id'], 'active.tendering')) return {'status': 'active.tendering'}, now elif not tender.get('lots') and tender['status'] == 'active.tendering' and not tender.get('auctionPeriod') and tenderPeriodEnd and tenderPeriodEnd > now: planned = False quick = os.environ.get('SANDBOX_MODE', False) and u'quick' in tender.get('submissionMethodDetails', '') while not planned: try: auctionPeriod, stream, skip_days = planning_auction(tender, tenderPeriodEnd, db, quick) planned = True except ResourceConflict: planned = False auctionPeriod = randomize(auctionPeriod).isoformat() LOG.info('Planned auction for tender {} to {}. Stream {}.{}'.format(tender['id'], auctionPeriod, stream, skipped_days(skip_days)), extra=context_unpack(request, {'MESSAGE_ID': 'planned_auction_tender'}, {'PLANNED_DATE': auctionPeriod, 'PLANNED_STREAM': stream, 'PLANNED_DAYS_SKIPPED': skip_days})) return {'auctionPeriod': {'startDate': auctionPeriod}}, now elif tender.get('lots') and tender['status'] == 'active.tendering' and any([not lot.get('auctionPeriod') for lot in tender['lots'] if lot['status'] == 'active']) and tenderPeriodEnd and tenderPeriodEnd > now: quick = os.environ.get('SANDBOX_MODE', False) and u'quick' in tender.get('submissionMethodDetails', '') lots = [] for lot in tender.get('lots', []): if lot['status'] != 'active' or lot.get('auctionPeriod'): lots.append({}) continue lot_id = lot['id'] planned = False while not planned: try: auctionPeriod, stream, skip_days = planning_auction(tender, tenderPeriodEnd, db, quick, lot_id) planned = True except ResourceConflict: planned = False auctionPeriod = randomize(auctionPeriod).isoformat() lots.append({'auctionPeriod': {'startDate': auctionPeriod}}) LOG.info('Planned auction for lot {} of tender {} to {}. Stream {}.{}'.format(lot_id, tender['id'], auctionPeriod, stream, skipped_days(skip_days)), extra=context_unpack(request, {'MESSAGE_ID': 'planned_auction_lot'}, {'PLANNED_DATE': auctionPeriod, 'PLANNED_STREAM': stream, 'PLANNED_DAYS_SKIPPED':skip_days, 'LOT_ID':lot_id})) return {'lots': lots}, now elif not tender.get('lots') and tender['status'] == 'active.tendering' and tenderPeriodEnd and tenderPeriodEnd <= now: LOG.info('Switched tender {} to {}'.format(tender['id'], 'active.auction')) return { 'status': 'active.auction', 'auctionPeriod': {'startDate': None} if tender.get('numberOfBids', 0) < 2 else {} }, now elif tender.get('lots') and tender['status'] == 'active.tendering' and tenderPeriodEnd and tenderPeriodEnd <= now: LOG.info('Switched tender {} to {}'.format(tender['id'], 'active.auction')) return { 'status': 'active.auction', 'lots': [ {'auctionPeriod': {'startDate': None}} if i.get('numberOfBids', 0) < 2 else {} for i in tender.get('lots', []) ] }, now elif not tender.get('lots') and tender['status'] == 'active.auction' and not tender.get('auctionPeriod'): planned = False quick = os.environ.get('SANDBOX_MODE', False) and u'quick' in tender.get('submissionMethodDetails', '') while not planned: try: auctionPeriod, stream, skip_days = planning_auction(tender, tenderPeriodEnd, db, quick) planned = True except ResourceConflict: planned = False auctionPeriod = randomize(auctionPeriod).isoformat() LOG.info('Planned auction for tender {} to {}. Stream {}.{}'.format(tender['id'], auctionPeriod, stream, skipped_days(skip_days)), extra=context_unpack(request, {'MESSAGE_ID': 'planned_auction_tender'}, {'PLANNED_DATE': auctionPeriod, 'PLANNED_STREAM': stream, 'PLANNED_DAYS_SKIPPED':skip_days})) return {'auctionPeriod': {'startDate': auctionPeriod}}, now elif not tender.get('lots') and tender['status'] == 'active.auction' and tender.get('auctionPeriod'): tenderAuctionStart = parse_date(tender.get('auctionPeriod', {}).get('startDate'), TZ).astimezone(TZ) tenderAuctionEnd = calc_auction_end_time(tender.get('numberOfBids', len(tender.get('bids', []))), tenderAuctionStart) if now > tenderAuctionEnd + MIN_PAUSE: planned = False quick = os.environ.get('SANDBOX_MODE', False) and u'quick' in tender.get('submissionMethodDetails', '') while not planned: try: auctionPeriod, stream, skip_days = planning_auction(tender, now, db, quick) planned = True except ResourceConflict: planned = False auctionPeriod = randomize(auctionPeriod).isoformat() LOG.info('Replanned auction for tender {} to {}. Stream {}.{}'.format(tender['id'], auctionPeriod, stream, skipped_days(skip_days)), extra=context_unpack(request, {'MESSAGE_ID': 'replanned_auction_tender'}, {'PLANNED_DATE': auctionPeriod, 'PLANNED_STREAM': stream, 'PLANNED_DAYS_SKIPPED':skip_days})) return {'auctionPeriod': {'startDate': auctionPeriod}}, now else: return None, tenderAuctionEnd + MIN_PAUSE elif tender.get('lots') and tender['status'] == 'active.auction' and any([not lot.get('auctionPeriod') for lot in tender['lots'] if lot['status'] == 'active']): quick = os.environ.get('SANDBOX_MODE', False) and u'quick' in tender.get('submissionMethodDetails', '') lots = [] for lot in tender.get('lots', []): if lot['status'] != 'active' or lot.get('auctionPeriod'): lots.append({}) continue lot_id = lot['id'] planned = False while not planned: try: auctionPeriod, stream, skip_days = planning_auction(tender, tenderPeriodEnd, db, quick, lot_id) planned = True except ResourceConflict: planned = False auctionPeriod = randomize(auctionPeriod).isoformat() lots.append({'auctionPeriod': {'startDate': auctionPeriod}}) LOG.info('Planned auction for lot {} of tender {} to {}. Stream {}.{}'.format(lot_id, tender['id'], auctionPeriod, stream, skipped_days(skip_days)), extra=context_unpack(request, {'MESSAGE_ID': 'planned_auction_lot'}, {'PLANNED_DATE': auctionPeriod, 'PLANNED_STREAM': stream, 'PLANNED_DAYS_SKIPPED':skip_days, 'LOT_ID':lot_id})) return {'lots': lots}, now elif tender.get('lots') and tender['status'] == 'active.auction': quick = os.environ.get('SANDBOX_MODE', False) and u'quick' in tender.get('submissionMethodDetails', '') lots = [] lots_ends = [] for lot in tender.get('lots', []): if lot['status'] != 'active' or lot.get('auctionPeriod', {}).get('endDate'): lots.append({}) continue lot_id = lot['id'] lotAuctionStart = parse_date(lot.get('auctionPeriod', {}).get('startDate'), TZ).astimezone(TZ) lotAuctionEnd = calc_auction_end_time(lot['numberOfBids'], lotAuctionStart) if now > lotAuctionEnd + MIN_PAUSE: planned = False while not planned: try: auctionPeriod, stream, skip_days = planning_auction(tender, now, db, quick, lot_id) planned = True except ResourceConflict: planned = False auctionPeriod = randomize(auctionPeriod).isoformat() lots.append({'auctionPeriod': {'startDate': auctionPeriod}}) LOG.info('Replanned auction for lot {} of tender {} to {}. Stream {}.{}'.format(lot_id, tender['id'], auctionPeriod, stream, skipped_days(skip_days)), extra=context_unpack(request, {'MESSAGE_ID': 'replanned_auction_lot'}, {'PLANNED_DATE': auctionPeriod, 'PLANNED_STREAM': stream, 'PLANNED_DAYS_SKIPPED':skip_days, 'LOT_ID':lot_id})) else: lots_ends.append(lotAuctionEnd + MIN_PAUSE) if any(lots): return {'lots': lots}, now else: return None, min(lots_ends) elif not tender.get('lots') and tender['status'] == 'active.awarded': standStillEnds = [ parse_date(a['complaintPeriod']['endDate'], TZ).astimezone(TZ) for a in tender.get('awards', []) if a.get('complaintPeriod', {}).get('endDate') ] if not standStillEnds: return None, None standStillEnd = max(standStillEnds) if standStillEnd <= now: pending_complaints = any([ i['status'] == 'pending' for i in tender.get('complaints', []) ]) pending_awards_complaints = any([ i['status'] == 'pending' for a in tender.get('awards', []) for i in a.get('complaints', []) ]) awarded = any([ i['status'] == 'active' for i in tender.get('awards', []) ]) if not pending_complaints and not pending_awards_complaints and not awarded: LOG.info('Switched tender {} to {}'.format(tender['id'], 'unsuccessful')) return {'id': tender['id']}, None elif standStillEnd > now: return None, standStillEnd elif tender.get('lots') and tender['status'] in ['active.qualification', 'active.awarded']: pending_complaints = any([ i['status'] == 'pending' for i in tender.get('complaints', []) ]) if pending_complaints: return None, None lots_ends = [] for lot in tender.get('lots', []): if lot['status'] != 'active': continue lot_awards = [i for i in tender['awards'] if i.get('lotID') == lot['id']] standStillEnds = [ parse_date(a['complaintPeriod']['endDate'], TZ).astimezone(TZ) for a in lot_awards if a.get('complaintPeriod', {}).get('endDate') ] if not standStillEnds: continue standStillEnd = max(standStillEnds) if standStillEnd <= now: pending_awards_complaints = any([ i['status'] == 'pending' for a in lot_awards for i in a.get('complaints', []) ]) awarded = any([ i['status'] == 'active' for i in lot_awards ]) if not pending_complaints and not pending_awards_complaints and not awarded: LOG.info('Switched lot {} of tender {} to {}'.format(lot['id'], tender['id'], 'unsuccessful')) return {'id': tender['id']}, None elif standStillEnd > now: lots_ends.append(standStillEnd) if lots_ends: return None, min(lots_ends) if enquiryPeriodEnd and enquiryPeriodEnd > now: return None, enquiryPeriodEnd elif tenderPeriodStart and tenderPeriodStart > now: return None, tenderPeriodStart elif tenderPeriodEnd and tenderPeriodEnd > now: return None, tenderPeriodEnd return None, None
def resync_tenders(request): next_url = request.params.get('url', '') if not next_url or 'opt_fields=status%2CauctionPeriod%2Clots%2Cnext_check' not in next_url: next_url = request.registry.api_url + 'tenders?mode=_all_&feed=changes&descending=1&opt_fields=status%2CauctionPeriod%2Clots%2Cnext_check' scheduler = request.registry.scheduler api_token = request.registry.api_token callback_url = request.registry.callback_url request_id = request.environ.get('REQUEST_ID', '') break_reason = 'unknown' LOGGER.info('Resync all started', extra=context_unpack(request, {'MESSAGE_ID': 'resync_all_started'})) while True: try: r = get_request(next_url, auth=(api_token, ''), headers={'X-Client-Request-ID': request_id}) if r.status_code == requests.codes.not_found: next_url = '' break_reason = 'not_found' break elif r.status_code != requests.codes.ok: break_reason = 'not_ok' break else: json = r.json() next_url = json['next_page']['uri'] if 'descending=1' in next_url: run_date = get_now() scheduler.add_job( push, 'date', run_date=run_date, timezone=TZ, id='resync_back', name='Resync back', misfire_grace_time=60 * 60, args=[callback_url + 'resync_back', { 'url': next_url }], replace_existing=True) next_url = json['prev_page']['uri'] if not json['data']: break_reason = 'empty_data' break process_listing(json['data'], scheduler, callback_url, request.registry.db) sleep(0.1) except Exception as e: LOGGER.error('Error on resync all: {}'.format(repr(e)), extra=context_unpack( request, {'MESSAGE_ID': 'error_resync_all'})) break_reason = 'exception' break LOGGER.info('Resync all break, reason: {}'.format(break_reason), extra=context_unpack( request, { 'MESSAGE_ID': 'resync_all_break', 'BREAK_REASON': break_reason, })) run_date = get_now() + timedelta(minutes=1) scheduler.add_job(push, 'date', run_date=run_date, timezone=TZ, id='resync_all', name='Resync all', misfire_grace_time=60 * 60, args=[callback_url + 'resync_all', { 'url': next_url }], replace_existing=True) return next_url