def process_single_auction(self, auction_id):
     try:
         auction = self.auctions_client.get_auction(auction_id)
     except ResourceNotFound:
         LOGGER.warning('Auction object {} not found'.format(auction_id))
     else:
         self.process_auction(auction['data'])
    def _form_auction(self, lot, auction_doc):
        # Convert assets to items
        items, documents = self._create_items_from_assets(lot.assets)

        if not items:
            self.switch_lot_status(lot.id, 'active.salable')
            self.invalidate_auction(auction_doc.id)
            return False

        api_auction_doc = self.auctions_client.get_resource_item(
            auction_doc['id']).data
        LOGGER.info('Received auction {} from CDB'.format(auction_doc['id']))

        # Add items to CDB
        patch_data = {'data': {'items': items, 'dgfID': lot.lotIdentifier}}
        message = 'Auction: {} was formed from lot: {}'.format(
            auction_doc['id'], lot.id)
        self._patch_resource_item(self.auctions_client, api_auction_doc.id,
                                  patch_data, message)

        # Add documents to CDB
        for document in documents:
            self.auctions_client.create_resource_item_subitem(
                api_auction_doc.id, {'data': document}, DOCUMENTS)
            LOGGER.info(
                'Added document with hash {} to auction id: {} item id:'
                ' {} in CDB'.format(document['hash'], auction_doc['id'],
                                    document['relatedItem']))
        return True
Exemple #3
0
 def _extract_transfer_token(self, auction_id):
     credentials = self.auctions_client.extract_credentials(
         resource_item_id=auction_id)
     LOGGER.info(
         "Successfully extracted tranfer_token from auction {})".format(
             auction_id))
     return credentials['data']['transfer_token']
 def prepare_auction(self, auction_doc):
     LOGGER.info('Prepare auction {}'.format(auction_doc.id))
     lot = self._receive_lot(auction_doc)
     if lot:
         auction_formed = self._form_auction(lot, auction_doc)
         if auction_formed:
             self._activate_auction(lot, auction_doc)
    def report_results(self, auction_doc):
        LOGGER.info('Report auction results {}'.format(auction_doc.id))

        lot_id = auction_doc.merchandisingObject

        # Get lot
        try:
            lot = self.lots_client.get_lot(lot_id).data
        except ResourceNotFound:
            LOGGER.warning(
                'Lot {} not found when report auction {} results'.format(
                    lot_id, auction_doc.id))
            return

        if lot.status != 'active.auction':
            LOGGER.info('Auction {} results already reported to lot {}'.format(
                auction_doc.id, lot_id))
            return

        LOGGER.info('Received lot {} from CDB'.format(lot_id))

        if auction_doc.status == 'complete':
            next_lot_status = 'pending.sold'
        else:
            next_lot_status = 'active.salable'

        # Report results
        try:
            self.switch_lot_status(lot['id'], next_lot_status)
        except Exception as e:
            LOGGER.error('Failed update lot info {}. {}'.format(
                lot_id, e.message))
 def _get_documents(self, item):
     if not hasattr(self.auctions_client, 'ds_client'):
         return []
     documents = []
     for doc in item.get('documents', []):
         item_document = {k: doc[k] for k in self.document_keys if k in doc}
         try:
             registered_doc = self.auctions_client.ds_client.register_document_upload(
                 doc['hash'])
             LOGGER.info('Registered document upload for item {} with hash'
                         ' {}'.format(item.id, doc['hash']))
         except:
             LOGGER.error('While registering document upload '
                          'something went wrong :(')
             continue
         transfer_item = {
             'get_url': doc.url,
             'upload_url': registered_doc['upload_url']
         }
         self.documents_transfer_queue.put(transfer_item)
         item_document['url'] = registered_doc['data']['url']
         item_document['documentOf'] = 'item'
         item_document['relatedItem'] = item.id
         documents.append(item_document)
     return documents
Exemple #7
0
 def _post_contract(self, contract_data):
     contract = self.contracts_client.create_contract(contract_data).data
     log_msg = "Successfully created contract {}".format(contract.id)
     if 'merchandisingObject' in contract_data['data']:
         log_msg += " from lot {}".format(
             contract_data['data']['merchandisingObject'])
     LOGGER.info(log_msg, extra={'MESSAGE_ID': CREATE_CONTRACT_MESSAGE_ID})
     return contract
