Beispiel #1
0
    def add_sellers(self,
                    data: dict,
                    sale_type,
                    transaction,
                    sellers,
                    rel,
                    source=None):
        hmo = get_crom_object(data)
        parent = data['parent_data']
        auction_data = parent['auction_of_lot']
        lot_object_key = object_key(auction_data)
        cno, lno, date = lot_object_key
        lot = get_crom_object(parent['_event_causing_prov_entry'])
        ts = getattr(lot, 'timespan', None)

        prev_procurements = []
        tx_label_args = tuple([self.helper, sale_type, 'Sold', rel] +
                              list(lot_object_key))
        for i, seller_data in enumerate(sellers):
            seller = get_crom_object(seller_data)
            tx_uri = hmo.id + f'-seller-{i}-Prov'
            tx, acq = self.related_procurement(hmo,
                                               tx_label_args,
                                               current_ts=ts,
                                               buyer=seller,
                                               previous=True,
                                               ident=tx_uri,
                                               make_label=prov_entry_label)
            self.attach_source_catalog(data, acq, [seller_data])
            if source:
                tx.referred_to_by = source
            prev_procurements.append(add_crom_data(data={}, what=tx))
        data['_prov_entries'] += prev_procurements
        return prev_procurements
Beispiel #2
0
    def add_transfer_of_custody(self,
                                data,
                                current_tx,
                                xfer_to,
                                xfer_from,
                                buy_sell_modifiers,
                                sequence=1,
                                purpose=None):
        THROUGH = CaseFoldingSet(buy_sell_modifiers['through'])
        FOR = CaseFoldingSet(buy_sell_modifiers['for'])

        buyers = xfer_to
        sellers = xfer_from
        hmo = get_crom_object(data)
        parent = data['parent_data']
        auction_data = parent['auction_of_lot']
        cno, lno, date = object_key(auction_data)

        xfer_label = None
        purpose_label = f'(for {purpose}) ' if purpose else ''
        try:
            object_label = f'“{hmo._label}”'
            xfer_label = f'Transfer of Custody {purpose_label}of {cno} {lno} ({date}): {object_label}'
        except AttributeError:
            object_label = '(object)'
            xfer_label = f'Transfer of Custody {purpose_label}of {cno} {lno} ({date})'

        # TODO: pass in the tx to use instead of getting it from `parent`
        tx_data = parent.get('_prov_entry_data')
        current_tx = get_crom_object(tx_data)

        # The custody transfer sequence is specific to a single transaction. Therefore,
        # the custody transfer URI must not share a prefix with the object URI, otherwise
        # all such custody transfers are liable to be merged during URI reconciliation as
        # part of the prev/post sale rewriting.
        xfer_id = self.helper.prepend_uri_key(hmo.id,
                                              f'CustodyTransfer,{sequence}')
        xfer = model.TransferOfCustody(ident=xfer_id, label=xfer_label)
        xfer.transferred_custody_of = hmo
        if purpose in self.custody_xfer_purposes:
            xfer.general_purpose = self.custody_xfer_purposes[purpose]

        for seller_data in sellers:
            seller = get_crom_object(seller_data)
            mods = self.modifiers(seller_data, 'auth_mod_a')
            if not THROUGH.intersects(mods):
                xfer.transferred_custody_from = seller

        for buyer_data in buyers:
            buyer = get_crom_object(buyer_data)
            mods = self.modifiers(buyer_data, 'auth_mod_a')
            if not THROUGH.intersects(mods):
                xfer.transferred_custody_to = buyer

        current_tx.part = xfer
Beispiel #3
0
 def set_lot_notes(self, lot, auction_data, sale_type):
     '''Associate notes with the auction lot.'''
     cno, lno, _ = object_key(auction_data)
     notes = auction_data.get('lot_notes')
     if notes:
         note_id = lot.id + '-Notes'
         lot.referred_to_by = vocab.Note(ident=note_id, content=notes)
     if not lno:
         warnings.warn(f'Setting empty identifier on {lot.id}')
     lno = str(lno)
     lot.identified_by = vocab.LotNumber(ident='', content=lno)
Beispiel #4
0
 def set_lot_notes(self, lot, auction_data, sale_type):
     '''Associate notes with the auction lot.'''
     cno, lno, _ = object_key(auction_data)
     notes = auction_data.get('lot_notes')
     if notes:
         # In case the lot is reconciled with another lot, notes should not be merged.
         # Therefore, the note URI must not share a prefix with the lot URI, otherwise
         # all notes are liable to be merged during URI reconciliation.
         note_uri = self.helper.prepend_uri_key(lot.id, 'NOTE')
         lot.referred_to_by = vocab.Note(ident=note_uri, content=notes)
     if not lno:
         warnings.warn(f'Setting empty identifier on {lot.id}')
     lno = str(lno)
     lot.identified_by = vocab.LotNumber(ident='', content=lno)
Beispiel #5
0
    def add_private_sellers(self, data: dict, sellers, sale_type, transaction,
                            transaction_types):
        parent = data['parent_data']
        auction_data = parent['auction_of_lot']
        cno, lno, date = object_key(auction_data)

        own_info_source = f'Listed as the seller of object in {cno} {lno} ({date}) that was privately sold'
        note = vocab.SourceStatement(ident='', content=own_info_source)
        rel = 'leading to the previous ownership of'
        return self.add_sellers(data,
                                sale_type,
                                transaction,
                                sellers,
                                rel,
                                source=note)
Beispiel #6
0
    def add_transfer_of_custody(self,
                                data,
                                current_tx,
                                xfer_to,
                                xfer_from,
                                sequence=1,
                                purpose=None):
        buyers = xfer_to
        sellers = xfer_from
        hmo = get_crom_object(data)
        parent = data['parent_data']
        auction_data = parent['auction_of_lot']
        cno, lno, date = object_key(auction_data)

        xfer_label = None
        purpose_label = f'(for {purpose}) ' if purpose else ''
        try:
            object_label = f'“{hmo._label}”'
            xfer_label = f'Transfer of Custody {purpose_label}of {cno} {lno} ({date}): {object_label}'
        except AttributeError:
            object_label = '(object)'
            xfer_label = f'Transfer of Custody {purpose_label}of {cno} {lno} ({date})'

        # TODO: pass in the tx to use instead of getting it from `parent`
        tx_data = parent['_prov_entry_data']
        current_tx = get_crom_object(tx_data)

        xfer_id = hmo.id + f'-CustodyTransfer-{sequence}'
        xfer = model.TransferOfCustody(ident=xfer_id, label=xfer_label)
        xfer.transferred_custody_of = hmo
        if purpose in self.custody_xfer_purposes:
            xfer.general_purpose = self.custody_xfer_purposes[purpose]

        for seller_data in sellers:
            seller = get_crom_object(seller_data)
            xfer.transferred_custody_from = seller

        for buyer_data in buyers:
            buyer = get_crom_object(buyer_data)
            xfer.transferred_custody_to = buyer

        current_tx.part = xfer
