def create_awards(request): auction = request.validated['auction'] auction.status = 'active.qualification' now = get_now() auction.awardPeriod = type(auction).awardPeriod({'startDate': now}) valid_bids = [bid for bid in auction.bids if bid['status'] != 'invalid'] bids = chef(valid_bids, auction.features or [], [], True) for bid, status in zip(bids, ['pending.verification', 'pending.waiting']): bid = bid.serialize() award = type(auction).awards.model_class({ '__parent__': request.context, 'bid_id': bid['id'], 'status': status, 'date': now, 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': now } }) if award.status == 'pending.verification': award.signingPeriod = award.paymentPeriod = award.verificationPeriod = { 'startDate': now } request.response.headers['Location'] = request.route_url( '{}:Auction Awards'.format(auction.procurementMethodType), auction_id=auction.id, award_id=award['id']) auction.awards.append(award)
def create_awards(request): """ Function create NUMBER_OF_BIDS_TO_BE_QUALIFIED awards objects First award always in pending.verification status others in pending.waiting status """ auction = request.validated['auction'] auction.status = 'active.qualification' now = get_now() auction.awardPeriod = type(auction).awardPeriod({'startDate': now}) awarding_type = request.content_configurator.awarding_type valid_bids = [bid for bid in auction.bids if bid['value'] is not None] bids = chef(valid_bids, auction.features or [], [], True) bids_to_qualify = get_bids_to_qualify(bids) for i in xrange(0, bids_to_qualify): status = 'pending.waiting' if i == 0: status = 'pending.verification' bid = bids[i].serialize() award = make_award(request, auction, bid, status, now, parent=True) if bid['status'] == 'invalid': set_award_status_unsuccessful(award, now) if award.status == 'pending.verification': award.signingPeriod = award.paymentPeriod = award.verificationPeriod = { 'startDate': now } add_award_route_url(request, auction, award, awarding_type) auction.awards.append(award)
def create_awards(request): """ Function create NUMBER_OF_BIDS_TO_BE_QUALIFIED awards objects First award always in pending.verification status others in pending.waiting status """ auction = request.validated['auction'] auction.status = 'active.qualification' now = get_now() auction.awardPeriod = type(auction).awardPeriod({'startDate': now}) awarding_type = request.content_configurator.awarding_type bids = chef(auction.bids, auction.features or [], [], True) # minNumberOfQualifiedBids == 1 bids_to_qualify = get_bids_to_qualify(bids) for bid, status in izip_longest(bids[:bids_to_qualify], ['pending.verification'], fillvalue='pending.waiting'): bid = bid.serialize() award = make_award(request, auction, bid, status, now, parent=True) if bid['status'] == 'invalid': set_award_status_unsuccessful(award, now) if award.status == 'pending.verification': award.verificationPeriod = award.paymentPeriod = award.signingPeriod = { 'startDate': now } add_award_route_url(request, auction, award, awarding_type) auction.awards.append(award)
def _start_awarding(self): """ Function create NUMBER_OF_BIDS_TO_BE_QUALIFIED awards objects First award always in pending.verification status others in pending.waiting status In case that only one bid was applied, award object in pending.admission status will be created for that bid """ auction = self.context auction.status = 'active.qualification' now = get_now() auction.awardPeriod = type(auction).awardPeriod({'startDate': now}) awarding_type = self.awarding_type valid_bids = [bid for bid in auction.bids if self.is_bid_valid(bid)] award_status = 'pending.admission' if len( valid_bids ) == 1 and self.pending_admission_for_one_bid else 'pending' bids = chef(valid_bids, auction.features or [], [], True) bids_to_qualify = self.get_bids_to_qualify(bids) for bid, status in izip_longest(bids[:bids_to_qualify], [award_status], fillvalue='pending.waiting'): bid = bid.serialize() award = make_award(self.request, auction, bid, status, now, parent=True) if bid['status'] == 'invalid': set_award_status_unsuccessful(award, now) if award.status == 'pending': award.verificationPeriod = self.verificationPeriod() award.signingPeriod = self.signingPeriod() add_award_route_url(self.request, auction, award, awarding_type) if award.status == 'pending.admission': award.admissionPeriod = { 'startDate': now, 'endDate': calculate_business_date(start=now, context=auction, **award.ADMISSION_PERIOD_PARAMS) } add_award_route_url(self.request, auction, award, awarding_type) auction.awards.append(award) return True
def prepare_bids_for_awarding(tender, bids, lot_id=None): """ Used by add_next_award method :param tender: :param bids :param lot_id: :return: list of bid dict objects sorted in a way they will be selected as winners """ lot_items = [i.id for i in tender.items if i.relatedLot == lot_id] # all items in case of non-lot tender features = [ i for i in (tender.features or []) if i.featureOf == "tenderer" or i.featureOf == "lot" and i.relatedItem == lot_id or i.featureOf == "item" and i.relatedItem in lot_items ] # all features in case of non-lot tender codes = [i.code for i in features] active_bids = [] for bid in bids: if bid.status == "active": bid_params = [i for i in bid.parameters if i.code in codes] if lot_id: for lot_value in bid.lotValues: if lot_value.relatedLot == lot_id and getattr(lot_value, "status", "active") == "active": active_bids.append( { "id": bid.id, "value": lot_value.value.serialize(), "tenderers": bid.tenderers, "parameters": bid_params, "date": lot_value.date, } ) continue # only one lotValue in a bid is expected else: active_bids.append( { "id": bid.id, "value": bid.value.serialize(), "tenderers": bid.tenderers, "parameters": bid_params, "date": bid.date, } ) configurator = tender.__parent__.request.content_configurator bids = chef( active_bids, features, ignore="", # filters by id, shouldn't be a part of this lib reverse=configurator.reverse_awarding_criteria, awarding_criteria_key=configurator.awarding_criteria_key, ) return bids
def create_awards(request): """ Function create NUMBER_OF_BIDS_TO_BE_QUALIFIED awards objects First award always in pending.verification status others in pending.waiting status In case that only one bid was applied, award object in pending.admission status will be created for that bid """ auction = request.validated['auction'] auction.status = 'active.qualification' now = get_now() auction.awardPeriod = type(auction).awardPeriod({'startDate': now}) awarding_type = request.content_configurator.awarding_type valid_bids = [bid for bid in auction.bids if bid['value'] is not None] if len(valid_bids) == 1: bid = valid_bids[0].serialize() award = make_award(request, auction, bid, 'pending.admission', now, parent=True) if bid['status'] == 'invalid': set_award_status_unsuccessful(award, now) if award.status == 'pending.admission': award.admissionPeriod = { 'startDate': now, 'endDate': calculate_business_date(now, VERIFY_ADMISSION_PROTOCOL_TIME, auction, True, AWARDING_PERIODS_END_DATE_HOUR) } add_award_route_url(request, auction, award, awarding_type) auction.awards.append(award) else: bids = chef(valid_bids, auction.features or [], [], True) bids_to_qualify = get_bids_to_qualify(bids) for bid, status in izip_longest(bids[:bids_to_qualify], ['pending'], fillvalue='pending.waiting'): bid = bid.serialize() award = make_award(request, auction, bid, status, now, parent=True) if bid['status'] == 'invalid': set_award_status_unsuccessful(award, now) if award.status == 'pending': award.signingPeriod = award.verificationPeriod = { 'startDate': now } add_award_route_url(request, auction, award, awarding_type) auction.awards.append(award)
def sorting_start_bids_by_amount(bids, features=None, reverse=True): """ >>> from json import load >>> import os >>> data = load(open(os.path.join(os.path.dirname(__file__), ... 'tests/functional/data/tender_simple.json'))) >>> sorted_data = sorting_start_bids_by_amount(data['data']['bids']) """ def get_amount(item): return item['value']['amount'] # return sorted(bids, key=get_amount, reverse=reverse) return chef(bids, features=features)
def migrate_awarding_1_0_to_awarding_2_1(auction, procurementMethodTypes): if (auction['procurementMethodType'] not in procurementMethodTypes or auction['status'] not in ['active.qualification', 'active.awarded'] or 'awards' not in auction): return now = get_now().isoformat() awards = auction["awards"] award = [a for a in awards if a['status'] in ['active', 'pending']][0] award_create_date = award['complaintPeriod']['startDate'] award.update({ 'verificationPeriod': { 'startDate': award_create_date, 'endDate': award_create_date }, 'paymentPeriod': { 'startDate': award_create_date, }, 'signingPeriod': { 'startDate': award_create_date, } }) if award['status'] == 'pending': award['status'] = 'pending.payment' elif award['status'] == 'active': award['paymentPeriod']['endDate'] = now awarded_bids = set([a['bid_id'] for a in awards]) sorted_bids = chef(auction['bids'], auction.get('features'), [], True) filtered_bids = [bid for bid in sorted_bids if bid['id'] not in awarded_bids] for bid in filtered_bids: award = { 'id': uuid4().hex, 'bid_id': bid['id'], 'status': 'pending.waiting', 'date': award_create_date, 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': award_create_date } } awards.append(award)
def create_awards(request): """ Function create NUMBER_OF_BIDS_TO_BE_QUALIFIED awards objects First award always in pending.verification status others in pending.waiting status """ auction = request.validated['auction'] auction.status = 'active.qualification' now = get_now() auction.awardPeriod = type(auction).awardPeriod({'startDate': now}) bids = chef(auction.bids, auction.features or [], [], True) # minNumberOfQualifiedBids == 1 bids_to_qualify = NUMBER_OF_BIDS_TO_BE_QUALIFIED \ if (len(bids) > NUMBER_OF_BIDS_TO_BE_QUALIFIED) \ else len(bids) for bid, status in izip_longest(bids[:bids_to_qualify], ['pending.verification'], fillvalue='pending.waiting'): bid = bid.serialize() award = type(auction).awards.model_class({ '__parent__': request.context, 'bid_id': bid['id'], 'status': status, 'date': now, 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': now } }) if bid['status'] == 'invalid': award.status = 'unsuccessful' award.complaintPeriod.endDate = now if award.status == 'pending.verification': award.verificationPeriod = award.paymentPeriod = award.signingPeriod = { 'startDate': now } request.response.headers['Location'] = request.route_url( '{}:Auction Awards'.format(auction.procurementMethodType), auction_id=auction.id, award_id=award['id']) auction.awards.append(award)
def sorting_start_bids_by_amount(bids, features=None, reverse=True): """ >>> from json import load >>> import os >>> data = load(open(os.path.join(os.path.dirname(__file__), ... 'tests/data/tender_data.json'))) >>> sorted_data = sorting_start_bids_by_amount(data['data']['bids']) >>> sorted_data[0]['value']['amount'] > sorted_data[1]['value']['amount'] True >>> sorted_data = sorting_start_bids_by_amount(data['data']['bids'], ... reverse=False) >>> sorted_data[0]['value']['amount'] < sorted_data[1]['value']['amount'] True """ def get_amount(item): return item['value']['amount'] # return sorted(bids, key=get_amount, reverse=reverse) return chef(bids, features=features)
def create_awards(request): auction = request.validated['auction'] auction.status = 'active.qualification' now = get_now() auction.awardPeriod = type(auction).awardPeriod({'startDate': now}) bids = chef(auction.bids, auction.features or [], [], True) for i, status in enumerate(['pending.verification', 'pending.waiting']): bid = bids[i].serialize() award = type(auction).awards.model_class({ 'bid_id': bid['id'], 'status': status, 'date': now, 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': now } }) if bid['status'] == 'invalid': award.status = 'unsuccessful' award.complaintPeriod.endDate = now if award.status == 'pending.verification': award.verificationPeriod = {'startDate': now} award.verificationPeriod.endDate = calculate_business_date( now, VERIFY_AUCTION_PROTOCOL_TIME, auction, True) award.paymentPeriod = {'startDate': now} award.paymentPeriod.endDate = calculate_business_date( now, AWARD_PAYMENT_TIME, auction, True) award.signingPeriod = {'startDate': now} award.complaintPeriod.endDate = award.signingPeriod.endDate = calculate_business_date( now, CONTRACT_SIGNING_TIME, auction, True) request.response.headers['Location'] = request.route_url( '{}:Auction Awards'.format(auction.procurementMethodType), auction_id=auction.id, award_id=award['id']) auction.awards.append(award)
def add_next_awards(request, reverse=False, awarding_criteria_key="amount", regenerate_all_awards=False, lot_id=None): """Adding next award. :param request: The pyramid request object. :param reverse: Is used for sorting bids to generate award. By default (reverse = False) awards are generated from lower to higher by value.amount When reverse is set to True awards are generated from higher to lower by value.amount """ tender = request.validated["tender"] max_awards = tender["maxAwardsCount"] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != "active": continue lot_awards = [ award for award in tender.awards if award.lotID == lot.id ] lot_awards_statuses = { award["status"] for award in tender.awards if award.lotID == lot.id } if lot_awards_statuses and lot_awards_statuses.issubset( {"pending", "active"}): statuses.union(lot_awards_statuses) continue all_bids = prepare_bids_for_awarding(tender, tender.bids, lot_id=lot.id) if not all_bids: lot.status = "unsuccessful" statuses.add("unsuccessful") continue selected_bids = exclude_unsuccessful_awarded_bids(tender, all_bids, lot_id=lot.id) if not regenerate_all_awards and lot.id == lot_id: # this block seems is supposed to cause the function append only one award # for a bid of the first (the only?) cancelled award cancelled_award_bid_ids = [ award.bid_id for award in lot_awards if award.status == "cancelled" and request.context.id == award.id ] if cancelled_award_bid_ids: selected_bids = [ bid for bid in all_bids if bid["id"] == cancelled_award_bid_ids[0] ] if max_awards: # limit awards selected_bids = selected_bids[:max_awards] active_award_bid_ids = { a.bid_id for a in lot_awards if a.status in ("active", "pending") } selected_bids = list( filter(lambda b: b["id"] not in active_award_bid_ids, selected_bids)) if selected_bids: for bid in selected_bids: tender.append_award(bid, all_bids, lot_id=lot.id) statuses.add("pending") else: statuses.add("unsuccessful") if (statuses.difference({"unsuccessful", "active"}) and any([i for i in tender.lots])): # logic for auction to switch status tender.awardPeriod.endDate = None tender.status = "active.qualification" else: # pragma: no cover if not tender.awards or request.context.status in ("cancelled", "unsuccessful"): codes = [i.code for i in tender.features or []] active_bids = [{ "id": bid.id, "value": bid.value.serialize(), "tenderers": bid.tenderers, "parameters": [i for i in bid.parameters if i.code in codes], "date": bid.date, } for bid in tender.bids if bid.status == "active"] cancelled_awards = None if not regenerate_all_awards: cancelled_awards = [ award.bid_id for award in tender.awards if award.status == "cancelled" and request.context.id == award.id ] unsuccessful_awards = [ i.bid_id for i in tender.awards if i.status == "unsuccessful" ] bids = chef(active_bids, tender.features or [], unsuccessful_awards, reverse, awarding_criteria_key) bids = [bid for bid in bids if bid["id"] == cancelled_awards[0] ] if cancelled_awards else bids bids = bids[:max_awards] if max_awards else bids active_awards = [ a.bid_id for a in tender.awards if a.status in ("active", "pending") ] bids = [bid for bid in bids if bid["id"] not in active_awards] if bids: for bid in bids: award = tender.__class__.awards.model_class({ "bid_id": bid["id"], "status": "pending", "date": get_now(), "value": bid["value"], "suppliers": bid["tenderers"], }) award.__parent__ = tender tender.awards.append(award) if tender.awards[ -1].status == "pending": # logic for auction to switch status tender.awardPeriod.endDate = None tender.status = "active.qualification"
def add_next_award(request): auction = request.validated['auction'] now = get_now() if not auction.awardPeriod: auction.awardPeriod = type(auction).awardPeriod({}) if not auction.awardPeriod.startDate: auction.awardPeriod.startDate = now if auction.lots: statuses = set() for lot in auction.lots: if lot.status != 'active': continue lot_awards = [i for i in auction.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ['pending', 'active']: statuses.add(lot_awards[-1].status if lot_awards else 'unsuccessful') continue lot_items = [i.id for i in auction.items if i.relatedLot == lot.id] features = [ i for i in (auction.features or []) if i.featureOf == 'tenderer' or i.featureOf == 'lot' and i.relatedItem == lot.id or i.featureOf == 'item' and i.relatedItem in lot_items ] codes = [i.code for i in features] bids = [ { 'id': bid.id, 'value': [i for i in bid.lotValues if lot.id == i.relatedLot][0].value, 'tenderers': bid.tenderers, 'parameters': [i for i in bid.parameters if i.code in codes], 'date': [i for i in bid.lotValues if lot.id == i.relatedLot][0].date } for bid in auction.bids if lot.id in [i.relatedLot for i in bid.lotValues] ] if not bids: lot.status = 'unsuccessful' statuses.add('unsuccessful') continue unsuccessful_awards = [i.bid_id for i in lot_awards if i.status == 'unsuccessful'] bids = chef(bids, features, unsuccessful_awards, True) if bids: bid = bids[0] award = type(auction).awards.model_class({ 'bid_id': bid['id'], 'lotID': lot.id, 'status': 'pending', 'value': bid['value'], 'date': get_now(), 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': now.isoformat() } }) auction.awards.append(award) request.response.headers['Location'] = request.route_url('{}:Auction Awards'.format(auction.procurementMethodType), auction_id=auction.id, award_id=award['id']) statuses.add('pending') else: statuses.add('unsuccessful') if statuses.difference(set(['unsuccessful', 'active'])): auction.awardPeriod.endDate = None auction.status = 'active.qualification' else: auction.awardPeriod.endDate = now auction.status = 'active.awarded' else: if not auction.awards or auction.awards[-1].status not in ['pending', 'active']: unsuccessful_awards = [i.bid_id for i in auction.awards if i.status == 'unsuccessful'] bids = chef(auction.bids, auction.features or [], unsuccessful_awards, True) if bids: bid = bids[0].serialize() award = type(auction).awards.model_class({ 'bid_id': bid['id'], 'status': 'pending', 'date': get_now(), 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': get_now().isoformat() } }) auction.awards.append(award) request.response.headers['Location'] = request.route_url('{}:Auction Awards'.format(auction.procurementMethodType), auction_id=auction.id, award_id=award['id']) if auction.awards[-1].status == 'pending': auction.awardPeriod.endDate = None auction.status = 'active.qualification' else: auction.awardPeriod.endDate = now auction.status = 'active.awarded'
def add_next_award(request): tender = request.validated["tender"] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != "active": continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ["pending", "active"]: statuses.add(lot_awards[-1].status if lot_awards else "unsuccessful") continue lot_items = [i.id for i in tender.items if i.relatedLot == lot.id] features = [ i for i in (tender.features or []) if i.featureOf == "tenderer" or i.featureOf == "lot" and i.relatedItem == lot.id or i.featureOf == "item" and i.relatedItem in lot_items ] codes = [i.code for i in features] bids = [ { "id": bid.id, "value": [i for i in bid.lotValues if lot.id == i.relatedLot][0].value, "tenderers": bid.tenderers, "parameters": [i for i in bid.parameters if i.code in codes], "date": [i for i in bid.lotValues if lot.id == i.relatedLot][0].date, } for bid in tender.bids if bid.status == "active" and lot.id in [i.relatedLot for i in bid.lotValues if getattr(i, "status", "active") == "active"] ] if not bids: lot.status = "unsuccessful" statuses.add("unsuccessful") continue unsuccessful_awards = [i.bid_id for i in lot_awards if i.status == "unsuccessful"] bids = chef(bids, features, unsuccessful_awards) if bids: bid = bids[0] award = tender.__class__.awards.model_class( { "bid_id": bid["id"], "lotID": lot.id, "status": "pending", "date": get_now(), "value": bid["value"], "suppliers": bid["tenderers"], "complaintPeriod": {"startDate": now.isoformat()}, } ) tender.awards.append(award) request.response.headers["Location"] = request.route_url( "Tender Awards", tender_id=tender.id, award_id=award["id"] ) statuses.add("pending") else: statuses.add("unsuccessful") if statuses.difference(set(["unsuccessful", "active"])): tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded" else: if not tender.awards or tender.awards[-1].status not in ["pending", "active"]: unsuccessful_awards = [i.bid_id for i in tender.awards if i.status == "unsuccessful"] active_bids = [bid for bid in tender.bids if bid.status == "active"] bids = chef(active_bids, tender.features or [], unsuccessful_awards) if bids: bid = bids[0].serialize() award = tender.__class__.awards.model_class( { "bid_id": bid["id"], "status": "pending", "date": get_now(), "value": bid["value"], "suppliers": bid["tenderers"], "complaintPeriod": {"startDate": get_now().isoformat()}, } ) tender.awards.append(award) request.response.headers["Location"] = request.route_url( "Tender Awards", tender_id=tender.id, award_id=award["id"] ) if tender.awards[-1].status == "pending": tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded"
def add_next_award(request): tender = request.validated['tender'] now = get_now() if not tender.awardPeriod: tender.awardPeriod = Period({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != 'active': continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ['pending', 'active']: statuses.add(lot_awards[-1].status if lot_awards else 'unsuccessful') continue lot_items = [i.id for i in tender.items if i.relatedLot == lot.id] features = [ i for i in tender.features if i.featureOf == 'tenderer' or i.featureOf == 'lot' and i.relatedItem == lot.id or i.featureOf == 'item' and i.relatedItem in lot_items ] codes = [i.code for i in features] bids = [ { 'id': bid.id, 'value': [i for i in bid.lotValues if lot.id == i.relatedLot][0].value, 'tenderers': bid.tenderers, 'parameters': [i for i in bid.parameters if i.code in codes], 'date': [i for i in bid.lotValues if lot.id == i.relatedLot][0].date } for bid in tender.bids if lot.id in [i.relatedLot for i in bid.lotValues] ] if not bids: lot.status = 'unsuccessful' statuses.add('unsuccessful') continue unsuccessful_awards = [i.bid_id for i in lot_awards if i.status == 'unsuccessful'] bids = chef(bids, features, unsuccessful_awards) if bids: bid = bids[0] award = Award({ 'bid_id': bid['id'], 'lotID': lot.id, 'status': 'pending', 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': now.isoformat() } }) tender.awards.append(award) request.response.headers['Location'] = request.route_url('Tender Awards', tender_id=tender.id, award_id=award['id']) statuses.add('pending') else: statuses.add('unsuccessful') if statuses.difference(set(['unsuccessful', 'active'])): tender.awardPeriod.endDate = None tender.status = 'active.qualification' else: tender.awardPeriod.endDate = now tender.status = 'active.awarded' else: if not tender.awards or tender.awards[-1].status not in ['pending', 'active']: unsuccessful_awards = [i.bid_id for i in tender.awards if i.status == 'unsuccessful'] bids = chef(tender.bids, tender.features, unsuccessful_awards) if bids: bid = bids[0].serialize() award = Award({ 'bid_id': bid['id'], 'status': 'pending', 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': get_now().isoformat() } }) tender.awards.append(award) request.response.headers['Location'] = request.route_url('Tender Awards', tender_id=tender.id, award_id=award['id']) if tender.awards[-1].status == 'pending': tender.awardPeriod.endDate = None tender.status = 'active.qualification' else: tender.awardPeriod.endDate = now tender.status = 'active.awarded'
def from0to1(registry): class Request(object): def __init__(self, registry): self.registry = registry results = registry.db.iterview('auctions/all', 2**10, include_docs=True) request = Request(registry) root = Root(request) docs = [] for i in results: auction = i.doc if auction['procurementMethodType'] not in ['dgfOtherAssets', 'dgfFinancialAssets'] \ or auction['status'] not in ['active.qualification', 'active.awarded'] \ or 'awards' not in auction: continue now = get_now().isoformat() awards = auction["awards"] unique_awards = len(set([a['bid_id'] for a in awards])) if unique_awards > 2: switch_auction_to_unsuccessful(auction) else: invalidate_bids_under_threshold(auction) if all(bid['status'] == 'invalid' for bid in auction['bids']): switch_auction_to_unsuccessful(auction) if auction['status'] != 'unsuccessful': award = [ a for a in auction["awards"] if a['status'] in ['active', 'pending'] ][0] award_create_date = award['complaintPeriod']['startDate'] periods = { 'verificationPeriod': { 'startDate': award_create_date, 'endDate': award_create_date }, 'paymentPeriod': { 'startDate': award_create_date, 'endDate': calculate_business_date(parse_date(award_create_date, TZ), AWARD_PAYMENT_TIME, auction, True).isoformat() }, 'signingPeriod': { 'startDate': award_create_date, 'endDate': calculate_business_date(parse_date(award_create_date, TZ), CONTRACT_SIGNING_TIME, auction, True).isoformat() } } award.update(periods) if award['status'] == 'pending': award['status'] = 'pending.payment' elif award['status'] == 'active': award['verificationPeriod']['endDate'] = award[ 'paymentPeriod']['endDate'] = now if unique_awards == 1: bid = chef(auction['bids'], auction.get('features'), [], True)[1] award = { 'id': uuid4().hex, 'bid_id': bid['id'], 'status': 'pending.waiting', 'date': awards[0]['date'], 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': awards[0]['date'] } } if bid['status'] == 'invalid': award['status'] == 'unsuccessful' award['complaintPeriod']['endDate'] = now awards.append(award) model = registry.auction_procurementMethodTypes.get( auction['procurementMethodType']) if model: try: auction = model(auction) auction.__parent__ = root auction = auction.to_primitive() except: LOGGER.error( "Failed migration of auction {} to schema 1.".format( auction.id), extra={ 'MESSAGE_ID': 'migrate_data_failed', 'AUCTION_ID': auction.id }) else: auction['dateModified'] = get_now().isoformat() docs.append(auction) if len(docs) >= 2**7: registry.db.update(docs) docs = [] if docs: registry.db.update(docs)
def add_next_award(request): tender = request.validated["tender"] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != "active": continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ["pending", "active"]: statuses.add( lot_awards[-1].status if lot_awards else "unsuccessful") continue lot_items = [i.id for i in tender.items if i.relatedLot == lot.id] features = [ i for i in (tender.features or []) if i.featureOf == "tenderer" or i.featureOf == "lot" and i.relatedItem == lot.id or i.featureOf == "item" and i.relatedItem in lot_items ] codes = [i.code for i in features] bids = [{ "id": bid.id, "value": [i for i in bid.lotValues if lot.id == i.relatedLot][0].value, "tenderers": bid.tenderers, "parameters": [i for i in bid.parameters if i.code in codes], "date": [i for i in bid.lotValues if lot.id == i.relatedLot][0].date, } for bid in tender.bids if lot.id in [i.relatedLot for i in bid.lotValues]] if not bids: lot.status = "unsuccessful" statuses.add("unsuccessful") continue unsuccessful_awards = [ i.bid_id for i in lot_awards if i.status == "unsuccessful" ] bids = chef(bids, features, unsuccessful_awards) if bids: bid = bids[0] award = type(tender).awards.model_class({ "bid_id": bid["id"], "lotID": lot.id, "status": "pending", "value": bid["value"], "date": get_now(), "suppliers": bid["tenderers"], }) tender.awards.append(award) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format(tender.procurementMethodType), tender_id=tender.id, award_id=award["id"]) statuses.add("pending") else: statuses.add("unsuccessful") if statuses.difference(set(["unsuccessful", "active"])): tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded" else: if not tender.awards or tender.awards[-1].status not in [ "pending", "active" ]: unsuccessful_awards = [ i.bid_id for i in tender.awards if i.status == "unsuccessful" ] bids = chef(tender.bids, tender.features or [], unsuccessful_awards) if bids: bid = bids[0].serialize() award = type(tender).awards.model_class({ "bid_id": bid["id"], "status": "pending", "date": get_now(), "value": bid["value"], "suppliers": bid["tenderers"], }) tender.awards.append(award) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format(tender.procurementMethodType), tender_id=tender.id, award_id=award["id"]) if tender.awards[-1].status == "pending": tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded"
def add_next_award(request, reverse=False, awarding_criteria_key="amount"): """Adding next award. :param request: The pyramid request object. :param reverse: Is used for sorting bids to generate award. By default (reverse = False) awards are generated from lower to higher by value.amount When reverse is set to True awards are generated from higher to lower by value.amount """ tender = request.validated["tender"] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != "active": continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ["pending", "active"]: statuses.add(lot_awards[-1].status if lot_awards else "unsuccessful") continue lot_items = [i.id for i in tender.items if i.relatedLot == lot.id] features = [ i for i in (tender.features or []) if i.featureOf == "tenderer" or i.featureOf == "lot" and i.relatedItem == lot.id or i.featureOf == "item" and i.relatedItem in lot_items ] codes = [i.code for i in features] bids = [ { "id": bid.id, "value": [i for i in bid.lotValues if lot.id == i.relatedLot][0].value.serialize(), "tenderers": bid.tenderers, "parameters": [i for i in bid.parameters if i.code in codes], "date": [i for i in bid.lotValues if lot.id == i.relatedLot][0].date, } for bid in tender.bids if bid.status == "active" and lot.id in [i.relatedLot for i in bid.lotValues if getattr(i, "status", "active") == "active"] ] if not bids: lot.status = "unsuccessful" statuses.add("unsuccessful") continue unsuccessful_awards = [i.bid_id for i in lot_awards if i.status == "unsuccessful"] bids = chef(bids, features, unsuccessful_awards, reverse, awarding_criteria_key) if bids: bid = bids[0] award = tender.__class__.awards.model_class( { "bid_id": bid["id"], "lotID": lot.id, "status": "pending", "date": get_now(), "value": bid["value"], "suppliers": bid["tenderers"], "complaintPeriod": {"startDate": now.isoformat()}, } ) award.__parent__ = tender tender.awards.append(award) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format(tender.procurementMethodType), tender_id=tender.id, award_id=award["id"] ) statuses.add("pending") else: statuses.add("unsuccessful") if statuses.difference(set(["unsuccessful", "active"])): tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded" else: if not tender.awards or tender.awards[-1].status not in ["pending", "active"]: unsuccessful_awards = [i.bid_id for i in tender.awards if i.status == "unsuccessful"] codes = [i.code for i in tender.features or []] active_bids = [ { "id": bid.id, "value": bid.value.serialize(), "tenderers": bid.tenderers, "parameters": [i for i in bid.parameters if i.code in codes], "date": bid.date, } for bid in tender.bids if bid.status == "active" ] bids = chef(active_bids, tender.features or [], unsuccessful_awards, reverse, awarding_criteria_key) if bids: bid = bids[0] award = tender.__class__.awards.model_class( { "bid_id": bid["id"], "status": "pending", "date": get_now(), "value": bid["value"], "suppliers": bid["tenderers"], "complaintPeriod": {"startDate": get_now().isoformat()}, } ) award.__parent__ = tender tender.awards.append(award) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format(tender.procurementMethodType), tender_id=tender.id, award_id=award["id"] ) if tender.awards[-1].status == "pending": tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded"
def add_next_award(request): tender = request.validated['tender'] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != 'active': continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ['pending', 'active']: statuses.add( lot_awards[-1].status if lot_awards else 'unsuccessful') continue lot_items = [i.id for i in tender.items if i.relatedLot == lot.id] features = [ i for i in (tender.features or []) if i.featureOf == 'tenderer' or i.featureOf == 'lot' and i.relatedItem == lot.id or i.featureOf == 'item' and i.relatedItem in lot_items ] codes = [i.code for i in features] bids = [{ 'id': bid.id, 'value': [i for i in bid.lotValues if lot.id == i.relatedLot][0].value, 'tenderers': bid.tenderers, 'parameters': [i for i in bid.parameters if i.code in codes], 'date': [i for i in bid.lotValues if lot.id == i.relatedLot][0].date } for bid in tender.bids if bid.status == "active" and lot.id in [ i.relatedLot for i in bid.lotValues if getattr(i, 'status', "active") == "active" ]] if not bids: lot.status = 'unsuccessful' statuses.add('unsuccessful') continue unsuccessful_awards = [ i.bid_id for i in lot_awards if i.status == 'unsuccessful' ] bids = chef(bids, features, unsuccessful_awards) if bids: bid = bids[0] award = tender.__class__.awards.model_class({ 'bid_id': bid['id'], 'lotID': lot.id, 'status': 'pending', 'date': get_now(), 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': now.isoformat() } }) tender.awards.append(award) request.response.headers['Location'] = request.route_url( 'Tender Awards', tender_id=tender.id, award_id=award['id']) statuses.add('pending') else: statuses.add('unsuccessful') if statuses.difference(set(['unsuccessful', 'active'])): tender.awardPeriod.endDate = None tender.status = 'active.qualification' else: tender.awardPeriod.endDate = now tender.status = 'active.awarded' else: if not tender.awards or tender.awards[-1].status not in [ 'pending', 'active' ]: unsuccessful_awards = [ i.bid_id for i in tender.awards if i.status == 'unsuccessful' ] active_bids = [ bid for bid in tender.bids if bid.status == "active" ] bids = chef(active_bids, tender.features or [], unsuccessful_awards) if bids: bid = bids[0].serialize() award = tender.__class__.awards.model_class({ 'bid_id': bid['id'], 'status': 'pending', 'date': get_now(), 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': get_now().isoformat() } }) tender.awards.append(award) request.response.headers['Location'] = request.route_url( 'Tender Awards', tender_id=tender.id, award_id=award['id']) if tender.awards[-1].status == 'pending': tender.awardPeriod.endDate = None tender.status = 'active.qualification' else: tender.awardPeriod.endDate = now tender.status = 'active.awarded'
def add_next_award(request): auction = request.validated['auction'] awarding_type = request.content_configurator.awarding_type now = get_now() if not auction.awardPeriod: auction.awardPeriod = type(auction).awardPeriod({}) if not auction.awardPeriod.startDate: auction.awardPeriod.startDate = now if auction.lots: statuses = set() for lot in auction.lots: if lot.status != 'active': continue lot_awards = [i for i in auction.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ['pending', 'active']: statuses.add( lot_awards[-1].status if lot_awards else 'unsuccessful') continue lot_items = [i.id for i in auction.items if i.relatedLot == lot.id] features = [ i for i in (auction.features or []) if i.featureOf == 'tenderer' or i.featureOf == 'lot' and i.relatedItem == lot.id or i.featureOf == 'item' and i.relatedItem in lot_items ] codes = [i.code for i in features] bids = [{ 'id': bid.id, 'value': [i for i in bid.lotValues if lot.id == i.relatedLot][0].value, 'tenderers': bid.tenderers, 'parameters': [i for i in bid.parameters if i.code in codes], 'date': [i for i in bid.lotValues if lot.id == i.relatedLot][0].date } for bid in auction.bids if lot.id in [i.relatedLot for i in bid.lotValues]] if not bids: lot.status = 'unsuccessful' statuses.add('unsuccessful') continue unsuccessful_awards = [ i.bid_id for i in lot_awards if i.status == 'unsuccessful' ] bids = chef(bids, features, unsuccessful_awards, True) if bids: bid = bids[0] award = make_award(request, auction, bid, 'pending', now, lot_id=lot.id, parent=None) auction.awards.append(award) add_award_route_url(request, auction, award, awarding_type) statuses.add('pending') else: statuses.add('unsuccessful') if statuses.difference(set(['unsuccessful', 'active'])): auction.awardPeriod.endDate = None auction.status = 'active.qualification' else: auction.awardPeriod.endDate = now auction.status = 'active.awarded' else: if not auction.awards or auction.awards[-1].status not in [ 'pending', 'active' ]: unsuccessful_awards = [ i.bid_id for i in auction.awards if i.status == 'unsuccessful' ] bids = chef(auction.bids, auction.features or [], unsuccessful_awards, True) if bids: bid = bids[0].serialize() award = make_award(request, auction, bid, 'pending', now, parent=None) auction.awards.append(award) add_award_route_url(request, auction, award, awarding_type) if auction.awards[-1].status == 'pending': auction.awardPeriod.endDate = None auction.status = 'active.qualification' else: auction.awardPeriod.endDate = now auction.status = 'active.awarded'
def from0to1(registry): class Request(object): def __init__(self, registry): self.registry = registry results = registry.db.iterview('auctions/all', 2**10, include_docs=True) request = Request(registry) root = Root(request) docs = [] for i in results: auction = i.doc if auction['procurementMethodType'] not in ['dgfOtherAssets', 'dgfFinancialAssets'] \ or auction['status'] not in ['active.qualification', 'active.awarded'] \ or 'awards' not in auction: continue now = get_now().isoformat() awards = auction["awards"] award = [a for a in awards if a['status'] in ['active', 'pending']][0] award_create_date = award['complaintPeriod']['startDate'] award.update({ 'verificationPeriod': { 'startDate': award_create_date, 'endDate': award_create_date }, 'paymentPeriod': { 'startDate': award_create_date, }, 'signingPeriod': { 'startDate': award_create_date, } }) if award['status'] == 'pending': award['status'] = 'pending.payment' elif award['status'] == 'active': award['paymentPeriod']['endDate'] = now awarded_bids = set([a['bid_id'] for a in awards]) sorted_bids = chef(auction['bids'], auction.get('features'), [], True) filtered_bids = [ bid for bid in sorted_bids if bid['id'] not in awarded_bids ] for bid in filtered_bids: award = { 'id': uuid4().hex, 'bid_id': bid['id'], 'status': 'pending.waiting', 'date': award_create_date, 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': award_create_date } } awards.append(award) model = registry.auction_procurementMethodTypes.get( auction['procurementMethodType']) if model: try: auction = model(auction) auction.__parent__ = root auction = auction.to_primitive() except: # pragma: no cover LOGGER.error( "Failed migration of auction {} to schema 1.".format( auction.id), extra={ 'MESSAGE_ID': 'migrate_data_failed', 'AUCTION_ID': auction.id }) else: auction['dateModified'] = get_now().isoformat() docs.append(auction) if len(docs) >= 2**7: # pragma: no cover registry.db.update(docs) docs = [] if docs: registry.db.update(docs)
def add_next_awards(request, reverse=False, awarding_criteria_key='amount', regenerate_all_awards=False, lot_id=None): """Adding next award. :param request: The pyramid request object. :param reverse: Is used for sorting bids to generate award. By default (reverse = False) awards are generated from lower to higher by value.amount When reverse is set to True awards are generated from higher to lower by value.amount """ tender = request.validated['tender'] max_awards = tender['maxAwardsCount'] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != 'active': continue lot_awards = [ award for award in tender.awards if award.lotID == lot.id ] lot_awards_statuses = [ award['status'] for award in tender.awards if award.lotID == lot.id ] set_lot_awards_statuses = set(lot_awards_statuses) if lot_awards_statuses and set_lot_awards_statuses.issubset( {'pending', 'active'}): statuses.union(set_lot_awards_statuses) continue lot_items = [i.id for i in tender.items if i.relatedLot == lot.id] features = [ i for i in (tender.features or []) if i.featureOf == 'tenderer' or i.featureOf == 'lot' and i.relatedItem == lot.id or i.featureOf == 'item' and i.relatedItem in lot_items ] codes = [i.code for i in features] bids = [{ 'id': bid.id, 'value': [i for i in bid.lotValues if lot.id == i.relatedLot][0].value.serialize(), 'tenderers': bid.tenderers, 'parameters': [i for i in bid.parameters if i.code in codes], 'date': [i for i in bid.lotValues if lot.id == i.relatedLot][0].date } for bid in tender.bids if bid.status == "active" and lot.id in [ i.relatedLot for i in bid.lotValues if getattr(i, 'status', "active") == "active" ]] if not bids: lot.status = 'unsuccessful' statuses.add('unsuccessful') continue cancelled_awards = None if not regenerate_all_awards and lot.id == lot_id: cancelled_awards = [award.bid_id for award in lot_awards if \ award.status == 'cancelled' and request.context.id == award.id] unsuccessful_awards = [ i.bid_id for i in lot_awards if i.status == 'unsuccessful' ] bids = [bid for bid in bids if bid['id'] == cancelled_awards[0] ] if cancelled_awards else bids bids = chef(bids, features, unsuccessful_awards, reverse, awarding_criteria_key) bids = bids[:max_awards] if max_awards else bids active_awards = [ a.bid_id for a in tender.awards if a.status in ('active', 'pending') and a.lotID == lot.id ] bids = [bid for bid in bids if bid['id'] not in active_awards] if bids: for bid in bids: award = tender.__class__.awards.model_class({ 'bid_id': bid['id'], 'lotID': lot.id, 'status': 'pending', 'date': get_now(), 'value': bid['value'], 'suppliers': bid['tenderers'] }) award.__parent__ = tender tender.awards.append(award) statuses.add('pending') else: statuses.add('unsuccessful') if statuses.difference(set(['unsuccessful', 'active' ])): # logic for auction to switch status tender.awardPeriod.endDate = None tender.status = 'active.qualification' else: if not tender.awards or request.context.status in ('cancelled', 'unsuccessful'): codes = [i.code for i in tender.features or []] active_bids = [{ 'id': bid.id, 'value': bid.value.serialize(), 'tenderers': bid.tenderers, 'parameters': [i for i in bid.parameters if i.code in codes], 'date': bid.date } for bid in tender.bids if bid.status == "active"] cancelled_awards = None if not regenerate_all_awards: cancelled_awards = [award.bid_id for award in tender.awards if \ award.status == 'cancelled' and request.context.id == award.id] unsuccessful_awards = [ i.bid_id for i in tender.awards if i.status == 'unsuccessful' ] bids = chef(active_bids, tender.features or [], unsuccessful_awards, reverse, awarding_criteria_key) bids = [bid for bid in bids if bid['id'] == cancelled_awards[0] ] if cancelled_awards else bids bids = bids[:max_awards] if max_awards else bids active_awards = [ a.bid_id for a in tender.awards if a.status in ('active', 'pending') ] bids = [bid for bid in bids if bid['id'] not in active_awards] if bids: for bid in bids: award = tender.__class__.awards.model_class({ 'bid_id': bid['id'], 'status': 'pending', 'date': get_now(), 'value': bid['value'], 'suppliers': bid['tenderers'] }) award.__parent__ = tender tender.awards.append(award) if tender.awards[ -1].status == 'pending': # logic for auction to switch status tender.awardPeriod.endDate = None tender.status = 'active.qualification'
def migrate_awarding_1_0_to_awarding_2_0(auction): if auction['procurementMethodType'] not in ['dgfOtherAssets', 'dgfFinancialAssets'] \ or auction['status'] not in ['active.qualification', 'active.awarded'] \ or 'awards' not in auction: return now = get_now().isoformat() awards = auction["awards"] unique_awards = len(set([a['bid_id'] for a in awards])) if unique_awards > 2: switch_auction_to_unsuccessful(auction) else: invalidate_bids_under_threshold(auction) award = [a for a in awards if a['status'] in ['active', 'pending']][0] for bid in auction['bids']: if bid['id'] == award['bid_id'] and bid['status'] == 'invalid': switch_auction_to_unsuccessful(auction) if auction['status'] != 'unsuccessful': award = [a for a in awards if a['status'] in ['active', 'pending']][0] award_create_date = award['complaintPeriod']['startDate'] award.update({ 'verificationPeriod': { 'startDate': award_create_date, 'endDate': award_create_date }, 'paymentPeriod': { 'startDate': award_create_date, }, 'signingPeriod': { 'startDate': award_create_date, } }) if award['status'] == 'pending': award['status'] = 'pending.payment' elif award['status'] == 'active': award['verificationPeriod']['endDate'] = award['paymentPeriod'][ 'endDate'] = now if unique_awards == 1: bid = chef(auction['bids'], auction.get('features'), [], True)[1] award = { 'id': uuid4().hex, 'bid_id': bid['id'], 'status': 'pending.waiting', 'date': awards[0]['date'], 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': awards[0]['date'] } } if bid['status'] == 'invalid': award['status'] = 'unsuccessful' award['complaintPeriod']['endDate'] = now awards.append(award)
def add_next_awards(request, reverse=False, awarding_criteria_key="amount", regenerate_all_awards=False, lot_id=None): """Adding next award. :param request: The pyramid request object. :param reverse: Is used for sorting bids to generate award. By default (reverse = False) awards are generated from lower to higher by value.amount When reverse is set to True awards are generated from higher to lower by value.amount """ tender = request.validated["tender"] max_awards = tender["maxAwardsCount"] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != "active": continue lot_awards = [ award for award in tender.awards if award.lotID == lot.id ] lot_awards_statuses = [ award["status"] for award in tender.awards if award.lotID == lot.id ] set_lot_awards_statuses = set(lot_awards_statuses) if lot_awards_statuses and set_lot_awards_statuses.issubset( {"pending", "active"}): statuses.union(set_lot_awards_statuses) continue lot_items = [i.id for i in tender.items if i.relatedLot == lot.id] features = [ i for i in (tender.features or []) if i.featureOf == "tenderer" or i.featureOf == "lot" and i.relatedItem == lot.id or i.featureOf == "item" and i.relatedItem in lot_items ] codes = [i.code for i in features] bids = [{ "id": bid.id, "value": [i for i in bid.lotValues if lot.id == i.relatedLot][0].value.serialize(), "tenderers": bid.tenderers, "parameters": [i for i in bid.parameters if i.code in codes], "date": [i for i in bid.lotValues if lot.id == i.relatedLot][0].date, } for bid in tender.bids if bid.status == "active" and lot.id in [ i.relatedLot for i in bid.lotValues if getattr(i, "status", "active") == "active" ]] if not bids: lot.status = "unsuccessful" statuses.add("unsuccessful") continue cancelled_awards = None if not regenerate_all_awards and lot.id == lot_id: cancelled_awards = [ award.bid_id for award in lot_awards if award.status == "cancelled" and request.context.id == award.id ] unsuccessful_awards = [ i.bid_id for i in lot_awards if i.status == "unsuccessful" ] bids = [bid for bid in bids if bid["id"] == cancelled_awards[0] ] if cancelled_awards else bids bids = chef(bids, features, unsuccessful_awards, reverse, awarding_criteria_key) bids = bids[:max_awards] if max_awards else bids active_awards = [ a.bid_id for a in tender.awards if a.status in ("active", "pending") and a.lotID == lot.id ] bids = [bid for bid in bids if bid["id"] not in active_awards] if bids: for bid in bids: award = tender.__class__.awards.model_class({ "bid_id": bid["id"], "lotID": lot.id, "status": "pending", "date": get_now(), "value": bid["value"], "suppliers": bid["tenderers"], }) award.__parent__ = tender tender.awards.append(award) statuses.add("pending") else: statuses.add("unsuccessful") if (statuses.difference(set(["unsuccessful", "active"])) and any([i for i in tender.lots])): # logic for auction to switch status tender.awardPeriod.endDate = None tender.status = "active.qualification" else: # pragma: no cover if not tender.awards or request.context.status in ("cancelled", "unsuccessful"): codes = [i.code for i in tender.features or []] active_bids = [{ "id": bid.id, "value": bid.value.serialize(), "tenderers": bid.tenderers, "parameters": [i for i in bid.parameters if i.code in codes], "date": bid.date, } for bid in tender.bids if bid.status == "active"] cancelled_awards = None if not regenerate_all_awards: cancelled_awards = [ award.bid_id for award in tender.awards if award.status == "cancelled" and request.context.id == award.id ] unsuccessful_awards = [ i.bid_id for i in tender.awards if i.status == "unsuccessful" ] bids = chef(active_bids, tender.features or [], unsuccessful_awards, reverse, awarding_criteria_key) bids = [bid for bid in bids if bid["id"] == cancelled_awards[0] ] if cancelled_awards else bids bids = bids[:max_awards] if max_awards else bids active_awards = [ a.bid_id for a in tender.awards if a.status in ("active", "pending") ] bids = [bid for bid in bids if bid["id"] not in active_awards] if bids: for bid in bids: award = tender.__class__.awards.model_class({ "bid_id": bid["id"], "status": "pending", "date": get_now(), "value": bid["value"], "suppliers": bid["tenderers"], }) award.__parent__ = tender tender.awards.append(award) if tender.awards[ -1].status == "pending": # logic for auction to switch status tender.awardPeriod.endDate = None tender.status = "active.qualification"
def add_next_award(request, reverse=False, awarding_criteria_key='amount'): """Adding next award. :param request: The pyramid request object. :param reverse: Is used for sorting bids to generate award. By default (reverse = False) awards are generated from lower to higher by value.amount When reverse is set to True awards are generated from higher to lower by value.amount """ tender = request.validated['tender'] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != 'active': continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ['pending', 'active']: statuses.add(lot_awards[-1].status if lot_awards else 'unsuccessful') continue lot_items = [i.id for i in tender.items if i.relatedLot == lot.id] features = [ i for i in (tender.features or []) if i.featureOf == 'tenderer' or i.featureOf == 'lot' and i.relatedItem == lot.id or i.featureOf == 'item' and i.relatedItem in lot_items ] codes = [i.code for i in features] bids = [ { 'id': bid.id, 'value': [i for i in bid.lotValues if lot.id == i.relatedLot][0].value.serialize(), 'tenderers': bid.tenderers, 'parameters': [i for i in bid.parameters if i.code in codes], 'date': [i for i in bid.lotValues if lot.id == i.relatedLot][0].date } for bid in tender.bids if bid.status == "active" and lot.id in [i.relatedLot for i in bid.lotValues if getattr(i, 'status', "active") == "active"] ] if not bids: lot.status = 'unsuccessful' statuses.add('unsuccessful') continue unsuccessful_awards = [i.bid_id for i in lot_awards if i.status == 'unsuccessful'] bids = chef(bids, features, unsuccessful_awards, reverse, awarding_criteria_key) if bids: bid = bids[0] award = tender.__class__.awards.model_class({ 'bid_id': bid['id'], 'lotID': lot.id, 'status': 'pending', 'date': get_now(), 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': now.isoformat() } }) award.__parent__ = tender tender.awards.append(award) request.response.headers['Location'] = request.route_url('{}:Tender Awards'.format(tender.procurementMethodType), tender_id=tender.id, award_id=award['id']) statuses.add('pending') else: statuses.add('unsuccessful') if statuses.difference(set(['unsuccessful', 'active'])): tender.awardPeriod.endDate = None tender.status = 'active.qualification' else: tender.awardPeriod.endDate = now tender.status = 'active.awarded' else: if not tender.awards or tender.awards[-1].status not in ['pending', 'active']: unsuccessful_awards = [i.bid_id for i in tender.awards if i.status == 'unsuccessful'] codes = [i.code for i in tender.features or []] active_bids = [ { 'id': bid.id, 'value': bid.value.serialize(), 'tenderers': bid.tenderers, 'parameters': [i for i in bid.parameters if i.code in codes], 'date': bid.date } for bid in tender.bids if bid.status == "active" ] bids = chef(active_bids, tender.features or [], unsuccessful_awards, reverse, awarding_criteria_key) if bids: bid = bids[0] award = tender.__class__.awards.model_class({ 'bid_id': bid['id'], 'status': 'pending', 'date': get_now(), 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': get_now().isoformat() } }) award.__parent__ = tender tender.awards.append(award) request.response.headers['Location'] = request.route_url('{}:Tender Awards'.format(tender.procurementMethodType), tender_id=tender.id, award_id=award['id']) if tender.awards[-1].status == 'pending': tender.awardPeriod.endDate = None tender.status = 'active.qualification' else: tender.awardPeriod.endDate = now tender.status = 'active.awarded'