Exemple #8
0
 def _patch_lot_contract(self, contract_data, lot_id, contract_id):
     self.lots_client.patch_resource_item_subitem(
         resource_item_id=lot_id,
         patch_data={'data': contract_data},
         subitem_name='contracts',
         subitem_id=contract_id)
     LOGGER.info('Update lot\'s {} contract data'.format(lot_id),
                 extra={'MESSAGE_ID': UPDATE_CONTRACT_MESSAGE_ID})
 def _patch_resource_item(self,
                          client,
                          resource_id,
                          patch_data,
                          message,
                          extra=None):
     resource = client.patch_resource_item(resource_id, patch_data)
     LOGGER.info(message, extra=extra)
     return resource
 def run(self):
     self.transmitter = spawn(self.file_bridge)
     sleep(1)
     LOGGER.info('Getting auctions')
     for auction in continuous_changes_feed(self.db, self.killer,
                                            self.timeout):
         self.process_auction(auction)
         if self.killer.kill_now:
             break
Exemple #11
0
    def _get_lot(self, auction_doc):
        lot_id = auction_doc.merchandisingObject
        try:
            lot = self.lots_client.get_lot(lot_id).data
        except ResourceNotFound:
            LOGGER.warning(
                'Lot {} not found when report auction {} results'.format(
                    lot_id, auction_doc.id))
            return

        LOGGER.info('Received lot {} from CDB'.format(lot_id))
        return lot
Exemple #12
0
 def _check_lot_auction(self, lot, auction_doc):
     lot_auction = next(
         (auction for auction in lot.auctions
          if auction_doc.id == auction.get('relatedProcessID')), None)
     if not lot_auction:
         LOGGER.warning('Auction object {} not found in lot {}'.format(
             auction_doc.id, lot.id))
         return
     if lot_auction['status'] != 'active':
         LOGGER.info('Auction {} results already reported to lot {}'.format(
             auction_doc.id, lot.id))
         return
     return lot_auction
Exemple #13
0
 def _switch_auction_status(self, status, lot_id, auction_id):
     self.lots_client.patch_resource_item_subitem(
         resource_item_id=lot_id,
         patch_data={'data': {
             'status': status
         }},
         subitem_name='auctions',
         subitem_id=auction_id)
     LOGGER.info('Switch lot\'s {} auction {} to ({}) status'.format(
         lot_id, auction_id, status),
                 extra={
                     'MESSAGE_ID': SWITCH_LOT_AUCTION_STATUS_MESSAGE_ID,
                     'STATUS': status
                 })
    def _receive_lot(self, auction_doc):
        lot_id = auction_doc.merchandisingObject

        # Get lot
        try:
            lot = self.lots_client.get_lot(lot_id).data
        except ResourceNotFound:
            self.invalidate_auction(auction_doc.id)
            return
        LOGGER.info('Received lot {} from CDB'.format(lot_id))
        is_lot_unusable = bool(
            (lot.status == u'active.awaiting'
             and auction_doc.id != lot.auctions[-1]) or lot.status
            not in [u'active.salable', u'active.awaiting', u'active.auction'])
        if is_lot_unusable:
            # lot['status'] = 'active.salable'
            # self.lots_client.patch_resource_item(lot)
            LOGGER.warning(
                'Lot status \'{}\' not equal \'active.salable\''.format(
                    lot.status),
                extra={'MESSAGE_ID': 'invalid_lot_status'})
            self.invalidate_auction(auction_doc.id)
            return
        elif lot.status == u'active.auction' and auction_doc.id == lot.auctions[
                -1]:
            # Switch auction
            self.switch_auction_status(auction_doc['id'], 'active.tendering')
            return
        elif lot.status == u'active.auction' and auction_doc.id != lot.auctions[
                -1]:
            self.invalidate_auction(auction_doc.id)
        elif lot.status == u'active.awaiting' and auction_doc.id == lot.auctions[
                -1]:
            return lot

        # Lock lot
        auctions_list = lot.get('auctions', [])
        auctions_list.append(auction_doc.id)
        lot_patch_data = {
            'data': {
                'status': 'active.awaiting',
                'auctions': auctions_list
            }
        }
        self._patch_resource_item(self.lots_client, lot.id, lot_patch_data,
                                  'Lock lot {}'.format(lot.id),
                                  {'MESSAGE_ID': 'lock_lot'})
        return lot
    def process_auction(self, auction):
        LOGGER.info('Received auction {} in status {}'.format(
            auction['id'], auction['status']),
                    extra={
                        'MESSAGE_ID': GET_AUCTION_MESSAGE_ID,
                        'STATUS': auction['status']
                    })

        if auction[
                'procurementMethodType'] not in self.auction_type_processing_configurator:
            LOGGER.warning(
                'Such procurementMethodType %s is not supported by this'
                ' convoy configuration' % auction['procurementMethodType'])
            return

        processing = self.auction_type_processing_configurator.get(
            auction['procurementMethodType'])
        processing.process_auction(auction)