Beispiel #7
0
    def add_sellers(self,
                    data: dict,
                    sale_type,
                    transaction,
                    sellers,
                    rel,
                    source=None):
        hmo = get_crom_object(data)
        parent = data['parent_data']
        auction_data = parent['auction_of_lot']
        lot_object_key = object_key(auction_data)
        cno, lno, date = lot_object_key
        lot = get_crom_object(parent.get('_event_causing_prov_entry'))
        ts = getattr(lot, 'timespan', None)

        prev_procurements = []
        tx_label_args = tuple([self.helper, sale_type, 'Sold', rel] +
                              list(lot_object_key))
        for i, seller_data in enumerate(sellers):
            seller = get_crom_object(seller_data)
            # The provenance entry for a seller's previous acquisition is specific to a
            # single transaction. Therefore, the provenance entry URI must not share a
            # prefix with the object URI, otherwise all such provenance entries are liable
            # to be merged during URI reconciliation as part of the prev/post sale rewriting.
            tx_uri = self.helper.prepend_uri_key(hmo.id, f'PROV,Seller-{i}')
            tx, acq = self.related_procurement(hmo,
                                               tx_label_args,
                                               current_ts=ts,
                                               buyer=seller,
                                               previous=True,
                                               ident=tx_uri,
                                               make_label=prov_entry_label)
            self.attach_source_catalog(data, acq, [seller_data])
            if source:
                tx.referred_to_by = source
            prev_procurements.append(add_crom_data(data={}, what=tx))
        data['_prov_entries'] += prev_procurements
        return prev_procurements
Beispiel #8
0
    def __call__(self, data: dict, non_auctions, event_properties,
                 buy_sell_modifiers, transaction_types):
        '''Determine if this record has an acquisition or bidding, and add appropriate modeling'''
        parent = data['parent_data']

        auction_houses_data = event_properties['auction_houses']
        event_experts = event_properties['experts']
        event_commissaires = event_properties['commissaire']

        sales_record = get_crom_object(data['_record'])
        transaction = parent['transaction']
        transaction = transaction.replace('[?]', '').rstrip()
        auction_data = parent['auction_of_lot']
        cno, lno, date = object_key(auction_data)
        shared_lot_number = self.helper.shared_lot_number_from_lno(lno)
        buyers = [
            self.add_person(self.helper.copy_source_information(p, parent),
                            record=sales_record,
                            relative_id=f'buyer_{i+1}',
                            catalog_number=cno)
            for i, p in enumerate(parent['buyer'])
        ]

        data.setdefault('_prov_entries', [])
        data.setdefault('_other_owners', [])

        sellers = [
            self.add_person(self.helper.copy_source_information(p, parent),
                            record=sales_record,
                            relative_id=f'seller_{i+1}',
                            catalog_number=cno)
            for i, p in enumerate(parent['seller'])
        ]
        all_mods = {
            m.lower().strip()
            for a in sellers for m in a.get('auth_mod_a', '').split(';')
        } - {''}
        seller_group = (all_mods == {
            'or'
        })  # the seller is *one* of the named people, model as a group

        orig_sellers = sellers
        if seller_group:
            tx_data = parent.get('_prov_entry_data')
            if tx_data:  # if there is a prov entry (e.g. was not withdrawn)
                current_tx = get_crom_object(tx_data)
                # The seller group URI is just the provenance entry URI with a suffix.
                # In any case where the provenance entry is merged, the seller group
                # should be merged as well.
                group_uri = current_tx.id + '-SellerGroup'
                group_data = {
                    'uri': group_uri,
                }
            else:
                pi_record_no = data['pi_record_no']
                group_uri_key = ('GROUP', 'PI', pi_record_no, 'SellerGroup')
                group_uri = self.helper.make_proj_uri(*group_uri_key)
                group_data = {
                    'uri_keys': group_uri_key,
                    'uri': group_uri,
                }
            g_label = f'Group containing the seller of {object_key_string(cno, lno, date)}'
            g = vocab.UncertainMemberClosedGroup(ident=group_uri,
                                                 label=g_label)
            for seller_data in sellers:
                seller = get_crom_object(seller_data)
                seller.member_of = g
                data['_other_owners'].append(seller_data)
            sellers = [add_crom_data({}, g)]

        SOLD = transaction_types['sold']
        UNSOLD = transaction_types['unsold']
        UNKNOWN = transaction_types['unknown']

        sale_type = non_auctions.get(cno, 'Auction')
        data.setdefault('_owner_locations', [])
        if transaction in SOLD:
            houses = [
                self.helper.add_auction_house_data(h)
                for h in auction_houses_data.get(cno, [])
            ]
            for data, current_tx in self.add_acquisition(
                    data, buyers, sellers, houses, non_auctions,
                    buy_sell_modifiers, transaction, transaction_types):
                acq = get_crom_object(data['_acquisition'])
                self.add_mod_notes(acq, all_mods, label=f'Seller modifier')
                experts = event_experts.get(cno, [])
                commissaires = event_commissaires.get(cno, [])
                custody_recievers = houses + [
                    add_crom_data(data={}, what=r)
                    for r in experts + commissaires
                ]

                if sale_type in ('Auction', 'Collection Catalog'):
                    # 'Collection Catalog' is treated just like an Auction
                    self.add_transfer_of_custody(
                        data,
                        current_tx,
                        xfer_to=custody_recievers,
                        xfer_from=sellers,
                        buy_sell_modifiers=buy_sell_modifiers,
                        sequence=1,
                        purpose='selling')
                    self.add_transfer_of_custody(
                        data,
                        current_tx,
                        xfer_to=buyers,
                        xfer_from=custody_recievers,
                        buy_sell_modifiers=buy_sell_modifiers,
                        sequence=2,
                        purpose='completing sale')
                elif sale_type in ('Private Contract Sale', 'Stock List'):
                    # 'Stock List' is treated just like a Private Contract Sale, except for the catalogs
                    metadata = {
                        'pi_record_no': parent['pi_record_no'],
                        'catalog_number': cno
                    }
                    for i, h in enumerate(custody_recievers):
                        house = get_crom_object(h)
                        if hasattr(house, 'label'):
                            house._label = f'{house._label}, private sale organizer for {cno} {shared_lot_number} ({date})'
                        else:
                            house._label = f'Private sale organizer for {cno} {shared_lot_number} ({date})'
                        data['_organizations'].append(h)

                    self.add_transfer_of_custody(
                        data,
                        current_tx,
                        xfer_to=custody_recievers,
                        xfer_from=sellers,
                        buy_sell_modifiers=buy_sell_modifiers,
                        sequence=1,
                        purpose='selling')
                    self.add_transfer_of_custody(
                        data,
                        current_tx,
                        xfer_to=buyers,
                        xfer_from=custody_recievers,
                        buy_sell_modifiers=buy_sell_modifiers,
                        sequence=2,
                        purpose='completing sale')

                    prev_procurements = self.add_private_sellers(
                        data, sellers, sale_type, transaction,
                        transaction_types)
                else:
                    warnings.warn(
                        f'*** not modeling transfer of custody for auction type {transaction}'
                    )
                yield data
        elif transaction in UNSOLD:
            houses = [
                self.helper.add_auction_house_data(h)
                for h in auction_houses_data.get(cno, [])
            ]
            experts = event_experts.get(cno, [])
            commissaires = event_commissaires.get(cno, [])
            custody_recievers = houses + [
                add_crom_data(data={}, what=r) for r in experts + commissaires
            ]
            bid_count = 0
            for data in self.add_bidding(data, buyers, sellers,
                                         buy_sell_modifiers, sale_type,
                                         transaction, transaction_types,
                                         custody_recievers):
                bid_count += 1
                act = get_crom_object(data.get('_bidding'))
                self.add_mod_notes(act, all_mods, label=f'Seller modifier')
                yield data
            if not bid_count:
                # there was no bidding, but we still want to model the seller(s) as
                # having previously acquired the object.
                prev_procurements = self.add_non_sale_sellers(
                    data, sellers, sale_type, transaction, transaction_types)
                yield data
        elif transaction in UNKNOWN:
            if sale_type == 'Lottery':
                yield data
            else:
                houses = [
                    self.helper.add_auction_house_data(h)
                    for h in auction_houses_data.get(cno, [])
                ]
                for data in self.add_bidding(data, buyers, sellers,
                                             buy_sell_modifiers, sale_type,
                                             transaction, transaction_types,
                                             houses):
                    act = get_crom_object(data.get('_bidding'))
                    self.add_mod_notes(act, all_mods, label=f'Seller modifier')
                    yield data
        else:
            prev_procurements = self.add_non_sale_sellers(
                data, sellers, sale_type, transaction, transaction_types)
            lot = get_crom_object(parent['_event_causing_prov_entry'])
            for tx_data in prev_procurements:
                tx = get_crom_object(tx_data)
                lot.starts_after_the_end_of = tx
            warnings.warn(
                f'Cannot create acquisition data for unrecognized transaction type: {transaction!r}'
            )
            yield data
