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
示例#2
0
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()
示例#4
0
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()
示例#6
0
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
示例#8
0
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