Exemple #16
0
    def report_results(self, auction_doc):
        LOGGER.info('Report auction results {}'.format(auction_doc.id))

        lot_processing = 'merchandisingObject' in auction_doc
        contract_processing = 'contractTerms' in auction_doc

        if lot_processing:  # lot ID, that auctions sells

            lot = self._get_lot(auction_doc)  # get lot from the registry
            if not lot:
                return

            lot_auction = self._check_lot_auction(
                lot, auction_doc)  # search for the auction in the lot
            if not lot_auction:
                return

        # terminalize status of lot auction
        terminalized_status = PRE_TERMINAL_MAPPING.get(auction_doc.status,
                                                       auction_doc.status)

        # update lot's auction status with actual auction status
        if auction_doc.status in UNSUCCESSFUL_TERMINAL_STATUSES + UNSUCCESSFUL_PRE_TERMINAL_STATUSES:
            if lot_processing:
                self._switch_auction_status(terminalized_status, lot.id,
                                            lot_auction.id)
            self.auctions_mapping.put(str(auction_doc.id), True)

        elif auction_doc.status in SUCCESSFUL_TERMINAL_STATUSES + SUCCESSFUL_PRE_TERMINAL_STATUSES:
            if contract_processing and lot_processing:
                # build contract regarding obligatoriness of it's fields
                contract_data = make_contract(auction_doc)
                try:
                    contract_data[
                        'transfer_token'] = self._extract_transfer_token(
                            auction_doc['id'])
                except EXCEPTIONS as e:
                    message = 'Server error: {}'.format(
                        e.status_code) if e.status_code >= 500 else e.message
                    LOGGER.error(
                        "Failed to extract transfer token from auction {} ({})"
                        .format(auction_doc.id, message))
                    return
                # create contract, if none of them are associated with lot
                if lot.contracts[0].get('relatedProcessID') is None:
                    contract = self._post_contract({'data': contract_data})
                else:
                    LOGGER.info(
                        'Contract {} has already created, and patched to lot {}'
                        .format(lot.contracts[0].get('relatedProcessID'),
                                lot.id))
                    return
            if lot_processing:
                # update lot's auction status with actual auction status
                self._switch_auction_status(terminalized_status, lot.id,
                                            lot_auction.id)
            if lot_processing and contract_processing:
                self.update_lot_contract(lot, contract)
            self.auctions_mapping.put(str(auction_doc.id), True)
    def __init__(self, convoy_conf):
        LOGGER.info('Init Convoy...')
        self.auction_type_processing_configurator = {}
        self.auction_types_for_filter = {}
        self.convoy_conf = convoy_conf
        self.killer = GracefulKiller()

        self.stop_transmitting = False

        self.transmitter_timeout = self.convoy_conf.get(
            'transmitter_timeout', 10)

        created_clients = init_clients(convoy_conf)

        for key, item in created_clients.items():
            setattr(self, key, item)
        self.documents_transfer_queue = Queue()
        self.timeout = self.convoy_conf.get('timeout', 10)
        self.keys = KEYS
        self.document_keys = DOCUMENT_KEYS

        if convoy_conf['lots'].get('loki'):

            process_loki = ProcessingLoki(convoy_conf['lots']['loki'],
                                          created_clients, self.keys,
                                          self.document_keys,
                                          self.documents_transfer_queue)
            self._register_aliases(process_loki, 'loki')
        if convoy_conf['lots'].get('basic'):
            process_basic = ProcessingBasic(convoy_conf['lots']['basic'],
                                            created_clients, self.keys,
                                            self.document_keys,
                                            self.documents_transfer_queue)
            self._register_aliases(process_basic, 'basic')

        push_filter_doc(self.db, self.auction_types_for_filter)
    def _create_items_from_assets(self, assets_ids):
        items = []
        documents = []
        for index, asset_id in enumerate(assets_ids):
            asset = self.assets_client.get_asset(asset_id).data
            LOGGER.info('Received asset {} with status {}'.format(
                asset.id, asset.status))

            # Convert asset to item
            item = {k: asset[k] for k in self.keys if k in asset}
            item['description'] = asset.title
            items.append(item)

            # Get documents from asset
            for doc in self._get_documents(asset):
                documents.append(doc)

            # Get items and items documents from complex asset
            for item in asset.get('items', []):
                items.append(item)
                for doc in self._get_documents(item):
                    documents.append(doc)

        return items, documents
 def file_bridge(self):
     while not self.stop_transmitting:
         try:
             transfer_item = self.documents_transfer_queue.get(timeout=2)
             try:
                 file_, _ = self.auctions_client.get_file(
                     transfer_item['get_url'])
                 LOGGER.debug('Received document file from asset DS')
                 # TODO: Fill headers valid data if needed
                 headers = {}
                 self.auctions_client.ds_client.document_upload_not_register(
                     file_, headers)
                 LOGGER.debug('Uploaded document file to auction DS')
             except Exception:
                 LOGGER.error('While receiving or uploading document '
                              'something went wrong :(')
                 self.documents_transfer_queue.put(transfer_item)
                 sleep(1)
                 continue
         except Empty:
             sleep(self.transmitter_timeout)