Beispiel #9
0
    def add_bidding(self, data: dict, buyers, sellers, buy_sell_modifiers,
                    sale_type, transaction, transaction_types,
                    auction_houses_data):
        '''Add modeling of bids that did not lead to an acquisition'''
        hmo = get_crom_object(data)
        parent = data['parent_data']
        data['seller'] = sellers
        auction_data = parent['auction_of_lot']
        lot_object_key = object_key(auction_data)
        cno, lno, date = lot_object_key
        lot_data = parent.get('_event_causing_prov_entry')
        if not lot_data:
            return
        lot = get_crom_object(lot_data)
        if not lot:
            return
        ts = getattr(lot, 'timespan', None)

        UNSOLD = transaction_types['unsold']
        model_custody_return = transaction in UNSOLD
        prev_procurements = self.add_non_sale_sellers(data, sellers, sale_type,
                                                      transaction,
                                                      transaction_types)

        prices = parent.get('price', [])
        if not prices:
            yield data
# 		amnts = [get_crom_object(p) for p in prices]

        tx_data = parent.get('_prov_entry_data')
        tx = get_crom_object(tx_data)
        houses = auction_houses_data
        self.add_transfer_of_custody(data,
                                     tx,
                                     xfer_to=houses,
                                     xfer_from=sellers,
                                     buy_sell_modifiers=buy_sell_modifiers,
                                     sequence=1,
                                     purpose='selling')
        if model_custody_return:
            self.add_transfer_of_custody(data,
                                         tx,
                                         xfer_to=sellers,
                                         xfer_from=houses,
                                         buy_sell_modifiers=buy_sell_modifiers,
                                         sequence=2,
                                         purpose='returning')

        data.setdefault('_prov_entries', [])
        data['_prov_entries'].append(tx_data)

        self.add_prev_post_owners(data, hmo, tx_data, sale_type,
                                  lot_object_key, ts)

        if prices:
            # The bidding for an object is specific to a single transaction. Therefore,
            # the bidding URI must not share a prefix with the object URI, otherwise all
            # such bidding entries are liable to be merged during URI reconciliation as
            # part of the prev/post sale rewriting.
            bidding_id = self.helper.prepend_uri_key(hmo.id, 'BID')
            all_bids_label = f'Bidding on {cno} {lno} ({date})'
            all_bids = model.Activity(ident=bidding_id, label=all_bids_label)
            all_bids.identified_by = model.Name(ident='',
                                                content=all_bids_label)
            for tx_data in prev_procurements:
                tx = get_crom_object(tx_data)
                all_bids.starts_after_the_end_of = tx

            all_bids.part_of = lot

            THROUGH = CaseFoldingSet(buy_sell_modifiers['through'])
            FOR = CaseFoldingSet(buy_sell_modifiers['for'])

            for seq_no, amnt_data in enumerate(prices):
                amnt = get_crom_object(amnt_data)
                # The individual bid and promise URIs are just the bidding URI with a suffix.
                # In any case where the bidding is merged, the bids and promises should be
                # merged as well.
                bid_id = bidding_id + f'-Bid-{seq_no}'
                bid = vocab.Bidding(ident=bid_id)
                prop_id = bidding_id + f'-Bid-{seq_no}-Promise'
                try:
                    amnt_label = amnt._label
                    bid_label = f'Bid of {amnt_label} on {cno} {lno} ({date})'
                    prop = model.PropositionalObject(
                        ident=prop_id, label=f'Promise to pay {amnt_label}')
                except AttributeError:
                    bid_label = f'Bid on {cno} {lno} ({date})'
                    prop = model.PropositionalObject(ident=prop_id,
                                                     label=f'Promise to pay')

                bid._label = bid_label
                bid.identified_by = model.Name(ident='', content=bid_label)
                self.set_possible_attribute(prop, 'refers_to', amnt_data)
                prop.refers_to = amnt
                bid.created = prop

                # TODO: there are often no buyers listed for non-sold records.
                #       should we construct an anonymous person to carry out the bid?
                for buyer_data in buyers:
                    buyer = get_crom_object(buyer_data)
                    mod = self.modifiers(buyer_data, 'auth_mod_a')
                    if THROUGH.intersects(mod):
                        bid.carried_out_by = buyer
                    elif FOR.intersects(mod):
                        warnings.warn(
                            f'buyer modifier {mod} for non-sale bidding: {cno} {lno} {date}'
                        )
                    else:
                        bid.carried_out_by = buyer

                all_bids.part = bid

            final_owner_data = data.get('_final_org')
            if final_owner_data:
                data['_organizations'].append(final_owner_data)
                final_owner = get_crom_object(final_owner_data)
                hmo = get_crom_object(data)
                tx_label_args = tuple([
                    self.helper, sale_type, 'Sold',
                    'leading to the currently known location of'
                ] + list(lot_object_key))
                tx, acq = self.final_owner_prov_entry(tx_label_args,
                                                      final_owner, None, hmo,
                                                      ts)
                note = final_owner_data.get('note')
                if note:
                    acq.referred_to_by = vocab.Note(ident='', content=note)
                data['_prov_entries'].append(add_crom_data(data={}, what=tx))

            data['_bidding'] = {'uri': bidding_id}
            add_crom_data(data=data['_bidding'], what=all_bids)
            yield data
        else:
            warnings.warn(
                f'*** No price data found for {parent["transaction"]!r} transaction'
            )
            yield data
Beispiel #10
0
    def add_acquisition(self, data, buyers, sellers, houses, non_auctions,
                        buy_sell_modifiers, transaction, transaction_types):
        '''Add modeling of an acquisition as a transfer of title from the seller to the buyer'''
        hmo = get_crom_object(data)
        parent = data['parent_data']
        # 	transaction = parent['transaction']
        prices = parent.get('price')
        ask_price = parent.get('ask_price')
        auction_data = parent['auction_of_lot']
        lot_object_key = object_key(auction_data)
        cno, lno, date = lot_object_key
        sale_type = non_auctions.get(cno, 'Auction')
        data['buyer'] = buyers
        data['seller'] = sellers

        acq_label = None
        try:
            object_label = f'“{hmo._label}”'
            acq_label = f'Acquisition of {cno} {lno} ({date}): {object_label}'
        except AttributeError:
            object_label = '(object)'
            acq_label = f'Acquisition of {cno} {lno} ({date})'

    # 	if not prices:
    # 		print(f'*** No price data found for {transaction} transaction')

        tx_data = parent.get('_prov_entry_data')
        current_tx = get_crom_object(tx_data)

        # The payment URIs are just the provenance entry URI with a suffix. In any case
        # where the provenance entry is merged, the payment should be merged as well.
        sell_payment_id = current_tx.id + '-Pay-to-Seller'
        buy_payment_id = current_tx.id + '-Pay-from-Buyer'

        # The acquisition URI is just the provenance entry URI with a suffix. In any case
        # where the provenance entry is merged, the acquisition should be merged as well.
        acq_id = hmo.id + '-Acq'
        acq = model.Acquisition(ident=acq_id, label=acq_label)
        acq.transferred_title_of = hmo

        self.attach_source_catalog(data, acq, buyers + sellers)

        multi = tx_data.get('multi_lot_tx')
        paym_label = f'multiple lots {multi}' if multi else object_label
        # 		paym = model.Payment(ident=payment_id, label=f'Payment for {paym_label}')
        payments = {
            'buy':
            model.Payment(ident=buy_payment_id,
                          label=f'Payment from buyer for {paym_label}'),
            'sell':
            model.Payment(ident=sell_payment_id,
                          label=f'Payment to seller for {paym_label}'),
        }
        for house_data in houses:
            house = get_crom_object(house_data)
            payments['buy'].paid_to = house
            payments['sell'].paid_from = house
        payments_used = set()

        THROUGH = CaseFoldingSet(buy_sell_modifiers['through'])
        FOR = CaseFoldingSet(buy_sell_modifiers['for'])

        single_seller = (len(sellers) == 1)
        single_buyer = (len(buyers) == 1)

        pi = self.helper.person_identity

        def is_or_anon(data: dict):
            mods = self.modifiers(data, 'auth_mod_a')
            return 'or anonymous' in mods

        or_anon_records = [is_or_anon(a) for a in sellers]
        uncertain_attribution = any(or_anon_records)

        for seq_no, seller_data in enumerate(sellers):
            seller = get_crom_object(seller_data)
            mod = self.modifiers(seller_data, 'auth_mod_a')
            attrib_assignment_classes = [model.AttributeAssignment]
            if uncertain_attribution:
                attrib_assignment_classes.append(vocab.PossibleAssignment)

            if THROUGH.intersects(mod):
                acq.carried_out_by = seller
                payments['sell'].carried_out_by = seller
                payments_used.add('sell')
            elif FOR.intersects(mod):
                acq.transferred_title_from = seller
                payments['sell'].paid_to = seller
                payments_used.add('sell')
            elif uncertain_attribution:  # this is true if ANY of the sellers have an 'or anonymous' modifier
                # The assignment URIs are just the acquisition URI with a suffix.
                # In any case where the acquisition is merged, the assignments should be
                # merged as well.
                acq_assignment_uri = acq.id + f'-seller-assignment-{seq_no}'
                paym_assignment_uri = payments[
                    'sell'].id + f'-seller-assignment-{seq_no}'

                acq_assignment_label = f'Uncertain seller as previous title holder in acquisition'
                acq_assignment = vocab.PossibleAssignment(
                    ident=acq_assignment_uri, label=acq_assignment_label)
                acq_assignment.referred_to_by = vocab.Note(
                    ident='', content=acq_assignment_label)
                acq_assignment.assigned_property = 'transferred_title_from'
                acq_assignment.assigned = seller
                acq.attributed_by = acq_assignment

                paym_assignment_label = f'Uncertain seller as recipient of payment'
                paym_assignment = vocab.PossibleAssignment(
                    ident=paym_assignment_uri, label=paym_assignment_label)
                paym_assignment.referred_to_by = vocab.Note(
                    ident='', content=paym_assignment_label)
                paym_assignment.assigned_property = 'paid_to'
                paym_assignment.assigned = seller
                payments['sell'].attributed_by = paym_assignment
                payments_used.add('sell')
            else:
                # covers non-modified
                # 				acq.carried_out_by = seller
                acq.transferred_title_from = seller
                # 				payments['sell'].carried_out_by = seller
                payments['sell'].paid_to = seller
                payments_used.add('sell')

        for buyer_data in buyers:
            buyer = get_crom_object(buyer_data)
            mod = self.modifiers(buyer_data, 'auth_mod_a')

            if 'or' in mod:
                # or/or others/or another
                mod_non_auth = buyer_data.get('auth_mod')
                if mod_non_auth:
                    acq.referred_to_by = vocab.Note(ident='',
                                                    label=f'Buyer modifier',
                                                    content=mod_non_auth)
                warnings.warn(f'Handle buyer modifier: {mod}'
                              )  # TODO: some way to model this uncertainty?

            if THROUGH.intersects(mod):
                acq.carried_out_by = buyer
                payments['buy'].carried_out_by = buyer
                payments_used.add('buy')
            elif FOR.intersects(mod):
                acq.transferred_title_to = buyer
                payments['buy'].paid_from = buyer
                payments_used.add('buy')
            else:
                # covers FOR modifiers and non-modified
                # 				acq.carried_out_by = buyer
                acq.transferred_title_to = buyer
                payments['buy'].paid_from = buyer
                # 				payments['buy'].carried_out_by = buyer
                payments_used.add('buy')

        if prices:
            amnt = get_crom_object(prices[0])
            for paym in payments.values():
                self.set_possible_attribute(paym, 'paid_amount', prices[0])
                for price in prices[1:]:
                    amnt = get_crom_object(price)
                    content = self._price_note(price)
                    if content:
                        paym.referred_to_by = vocab.PriceStatement(
                            ident='', content=content)
        elif ask_price:
            # for non-auction sales, the ask price is the amount paid for the acquisition
            for paym in payments.values():
                self.set_possible_attribute(paym, 'paid_amount', ask_price)

        ts = tx_data.get('_date')
        if ts:
            acq.timespan = ts

        current_tx.part = acq
        for pay_key in payments_used:
            paym = payments[pay_key]
            current_tx.part = paym
# 		current_tx.part = paym
        data['_prov_entries'] += [add_crom_data(data={}, what=current_tx)]
        # 	lot_uid, lot_uri = helper.shared_lot_number_ids(cno, lno)
        # TODO: `annotation` here is from add_physical_catalog_objects
        # 	paym.referred_to_by = annotation

        data['_acquisition'] = add_crom_data(data={'uri': acq_id}, what=acq)

        final_owner_data = data.get('_final_org', [])
        if final_owner_data:
            data['_organizations'].append(final_owner_data)
            final_owner = get_crom_object(final_owner_data)
            tx_label_args = tuple([
                self.helper, sale_type, 'Sold',
                'leading to the currently known location of'
            ] + list(lot_object_key))
            tx, acq = self.final_owner_prov_entry(tx_label_args, final_owner,
                                                  current_tx, hmo, ts)
            note = final_owner_data.get('note')
            if note:
                acq.referred_to_by = vocab.Note(ident='', content=note)
            data['_prov_entries'].append(add_crom_data(data={}, what=tx))

        self.add_prev_post_owners(data, hmo, tx_data, sale_type,
                                  lot_object_key, ts)
        yield data, current_tx
Beispiel #11
0
    def __call__(self, data, non_auctions, event_properties,
                 problematic_records, transaction_types):
        '''Add modeling data for the auction of a lot of objects.'''
        self.helper.copy_source_information(data['_object'], data)

        auction_houses_data = event_properties['auction_houses']

        auction_locations = event_properties['auction_locations']
        auction_data = data['auction_of_lot']
        try:
            lot_object_key = object_key(auction_data)
        except Exception as e:
            warnings.warn(
                f'Failed to compute lot object key from data {auction_data} ({e})'
            )
            pprint.pprint({k: v
                           for k, v in data.items() if v != ''},
                          stream=sys.stderr)
            raise
        cno, lno, date = lot_object_key
        sale_type = non_auctions.get(cno, 'Auction')

        ask_price = data.get('ask_price', {}).get('ask_price')
        if ask_price:
            # if there is an asking price/currency, it's a direct sale, not an auction;
            # filter these out from subsequent modeling of auction lots.
            warnings.warn(
                f'Skipping {cno} {lno} because it asserts an asking price')
            return

        if sale_type != 'Auction':
            # the records in this sales catalog do not represent auction sales, so the
            # price data should not be asserted as a sale price, but instead as an
            # asking price.
            with suppress(KeyError):
                prices = data['price']
                del data['price']
                if prices:
                    price_data = prices[0]
                    price = get_crom_object(price_data)
                    if price:
                        ma = vocab.add_classification(price, vocab.AskingPrice)
                        data['ask_price'] = add_crom_data(price_data, ma)

        shared_lot_number = self.helper.shared_lot_number_from_lno(lno)
        uid, uri = self.helper.shared_lot_number_ids(cno, lno, date)
        sale_data = {'uid': uid, 'uri': uri}

        lot = self.helper.sale_for_sale_type(sale_type, lot_object_key)
        data['lot_object_id'] = f'{cno} {lno} ({date})'

        if 'link_to_pdf' in auction_data:
            url = auction_data['link_to_pdf']
            page = vocab.WebPage(ident=url, label=url)
            lot.referred_to_by = page

        for problem_key, problem in problematic_records.get('lots', []):
            # TODO: this is inefficient, but will probably be OK so long as the number
            #       of problematic records is small. We do it this way because we can't
            #       represent a tuple directly as a JSON dict key, and we don't want to
            #       have to do post-processing on the services JSON files after loading.
            if tuple(problem_key) == lot_object_key:
                note = model.LinguisticObject(ident='', content=problem)
                problem_classification = model.Type(
                    ident=self.helper.problematic_record_uri,
                    label='Problematic Record')
                problem_classification.classified_as = vocab.instances[
                    "brief text"]
                note.classified_as = problem_classification
                lot.referred_to_by = note

        cite_content = []
        if data.get('transaction_so'):
            cite_content.append(data['transaction_so'])
        if data.get('transaction_cite'):
            cite_content.append(data['transaction_cite'])
        if cite_content:
            content = ', '.join(cite_content)
            cite = vocab.BibliographyStatement(
                ident='', content=content, label='Source of transaction type')
            cite.identified_by = model.Name(
                ident='', content='Source of transaction type')
            lot.referred_to_by = cite

        transaction = data.get('transaction')
        SOLD = transaction_types['sold']
        WITHDRAWN = transaction_types['withdrawn']
        self.set_lot_objects(lot, cno, lno, sale_data['uri'], data, sale_type)
        auction, _, _ = self.helper.sale_event_for_catalog_number(
            cno, sale_type)
        if transaction not in WITHDRAWN:
            lot.part_of = auction
            event_dates = event_properties['auction_dates'].get(cno)

            auction_houses = [
                get_crom_object(self.helper.add_auction_house_data(h.copy()))
                for h in auction_houses_data.get(cno, [])
            ]

            self.set_lot_auction_houses(lot, cno, auction_houses)
            self.set_lot_location(lot, cno, auction_locations)
            self.set_lot_date(lot, auction_data, event_dates)
            self.set_lot_notes(lot, auction_data, sale_type)

            tx_uri = self.helper.transaction_uri_for_lot(auction_data, data)
            lots = self.helper.lots_in_transaction(auction_data, data)
            tx = vocab.ProvenanceEntry(ident=tx_uri)
            tx_label = prov_entry_label(self.helper, sale_type, transaction,
                                        'of', cno, lots, date)
            tx._label = tx_label
            tx.identified_by = model.Name(ident='', content=tx_label)
            tx.caused_by = lot
            tx_data = {'uri': tx_uri}

            if transaction in SOLD:
                if sale_type == 'Auction':
                    # the records in this sales catalog represent auction sales, so the
                    # price data for a sale should be asserted as a hammer price.
                    with suppress(KeyError):
                        prices = data['price']
                        if prices:
                            price_data = prices[0]
                            price = get_crom_object(price_data)
                            if price:
                                vocab.add_classification(
                                    price, vocab.HammerPrice)

                multi = self.helper.transaction_contains_multiple_lots(
                    auction_data, data)
                if multi:
                    tx_data['multi_lot_tx'] = lots

            with suppress(AttributeError):
                tx_data['_date'] = lot.timespan
            data['_prov_entry_data'] = add_crom_data(data=tx_data, what=tx)

            data['_event_causing_prov_entry'] = add_crom_data(data=sale_data,
                                                              what=lot)
        yield data
Beispiel #12
0
    def __call__(self, data: dict, non_auctions, event_properties,
                 buy_sell_modifiers, transaction_types):
        '''Determine if this record has an acquisition or bidding, and add appropriate modeling'''
        parent = data['parent_data']

        auction_houses_data = event_properties['auction_houses']
        event_experts = event_properties['experts']
        event_commissaires = event_properties['commissaire']

        sales_record = get_crom_object(data['_record'])
        transaction = parent['transaction']
        transaction = transaction.replace('[?]', '').rstrip()
        auction_data = parent['auction_of_lot']
        cno, lno, date = object_key(auction_data)
        shared_lot_number = self.helper.shared_lot_number_from_lno(lno)
        buyers = [
            self.add_person(self.helper.copy_source_information(p, parent),
                            record=sales_record,
                            relative_id=f'buyer_{i+1}',
                            catalog_number=cno)
            for i, p in enumerate(parent['buyer'])
        ]

        sellers = [
            self.add_person(self.helper.copy_source_information(p, parent),
                            record=sales_record,
                            relative_id=f'seller_{i+1}',
                            catalog_number=cno)
            for i, p in enumerate(parent['seller'])
        ]

        SOLD = transaction_types['sold']
        UNSOLD = transaction_types['unsold']
        UNKNOWN = transaction_types['unknown']

        data.setdefault('_prov_entries', [])

        sale_type = non_auctions.get(cno, 'Auction')
        if transaction in SOLD:
            data['_owner_locations'] = []
            for data, current_tx in self.add_acquisition(
                    data, buyers, sellers, non_auctions, buy_sell_modifiers,
                    transaction, transaction_types):
                houses = [
                    self.helper.add_auction_house_data(h)
                    for h in auction_houses_data.get(cno, [])
                ]
                experts = event_experts.get(cno, [])
                commissaires = event_commissaires.get(cno, [])
                custody_recievers = houses + [
                    add_crom_data(data={}, what=r)
                    for r in experts + commissaires
                ]

                if sale_type in ('Auction', 'Collection Catalog'):
                    # 'Collection Catalog' is treated just like an Auction
                    self.add_transfer_of_custody(data,
                                                 current_tx,
                                                 xfer_to=custody_recievers,
                                                 xfer_from=sellers,
                                                 sequence=1,
                                                 purpose='selling')
                    self.add_transfer_of_custody(data,
                                                 current_tx,
                                                 xfer_to=buyers,
                                                 xfer_from=custody_recievers,
                                                 sequence=2,
                                                 purpose='completing sale')
                elif sale_type in ('Private Contract Sale', 'Stock List'):
                    # 'Stock List' is treated just like a Private Contract Sale, except for the catalogs
                    metadata = {
                        'pi_record_no': parent['pi_record_no'],
                        'catalog_number': cno
                    }
                    for i, h in enumerate(custody_recievers):
                        house = get_crom_object(h)
                        if hasattr(house, 'label'):
                            house._label = f'{house._label}, private sale organizer for {cno} {shared_lot_number} ({date})'
                        else:
                            house._label = f'Private sale organizer for {cno} {shared_lot_number} ({date})'
                        data['_organizations'].append(h)

                    self.add_transfer_of_custody(data,
                                                 current_tx,
                                                 xfer_to=custody_recievers,
                                                 xfer_from=sellers,
                                                 sequence=1,
                                                 purpose='selling')
                    self.add_transfer_of_custody(data,
                                                 current_tx,
                                                 xfer_to=buyers,
                                                 xfer_from=custody_recievers,
                                                 sequence=2,
                                                 purpose='completing sale')

                    prev_procurements = self.add_private_sellers(
                        data, sellers, sale_type, transaction,
                        transaction_types)
                yield data
        elif transaction in UNSOLD:
            houses = [
                self.helper.add_auction_house_data(h)
                for h in auction_houses_data.get(cno, [])
            ]
            experts = event_experts.get(cno, [])
            commissaires = event_commissaires.get(cno, [])
            custody_recievers = houses + [
                add_crom_data(data={}, what=r) for r in experts + commissaires
            ]
            yield from self.add_bidding(data, buyers, sellers,
                                        buy_sell_modifiers, sale_type,
                                        transaction, transaction_types,
                                        custody_recievers)
        elif transaction in UNKNOWN:
            if sale_type == 'Lottery':
                yield data
            else:
                houses = [
                    self.helper.add_auction_house_data(h)
                    for h in auction_houses_data.get(cno, [])
                ]
                yield from self.add_bidding(data, buyers, sellers,
                                            buy_sell_modifiers, sale_type,
                                            transaction, transaction_types,
                                            houses)
        else:
            prev_procurements = self.add_non_sale_sellers(
                data, sellers, sale_type, transaction, transaction_types)
            lot = get_crom_object(parent['_event_causing_prov_entry'])
            for tx_data in prev_procurements:
                tx = get_crom_object(tx_data)
                lot.starts_after_the_end_of = tx
            warnings.warn(
                f'Cannot create acquisition data for unrecognized transaction type: {transaction!r}'
            )
            yield data
Beispiel #13
0
    def add_bidding(self, data: dict, buyers, sellers, buy_sell_modifiers,
                    sale_type, transaction, transaction_types,
                    auction_houses_data):
        '''Add modeling of bids that did not lead to an acquisition'''
        hmo = get_crom_object(data)
        parent = data['parent_data']
        data['seller'] = sellers
        auction_data = parent['auction_of_lot']
        lot_object_key = object_key(auction_data)
        cno, lno, date = lot_object_key
        lot_data = parent.get('_event_causing_prov_entry')
        if not lot_data:
            return
        lot = get_crom_object(lot_data)
        if not lot:
            return
        ts = lot.timespan

        UNSOLD = transaction_types['unsold']
        model_custody_return = transaction in UNSOLD
        prev_procurements = self.add_non_sale_sellers(data, sellers, sale_type,
                                                      transaction,
                                                      transaction_types)

        prices = parent.get('price', [])
        if not prices:
            yield data
        amnts = [get_crom_object(p) for p in prices]

        tx_data = parent.get('_prov_entry_data')
        tx = get_crom_object(tx_data)
        houses = auction_houses_data
        self.add_transfer_of_custody(data,
                                     tx,
                                     xfer_to=houses,
                                     xfer_from=sellers,
                                     sequence=1,
                                     purpose='selling')
        if model_custody_return:
            self.add_transfer_of_custody(data,
                                         tx,
                                         xfer_to=sellers,
                                         xfer_from=houses,
                                         sequence=2,
                                         purpose='returning')

        data.setdefault('_prov_entries', [])
        data['_prov_entries'].append(tx_data)

        if amnts:
            bidding_id = hmo.id + '-Bidding'
            all_bids_label = f'Bidding on {cno} {lno} ({date})'
            all_bids = model.Activity(ident=bidding_id, label=all_bids_label)
            all_bids.identified_by = model.Name(ident='',
                                                content=all_bids_label)
            for tx_data in prev_procurements:
                tx = get_crom_object(tx_data)
                all_bids.starts_after_the_end_of = tx

            all_bids.part_of = lot

            THROUGH = set(buy_sell_modifiers['through'])
            FOR = set(buy_sell_modifiers['for'])

            for seq_no, amnt in enumerate(amnts):
                bid_id = hmo.id + f'-Bid-{seq_no}'
                bid = vocab.Bidding(ident=bid_id)
                prop_id = hmo.id + f'-Bid-{seq_no}-Promise'
                try:
                    amnt_label = amnt._label
                    bid_label = f'Bid of {amnt_label} on {cno} {lno} ({date})'
                    prop = model.PropositionalObject(
                        ident=prop_id, label=f'Promise to pay {amnt_label}')
                except AttributeError:
                    bid_label = f'Bid on {cno} {lno} ({date})'
                    prop = model.PropositionalObject(ident=prop_id,
                                                     label=f'Promise to pay')

                bid._label = bid_label
                bid.identified_by = model.Name(ident='', content=bid_label)
                prop.refers_to = amnt
                bid.created = prop

                # TODO: there are often no buyers listed for non-sold records.
                #       should we construct an anonymous person to carry out the bid?
                for buyer_data in buyers:
                    buyer = get_crom_object(buyer_data)
                    mod = buyer_data.get('auth_mod_a', '')
                    if mod in THROUGH:
                        bid.carried_out_by = buyer
                    elif mod in FOR:
                        warnings.warn(
                            f'buyer modifier {mod} for non-sale bidding: {cno} {lno} {date}'
                        )
                    else:
                        bid.carried_out_by = buyer

                all_bids.part = bid

            final_owner_data = data.get('_final_org')
            if final_owner_data:
                data['_organizations'].append(final_owner_data)
                final_owner = get_crom_object(final_owner_data)
                hmo = get_crom_object(data)
                tx_label_args = tuple([
                    self.helper, sale_type, 'Sold',
                    'leading to the currently known location of'
                ] + list(lot_object_key))
                tx, acq = self.final_owner_procurement(tx_label_args,
                                                       final_owner, None, hmo,
                                                       ts)
                note = final_owner_data.get('note')
                if note:
                    acq.referred_to_by = vocab.Note(ident='', content=note)
                data['_prov_entries'].append(add_crom_data(data={}, what=tx))

            data['_bidding'] = {'uri': bidding_id}
            add_crom_data(data=data['_bidding'], what=all_bids)
            yield data
        else:
            warnings.warn(
                f'*** No price data found for {parent["transaction"]!r} transaction'
            )
            yield data
Beispiel #14
0
    def add_acquisition(self, data, buyers, sellers, non_auctions,
                        buy_sell_modifiers, transaction, transaction_types):
        '''Add modeling of an acquisition as a transfer of title from the seller to the buyer'''
        hmo = get_crom_object(data)
        parent = data['parent_data']
        # 	transaction = parent['transaction']
        prices = parent.get('price')
        auction_data = parent['auction_of_lot']
        lot_object_key = object_key(auction_data)
        cno, lno, date = lot_object_key
        sale_type = non_auctions.get(cno, 'Auction')
        data['buyer'] = buyers
        data['seller'] = sellers

        acq_label = None
        try:
            object_label = f'“{hmo._label}”'
            acq_label = f'Acquisition of {cno} {lno} ({date}): {object_label}'
        except AttributeError:
            object_label = '(object)'
            acq_label = f'Acquisition of {cno} {lno} ({date})'

    # 	if not prices:
    # 		print(f'*** No price data found for {transaction} transaction')

        tx_data = parent['_prov_entry_data']
        current_tx = get_crom_object(tx_data)
        payment_id = current_tx.id + '-Pay'

        acq_id = hmo.id + '-Acq'
        acq = model.Acquisition(ident=acq_id, label=acq_label)
        acq.transferred_title_of = hmo

        self.attach_source_catalog(data, acq, buyers + sellers)

        multi = tx_data.get('multi_lot_tx')
        paym_label = f'multiple lots {multi}' if multi else object_label
        paym = model.Payment(ident=payment_id,
                             label=f'Payment for {paym_label}')

        THROUGH = set(buy_sell_modifiers['through'])
        FOR = set(buy_sell_modifiers['for'])

        single_seller = (len(sellers) == 1)
        single_buyer = (len(buyers) == 1)

        for seq_no, seller_data in enumerate(sellers):
            seller = get_crom_object(seller_data)
            mod = seller_data.get('auth_mod_a', '')

            if mod == 'or':
                mod_non_auth = seller_data.get('auth_mod')
                if mod_non_auth:
                    acq.referred_to_by = vocab.Note(ident='',
                                                    label=f'Seller modifier',
                                                    content=mod_non_auth)
                warnings.warn('Handle OR buyer modifier'
                              )  # TODO: some way to model this uncertainty?

            if mod in THROUGH:
                acq.carried_out_by = seller
                paym.carried_out_by = seller
            elif mod in FOR:
                acq.transferred_title_from = seller
                paym.paid_to = seller
            elif mod == 'or anonymous':
                acq_assignment = vocab.PossibleAssignment(
                    ident=acq.id + f'-seller-assignment-{seq_no}',
                    label=
                    f'Uncertain seller as previous title holder in acquisition'
                )
                acq_assignment.assigned_property = 'transferred_title_from'
                acq_assignment.assigned = seller
                acq.attributed_by = acq_assignment

                paym_assignment = vocab.PossibleAssignment(
                    ident=paym.id + f'-seller-assignment-{seq_no}',
                    label=f'Uncertain seller as recipient of payment')
                paym_assignment.assigned_property = 'paid_to'
                paym_assignment.assigned = seller
                paym.attributed_by = paym_assignment
            else:
                # covers non-modified
                acq.carried_out_by = seller
                acq.transferred_title_from = seller
                paym.carried_out_by = seller
                paym.paid_to = seller

        for buyer_data in buyers:
            buyer = get_crom_object(buyer_data)
            mod = buyer_data.get('auth_mod_a', '')

            if mod == 'or':
                # or/or others/or another
                mod_non_auth = buyer_data.get('auth_mod')
                if mod_non_auth:
                    acq.referred_to_by = vocab.Note(ident='',
                                                    label=f'Buyer modifier',
                                                    content=mod_non_auth)
                warnings.warn(f'Handle buyer modifier: {mod}'
                              )  # TODO: some way to model this uncertainty?

            if mod in THROUGH:
                acq.carried_out_by = buyer
                paym.carried_out_by = buyer
            else:
                # covers FOR modifiers and non-modified
                acq.transferred_title_to = buyer
                paym.paid_from = buyer

        if prices:
            amnt = get_crom_object(prices[0])
            paym.paid_amount = amnt
            for price in prices[1:]:
                amnt = get_crom_object(price)
                content = self._price_note(price)
                if content:
                    paym.referred_to_by = vocab.PriceStatement(ident='',
                                                               content=content)

        ts = tx_data.get('_date')
        if ts:
            acq.timespan = ts

        current_tx.part = acq
        current_tx.part = paym
        data['_prov_entries'] += [add_crom_data(data={}, what=current_tx)]
        # 	lot_uid, lot_uri = helper.shared_lot_number_ids(cno, lno)
        # TODO: `annotation` here is from add_physical_catalog_objects
        # 	paym.referred_to_by = annotation

        data['_acquisition'] = add_crom_data(data={'uri': acq_id}, what=acq)

        final_owner_data = data.get('_final_org', [])
        if final_owner_data:
            data['_organizations'].append(final_owner_data)
            final_owner = get_crom_object(final_owner_data)
            tx_label_args = tuple([
                self.helper, sale_type, 'Sold',
                'leading to the currently known location of'
            ] + list(lot_object_key))
            tx, acq = self.final_owner_procurement(tx_label_args, final_owner,
                                                   current_tx, hmo, ts)
            note = final_owner_data.get('note')
            if note:
                acq.referred_to_by = vocab.Note(ident='', content=note)
            data['_prov_entries'].append(add_crom_data(data={}, what=tx))

        post_own = data.get('post_owner', [])
        prev_own = data.get('prev_owner', [])
        prev_post_owner_records = [(post_own, False), (prev_own, True)]
        for owner_data, rev in prev_post_owner_records:
            if rev:
                rev_name = 'prev-owner'
            else:
                rev_name = 'post-owner'
            ignore_fields = {'own_so', 'own_auth_l', 'own_auth_d'}
            for seq_no, owner_record in enumerate(owner_data):
                record_id = f'{rev_name}-{seq_no+1}'
                if not any([
                        bool(owner_record.get(k))
                        for k in owner_record.keys() if k not in ignore_fields
                ]):
                    # some records seem to have metadata (source information, location, or notes)
                    # but no other fields set these should not constitute actual records of a prev/post owner.
                    continue
                self.handle_prev_post_owner(data,
                                            hmo,
                                            tx_data,
                                            sale_type,
                                            lot_object_key,
                                            owner_record,
                                            record_id,
                                            rev,
                                            ts,
                                            make_label=prov_entry_label)
        yield data, current_tx