def _populate_object_prev_post_sales(self, data: dict, this_key, post_sale_map): hmo = get_crom_object(data) post_sales = data.get('post_sale', []) prev_sales = data.get('prev_sale', []) prev_post_sales_records = [(post_sales, False), (prev_sales, True)] for sales_data, rev in prev_post_sales_records: for sale_record in sales_data: pcno = sale_record.get('cat') plno = sale_record.get('lot') # plot = self.helper.shared_lot_number_from_lno(plno) pdate = implode_date(sale_record, '') if pcno and plno and pdate: if pcno == 'NA': desc = f'Also sold in an unidentified sale: {plno} ({pdate})' note = vocab.Note(ident='', content=desc) hmo.referred_to_by = note elif 'or' in plno.lower(): desc = f'Also sold in an uncertain lot: {pcno} {plno} ({pdate})' note = vocab.Note(ident='', content=desc) hmo.referred_to_by = note else: that_key = (pcno, plno, pdate) if rev: # `that_key` is for a previous sale for this object post_sale_map[this_key] = that_key else: # `that_key` is for a later sale for this object post_sale_map[that_key] = this_key
def handle_places(self, data): base_uri = self.helper.make_proj_uri('PLACE', '') for loc in data.get('locations', []): l = loc.get('location') if l: current = parse_location_name(l, uri_base=self.helper.proj_prefix) place_data = self.helper.make_place(current, base_uri=base_uri) data['places'].append(place_data) note = loc.get('location_note') if note: note = vocab.Note(ident='', content=note) data['referred_to_by'].append(note) date = loc.get('location_date') if date: note = vocab.BibliographyStatement( ident='', content=f'Residence in {l} ({date})') data['referred_to_by'].append(note) address = loc.get('address') if address: contact = model.Identifier(ident='', content=address) contact_data = add_crom_data(data={}, what=contact) data['contact_point'].append(contact_data) note = loc.get('address_note') if note: note = vocab.Note(ident='', content=note) data['referred_to_by'].append(note) date = loc.get('address_date') if date: note = vocab.BibliographyStatement( ident='', content=f'Address at {l} ({date})') data['referred_to_by'].append(note)
def model_record_desc_group(self, record, data): record.setdefault('referred_to_by', []) record.setdefault('identifiers', []) jid = data['record_id'] inote = data.get('internal_note') snote = data.get('source_note') record['identifiers'].append(self.helper.gci_number_id(jid)) if inote: record['referred_to_by'].append(vocab.Note(ident='', content=inote)) if snote: record['referred_to_by'].append(vocab.Note(ident='', content=snote))
def add_mod_notes(self, act, all_mods, label): if act and all_mods: # Preserve the seller modifier strings as notes on the acquisition/bidding activity for mod in all_mods: note = vocab.Note(ident='', label=label, content=mod) note.classified_as = vocab.instances['qualifier'] act.referred_to_by = note
def handle_statements(self, data): text_content = data.get('text') if text_content: cite = vocab.BiographyStatement(ident='', content=text_content) data['referred_to_by'].append(cite) source_content = data.get('source') if source_content: cite = vocab.BibliographyStatement(ident='', content=source_content) data['referred_to_by'].append(cite) project = data.get('project') if project: data['referred_to_by'].append( vocab.SourceStatement(ident='', content=project)) awards = { l.strip() for l in data.get('medal_received', '').split(';') } - {''} for award in awards: cite = vocab.Note( ident='', content=award ) # TODO: add proper classification for an Awards Statement data['referred_to_by'].append(cite)
def __call__(self, data): d = {k: v for k, v in data.items()} parent = data['parent_data'] cno = str(parent['catalog_number']) sno = parent['star_record_no'] entry_record = get_crom_object(data.get('_catalog')) catalog = get_crom_object(d) for lugt_no in parent.get('lugt', {}).values(): if not lugt_no: warnings.warn(f'Setting empty identifier on {catalog.id}') catalog.identified_by = self.lugt_number_id(lugt_no) if not cno: warnings.warn(f'Setting empty identifier on {catalog.id}') catalog.identified_by = self.helper.gpi_number_id( cno, vocab.LocalNumber) if not sno: warnings.warn(f'Setting empty identifier on {catalog.id}') catalog.identified_by = self.helper.gpi_number_id( sno, vocab.StarNumber) notes = data.get('notes') if notes: note = vocab.Note(ident='', content=parent['notes']) catalog.referred_to_by = note return d
def populate_destruction_events(self, data: dict, note, *, type_map, location=None): destruction_types_map = type_map hmo = get_crom_object(data) title = data.get('title') short_title = truncate_with_ellipsis(title, 100) or title r = re.compile( r'[Dd]estroyed(?: (?:by|during) (\w+))?(?: in (\d{4})[.]?)?') m = r.search(note) if m: method = m.group(1) year = m.group(2) # The destruction URI is just the object URI with a suffix. When URIs are # reconciled during prev/post sale rewriting, this will allow us to also reconcile # the URIs for the destructions (of which there should only be one per object) dest_uri = hmo.id + '-Destruction' d = model.Destruction(ident=dest_uri, label=f'Destruction of “{short_title}”') d.referred_to_by = vocab.Note(ident='', content=note) if year is not None: begin, end = date_cleaner(year) ts = timespan_from_outer_bounds(begin, end) ts.identified_by = model.Name(ident='', content=year) d.timespan = ts if method: with suppress(KeyError, AttributeError): type_name = destruction_types_map[method.lower()] otype = vocab.instances[type_name] event = model.Event( label= f'{method.capitalize()} event causing the destruction of “{short_title}”' ) event.classified_as = otype d.caused_by = event data['_events'].append(add_crom_data(data={}, what=event)) if location: current = parse_location_name( location, uri_base=self.helper.uid_tag_prefix) # The place URI used for destruction events is based on the object URI with # a suffix. When URIs are reconciled during prev/post sale rewriting, this # will allow us to also reconcile the URIs for the places of destruction # (of which there should only be one hierarchy per object) base_uri = hmo.id + '-Destruction-Place,' place_data = self.helper.make_place(current, base_uri=base_uri) place = get_crom_object(place_data) if place: data['_locations'].append(place_data) d.took_place_at = place hmo.destroyed_by = d
def model_authorship_group(self, record, data): if not data: return record.setdefault('_people', []) record.setdefault('created_by', []) authors = _as_list(data.get('primary_author')) mlap = MakeLinkedArtPerson() mlao = MakeLinkedArtOrganization() ordered_data = [] article_label = record['label'] creation_id = record['uri'] + '-Creation' creation = model.Creation(ident=creation_id, label=f'Creation of {article_label}') for a in authors: gaia_id = a['gaia_authority_id'] gaia_type = a['gaia_authority_type'] name = a['author_name'] roles = _as_list(a['author_role']) order = a['author_order'] ordered_data.append((order, name)) p = { 'label': name, 'name': name, } if gaia_type == 'Person': uri = self.helper.person_uri(gaia_id) p['uri'] = uri mlap(p) elif gaia_type == 'Corp': uri = self.helper.corporate_body_uri(gaia_id) p['uri'] = uri mlao(p) else: raise Exception( f'Unexpected type of authorship record: {gaia_type}') # uri = self.helper.make_proj_uri(gaia_type, 'GAIA', gaia_id) record['_people'].append(p) for role in roles: part = model.Creation(ident='', label=f'{role} Creation sub-event') part.carried_out_by = get_crom_object(p) cl = self.helper.role_type(role) if cl: part.classified_as = cl creation.part = part ordered_authors = [p[1] for p in sorted(ordered_data)] order_string = self.helper.ordered_author_string(ordered_authors) creation.referred_to_by = vocab.Note(ident='', content=order_string) record['created_by'].append(creation)
def model_notes_group(self, record, data): if not data: return record.setdefault('_declared_languages', set()) record.setdefault('language', []) record.setdefault('identifiers', []) record.setdefault('referred_to_by', []) lang_docs = _as_list(data.get('lang_doc')) lang_summaries = _as_list(data.get('lang_summary')) isbns = _as_list(data.get('isbn')) issns = _as_list(data.get('issn')) citation_note = data.get('citation_note') inotes = _as_list(data.get('internal_note')) for lang in lang_summaries: record['_declared_languages'].add(lang) for lang in lang_docs: record['_declared_languages'].add(lang) l = self.helper.language_object_from_code(lang) if l: record['language'].append(l) for isbn in isbns: num = isbn.get('isbn_number') q = isbn.get('isbn_qualifier') if num: i = vocab.IsbnIdentifier(ident='', content=num) if q: i.referred_to_by = vocab.Note(ident='', content=q) record['identifiers'].append(i) for issn in issns: i = vocab.IssnIdentifier(ident='', content=issn) record['identifiers'].append(i) if citation_note: record['referred_to_by'].append( vocab.Citation(ident='', content=citation_note)) for inote in inotes: record['referred_to_by'].append( vocab.Note(ident='', content=inote['note']))
def set_referred_to_by(self, data, thing): for notedata in data.get('referred_to_by', []): if isinstance(notedata, tuple): content, itype = notedata if itype is not None: if isinstance(itype, type): note = itype(content=content) elif isinstance(itype, object): note = itype note.content = content else: note = vocab.Note(content=content) note.classified_as = itype elif isinstance(notedata, model.BaseResource): note = notedata elif isinstance(notedata, str): note = vocab.Note(content=notedata) else: note = notedata thing.referred_to_by = note
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)
def populate_destruction_events(self, data: dict, note, *, type_map, location=None): destruction_types_map = type_map hmo = get_crom_object(data) title = data.get('title') short_title = truncate_with_ellipsis(title, 100) or title r = re.compile( r'[Dd]estroyed(?: (?:by|during) (\w+))?(?: in (\d{4})[.]?)?') m = r.search(note) if m: method = m.group(1) year = m.group(2) dest_id = hmo.id + '-Destr' d = model.Destruction(ident=dest_id, label=f'Destruction of “{short_title}”') d.referred_to_by = vocab.Note(ident='', content=note) if year is not None: begin, end = date_cleaner(year) ts = timespan_from_outer_bounds(begin, end) ts.identified_by = model.Name(ident='', content=year) d.timespan = ts if method: with suppress(KeyError, AttributeError): type_name = destruction_types_map[method.lower()] otype = vocab.instances[type_name] event = model.Event( label= f'{method.capitalize()} event causing the destruction of “{short_title}”' ) event.classified_as = otype d.caused_by = event data['_events'].append(add_crom_data(data={}, what=event)) if location: current = parse_location_name( location, uri_base=self.helper.uid_tag_prefix) base_uri = hmo.id + '-Place,' place_data = self.helper.make_place(current, base_uri=base_uri) place = get_crom_object(place_data) if place: data['_locations'].append(place_data) d.took_place_at = place hmo.destroyed_by = d
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)
def model_sojourn(self, data, loc): base_uri = self.helper.make_proj_uri('PLACE', '') cb = data.get('corporate_body', False) sojourn_type = vocab.Establishment if cb else vocab.Residing sdata = { 'type': sojourn_type, 'referred_to_by': [], } verbatim_date = loc.get('address_date') if verbatim_date: date_range = date_cleaner(verbatim_date) if date_range: begin, end = date_range ts = timespan_from_outer_bounds(*date_range) ts.identified_by = model.Name(ident='', content=verbatim_date) sdata['timespan'] = add_crom_data( { 'address_date': verbatim_date, 'begin': begin, 'end': end }, ts) current = None l = loc.get('location') if l: current = parse_location_name(l, uri_base=self.helper.proj_prefix) address = loc.get('address') if address: current = { 'name': address, 'part_of': current, 'type': 'address', } for k in ('address_note', 'location_note'): note = loc.get(k) if note: sdata['referred_to_by'].append( vocab.Note(ident='', content=note)) if current: place_data = self.helper.make_place(current, base_uri=base_uri) data['_places'].append(place_data) sdata['place'] = place_data return sdata
def __call__(self, data: dict, non_auctions): '''Add modeling for physical copies of an auction catalog''' catalog = get_crom_object(data['_catalog']) cno = data['catalog_number'] owner = data['owner_code'] copy = data['copy_number'] sale_type = non_auctions.get(cno, 'Auction') catalogObject = self.helper.physical_catalog(cno, sale_type, owner, copy) data['uri'] = catalogObject.id info = data.get('annotation_info') if info: catalogObject.referred_to_by = vocab.Note(ident='', content=info) catalogObject.carries = catalog add_crom_data(data=data, what=catalogObject) return data
def model_series_group(self, record, data): if not data: return record.setdefault('identifiers', []) record.setdefault('referred_to_by', []) record.setdefault('language', []) title = data.get('title') title_translated = data.get('title_translated') variant_titles = _as_list(data.get('variant_title')) related_titles = _as_list(data.get('related_title')) lang_docs = _as_list(data['lang_doc']) frequency = data.get('frequency') start_year = data.get('start_year') cease_year = data.get('cease_year') issn = data.get('issn') coden = data.get('coden') if title: record['label'] = title record['identifiers'].append( vocab.PrimaryName(ident='', content=title)) if title_translated: record['identifiers'].append( vocab.TranslatedTitle(ident='', content=title)) for vtitle in variant_titles: record['identifiers'].append(vocab.Title(ident='', content=vtitle)) for lang in lang_docs: l = self.helper.language_object_from_code(lang) if l: record['language'].append(l) if frequency: record['referred_to_by'].append( vocab.Note(ident='', content=frequency)) if start_year: record['_publishing_start_year'] = start_year if cease_year: record['_publishing_cease_year'] = cease_year if issn: record['identifiers'].append( vocab.IssnIdentifier(ident='', content=issn)) if coden: record['identifiers'].append( vocab.CodenIdentifier(ident='', content=coden))
def _populate_object_notes(self, data: dict, parent, unique_catalogs): hmo = get_crom_object(data) notes = data.get('hand_note', []) for note in notes: hand_note_content = note['hand_note'] owner = note.get('hand_note_so') cno = parent['auction_of_lot']['catalog_number'] catalog_uri = self.helper.make_proj_uri('CATALOG', cno, owner, None) catalogs = unique_catalogs.get(catalog_uri) note = vocab.Note(ident='', content=hand_note_content) hmo.referred_to_by = note if catalogs and len(catalogs) == 1: note.carried_by = vocab.AuctionCatalog( ident=catalog_uri, label=f'Sale Catalog {cno}, owned by “{owner}”') inscription = data.get('inscription') if inscription: hmo.referred_to_by = vocab.InscriptionStatement( ident='', content=inscription)
def model_concept_group(self, record, data): record.setdefault('referred_to_by', []) record.setdefault('identifiers', []) record.setdefault('_places', []) # for extraction/serialization by the pipeline record.setdefault('places', []) # for pipeline.linkedart modeling code gaia_id = data['gaia_auth_id'] snote = data.get('scope_note') inote = data.get('internal_note') snfnote = data.get('source_not_found_note') locations = _as_list(data.get('location', [])) record['uri'] = self.helper.corporate_body_uri(gaia_id) record['identifiers'].append(self.helper.gci_number_id(gaia_id, id_class=vocab.SystemNumber)) if snote: record['referred_to_by'].append(vocab.Note(ident='', content=snote)) if inote: record['referred_to_by'].append(vocab.InternalNote(ident='', content=inote)) if snfnote: record['referred_to_by'].append(vocab.InternalNote(ident='', content=snfnote)) mlap = MakeLinkedArtPlace() for loc in locations: geog_id = loc.get('gaia_geog_id') if geog_id: geog_uri = self.helper.place_uri(geog_id) geog_data = { 'uri': geog_uri, 'identifiers': [], } geog_name = loc.get('location_string') if geog_name: geog_data['label'] = geog_name geog_data['name'] = geog_name mlap(geog_data) record['places'].append(geog_data) record['_places'].append(geog_data)
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
def person(self, label): p = model.Person(ident='http://example.org/person', label=label) p.referred_to_by = vocab.Note(ident='', content='This is Eve') return p
def handle_prev_post_owner(self, data, hmo, tx_data, sale_type, lot_object_key, owner_record, record_id, rev, ts=None, make_label=None): current_tx = get_crom_object(tx_data) sales_record = get_crom_object(data['_record']) if rev: rel = f'leading to the previous ownership of' source_label = 'Source of information on history of the object prior to the current sale.' else: rel = f'leading to the subsequent ownership of' source_label = 'Source of information on history of the object after the current sale.' owner_record.update({ 'pi_record_no': data['pi_record_no'], 'ulan': owner_record.get('ulan', owner_record.get('own_ulan')), }) self.add_person(owner_record, record=sales_record, relative_id=record_id, role='artist') owner = get_crom_object(owner_record) # TODO: handle other fields of owner_record: own_auth_d, own_auth_q, own_ques, own_so if owner_record.get('own_auth_l'): loc = owner_record['own_auth_l'] current = parse_location_name(loc, uri_base=self.helper.uid_tag_prefix) place_data = self.helper.make_place(current) place = get_crom_object(place_data) owner.residence = place data['_owner_locations'].append(place_data) if owner_record.get('own_auth_p'): content = owner_record['own_auth_p'] owner.referred_to_by = vocab.Note(ident='', content=content) data.setdefault('_other_owners', []) data['_other_owners'].append(owner_record) # The Provenance Entry URI must not share a prefix with the object URI, otherwise # we run the rist of provenance entries being accidentally merged during URI # reconciliation as part of the prev/post sale rewriting. tx_uri = self.helper.prepend_uri_key(hmo.id, f'PROV-{record_id}') tx_label_args = tuple([self.helper, sale_type, 'Sold', rel] + list(lot_object_key)) tx, _ = self.related_procurement(hmo, tx_label_args, current_tx, ts, buyer=owner, previous=rev, ident=tx_uri, make_label=make_label) if owner_record.get('own_auth_e'): content = owner_record['own_auth_e'] tx.referred_to_by = vocab.Note(ident='', content=content) own_info_source = owner_record.get('own_so') if own_info_source: note = vocab.SourceStatement(ident='', content=own_info_source, label=source_label) tx.referred_to_by = note ptx_data = tx_data.copy() data['_prov_entries'].append(add_crom_data(data=ptx_data, what=tx))
def add_props(self, data: dict, role=None, **kwargs): role = role if role else 'person' auth_name = data.get('auth_name', '') period_match = self.anon_period_re.match(auth_name) nationalities = [] if 'nationality' in data: nationality = data['nationality'] if isinstance(nationality, str): nationalities.append(nationality.lower()) elif isinstance(nationality, list): nationalities += [n.lower() for n in nationality] data['nationality'] = [] data.setdefault('referred_to_by', []) name = data['label'] active = self.clamped_timespan_args(data, name) if active: pact_uri = data['uri'] + '-ProfAct-active' a = self.professional_activity(name, ident=pact_uri, **active) data['events'].append(a) for key in ('notes', 'brief_notes', 'working_notes'): if key in data: for content in [n.strip() for n in data[key].split(';')]: if content: note = vocab.Note(ident='', content=content) data['referred_to_by'].append(note) for key in ('name_cite', 'bibliography'): if data.get(key): cite = vocab.BibliographyStatement(ident='', content=data[key]) data['referred_to_by'].append(cite) if data.get('name_cite'): cite = vocab.BibliographyStatement(ident='', content=data['name_cite']) data['referred_to_by'].append(cite) if self.is_anonymous_group(auth_name): nationality_match = self.anon_nationality_re.match(auth_name) dated_nationality_match = self.anon_dated_nationality_re.match( auth_name) dated_match = self.anon_dated_re.match(auth_name) data.setdefault('events', []) if nationality_match: with suppress(ValueError): nationality = nationality_match.group(1).lower() nationalities.append(nationality) group_label = self.anonymous_group_label( role, nationality=nationality) data['label'] = group_label elif dated_nationality_match: with suppress(ValueError): nationality = dated_nationality_match.group(1).lower() nationalities.append(nationality) century = int(dated_nationality_match.group(2)) group_label = self.anonymous_group_label( role, century=century, nationality=nationality) data['label'] = group_label pact_uri = data['uri'] + '-ProfAct-dated-natl' a = self.professional_activity(group_label, ident=pact_uri, century=century, narrow=True) data['events'].append(a) elif dated_match: with suppress(ValueError): century = int(dated_match.group(1)) group_label = self.anonymous_group_label(role, century=century) data['label'] = group_label pact_uri = data['uri'] + '-ProfAct-dated' a = self.professional_activity(group_label, ident=pact_uri, century=century, narrow=True) data['events'].append(a) elif period_match: period = period_match.group(1).lower() data['label'] = f'anonymous {period} {role}s' for nationality in nationalities: key = f'{nationality.lower()} nationality' n = vocab.instances.get(key) if n: data['nationality'].append(n) else: warnings.warn( f'No nationality instance found in crom for: {key!r}')
def set_properties(self, data, thing): ''' The following keys in `data` are handled to set properties on `thing`: `referred_to_by` `identifiers` `names` - An array of arrays of one or two elements. The first element of each array is a name string, and is set as the value of a `model.Name` for `thing`. If there is a `dict` second element, its contents are used to assert properties of the name: - An array associated with the key `'referred_to_by'` will be used to assert that the `LinguisticObject`s (or `dict`s representing a `LinguisticObject`) refer to the name. - A value associated with the key `'classified_as'` (either a `model.Type` or a cromulent vocab class) will be asserted as the classification of the `model.Name`. Example data: { 'names': [ ['J. Paul Getty'], [ 'Getty', { 'classified_as': model.Type(ident='http://vocab.getty.edu/aat/300404670', label='Primary Name'), # or: 'classified_as': vocab.PrimaryName, 'referred_to_by': [ {'uri': 'tag:getty.edu,2019:digital:pipeline:REPLACE-WITH-UUID:knoedler#K-ROW-1-2-3'}, model.LinguisticObject(ident='tag:getty.edu,2019:digital:pipeline:REPLACE-WITH-UUID:knoedler#K-ROW-1-7-10'), ] } ] ] } ''' for notedata in data.get('referred_to_by', []): if isinstance(notedata, tuple): content, itype = notedata if itype is not None: if isinstance(itype, type): note = itype(content=content) elif isinstance(itype, object): note = itype note.content = content else: note = vocab.Note(content=content) note.classified_as = itype elif isinstance(notedata, model.BaseResource): note = notedata elif isinstance(notedata, str): note = vocab.Note(content=notedata) else: note = notedata thing.referred_to_by = note for identifier in data.get('identifiers', []): if isinstance(identifier, tuple): content, itype = identifier if itype is not None: if isinstance(itype, type): ident = itype(ident='', content=content) if not content: warnings.warn( f'Setting empty identifier on {thing.id}') elif isinstance(itype, object): ident = itype ident.content = content if not content: warnings.warn( f'Setting empty identifier on {thing.id}') else: ident = model.Identifier(ident='') if not content: warnings.warn( f'Setting empty identifier on {thing.id}') ident.content = content ident.classified_as = itype else: ident = identifier # c = ident.content thing.identified_by = ident if not hasattr(thing, '_label') and 'label' in data: setattr(thing, '_label', data['label']) for namedata in data.get('names', []): # namedata should take the form of: # ["A. Name"] # ["A. Name", {'referred_to_by': [{'uri': 'URI-OF-LINGUISTIC_OBJECT'}, model.LinguisticObject()]}] if isinstance(namedata, tuple): name, *properties = namedata else: name = namedata properties = [] name_kwargs = {} for props in properties: if 'classified_as' in props: cl = props['classified_as'] del props['classified_as'] name_kwargs['title_type'] = cl n = set_la_name(thing, name, **name_kwargs) self.set_lo_properties(n, *properties)
def model_artists_with_modifers(self, data: dict, hmo, attribution_modifiers, attribution_group_types, attribution_group_names): '''Add modeling for artists as people involved in the production of an object''' sales_record = get_crom_object(data['_record']) data.setdefault('_organizations', []) data.setdefault('_original_objects', []) try: hmo_label = f'{hmo._label}' except AttributeError: hmo_label = 'object' STYLE_OF = attribution_modifiers['style of'] ATTRIBUTED_TO = attribution_modifiers['attributed to'] COPY_AFTER = attribution_modifiers['copy after'] COPY_BY = attribution_modifiers['copy by'] POSSIBLY = attribution_modifiers['possibly by'] UNCERTAIN = attribution_modifiers['uncertain'] FORMERLY_MODS = attribution_modifiers['formerly attributed to'] COPY_BY = attribution_modifiers['copy by'] # The production event URI is just the object URI with a suffix. When URIs are # reconciled during prev/post sale rewriting, this will allow us to also reconcile # the URIs for the production events (of which there should only be one per object) event_uri = hmo.id + '-Production' prod_event = model.Production( ident=event_uri, label=f'Production event for {hmo_label}') hmo.produced_by = prod_event artists = data.get('_artists', []) for a in artists: self.add_properties(data, a) # 1. Remove "copy by/after" modifiers when in the presence of "manner of; style of". This combination is not meaningful, and the intended semantics are preserved by keeping only the style assertion (with the understanding that every "copy by" modifier has a paired "copy after" in another artist record, and vice-versa) for a in artists: mods = a['modifiers'] if STYLE_OF.intersects(mods): for COPY in (COPY_BY, COPY_AFTER): if COPY.intersects(mods): a['modifiers'] -= COPY.intersection(mods) NON_ARTIST_MODS = COPY_AFTER | STYLE_OF ARTIST_NON_GROUP_MODS = FORMERLY_MODS | COPY_BY | {'attributed to'} artist_assertions = [] non_artist_assertions = [] for a in artists: mods = a['modifiers'] if NON_ARTIST_MODS.intersects(mods): non_artist_assertions.append(a) if ARTIST_NON_GROUP_MODS.intersects(mods): # these have both artist and non-artist assertions artist_assertions.append(a) else: artist_assertions.append(a) # 4. Check for the special case of "A or style of A" uncertain = False if self.uncertain_artist_or_style(artists): artist = artists[0]['label'] uncertain = True note = f'Record indicates certainty that this object was either created by {artist} or was created in the style of {artist}' hmo.referred_to_by = vocab.Note(ident='', content=note) # 2--3 self.model_object_influence(data, non_artist_assertions, hmo, prod_event, attribution_modifiers, attribution_group_types, attribution_group_names, all_uncertain=uncertain) # 5 self.model_object_artists(data, artist_assertions, hmo, prod_event, attribution_modifiers, attribution_group_types, attribution_group_names, all_uncertain=uncertain) # data['_artists'] is what's pulled out by the serializers data['_artists'] = [a for a in artists if not self.is_or_anon(a)] return data
def model_issue_group(self, record, data, seq): record.setdefault('^part', []) issue_id = data['issue_id'] title = data.get('title') title_translated = data.get('title_translated') date = data.get('date') # issue_group/date/display_date # issue_group/date/sort_year volume = data.get('volume') number = data.get('number') note = data.get('note') journal_label = record['label'] issue_label = f'Issue of {journal_label}' if title: issue_label = f'{journal_label}: “{title}”' if volume and number: issue_label = f'{issue_label} (v. {volume}, n. {number})' elif volume and number: issue_label = f'{journal_label} (v. {volume}, n. {number})' jid = record['record_desc_group']['record_id'] issue = { 'uri': self.helper.issue_uri(jid, issue_id), 'label': issue_label, 'object_type': vocab.IssueText, 'identifiers': [self.helper.gci_number_id(issue_id)], 'referred_to_by': [], 'used_for': [], } if title: issue['identifiers'].append( vocab.PrimaryName(ident='', content=title)) if title_translated: issue['identifiers'].append( vocab.TranslatedTitle(ident='', content=title_translated)) if date: display_date = date.get('display_date') sort_year = date.get('sort_year') if display_date or sort_year: a_uri = issue['uri'] + f'-pub' a = vocab.Publishing(ident=a_uri, label=f'Publishing of {issue_label}') ts = model.TimeSpan(ident='') if display_date: ts._label = display_date ts.identified_by = vocab.DisplayName(ident='', content=display_date) if sort_year: try: year = int(sort_year) ts.begin_of_the_begin = '%04d-01-01:00:00:00Z' % ( year, ) ts.end_of_the_end = '%04d-01-01:00:00:00Z' % (year + 1, ) except: pass a.timespan = ts issue['used_for'].append(a) # TODO: # volume # number if note: issue['referred_to_by'].append(vocab.Note(ident='', content=note)) mlalo = MakeLinkedArtLinguisticObject() mlalo(issue) i = get_crom_object(issue) for a in issue.get('used_for', []): i.used_for = a record['^part'].append(issue)
def extract_monetary_amount(data): ''' Returns a `MonetaryAmount`, `StartingPrice`, or `EstimatedPrice` object based on properties of the supplied `data` dict. If no amount or currency data is found in found, returns `None`. For `EstimatedPrice`, values will be accessed from these keys: - amount: `est_price_amount` or `est_price` - currency: `est_price_currency` or `est_price_curr` - note: `est_price_note` or `est_price_desc` - bibliographic statement: `est_price_citation` For `StartingPrice`, values will be accessed from these keys: - amount: `start_price_amount` or `start_price` - currency: `start_price_currency` or `start_price_curr` - note: `start_price_note` or `start_price_desc` - bibliographic statement: `start_price_citation` For `MonetaryAmount` prices, values will be accessed from these keys: - amount: `price_amount` or `price` - currency: `price_currency` or `price_curr` - note: `price_note` or `price_desc` - bibliographic statement: `price_citation` ''' amount_type = 'Price' if 'est_price' in data: amnt = vocab.EstimatedPrice() price_amount = data.get('est_price_amount', data.get('est_price')) price_currency = data.get('est_price_currency', data.get('est_price_curr')) amount_type = 'Estimated Price' note = data.get('est_price_note', data.get('est_price_desc')) cite = data.get('est_price_citation') elif 'start_price' in data: amnt = vocab.StartingPrice() price_amount = data.get('start_price_amount', data.get('start_price')) price_currency = data.get('start_price_currency', data.get('start_price_curr')) amount_type = 'Starting Price' note = data.get('start_price_note', data.get('start_price_desc')) cite = data.get('start_price_citation') else: amnt = model.MonetaryAmount() price_amount = data.get('price_amount', data.get('price')) price_currency = data.get('price_currency', data.get('price_curr')) note = data.get('price_note', data.get('price_desc')) cite = data.get('price_citation') if price_amount or price_currency: if cite: amnt.referred_to_by = vocab.BibliographyStatement(content=cite) if note: amnt.referred_to_by = vocab.Note(content=note) if price_amount: try: value = price_amount value = value.replace('[?]', '') value = value.replace('?', '') value = value.strip() price_amount = float(value) amnt.value = price_amount except ValueError: amnt._label = price_amount amnt.identified_by = model.Name(content=price_amount) # warnings.warn(f'*** Not a numeric price amount: {value}') if price_currency: if price_currency in CURRENCY_MAPPING: try: price_currency = CURRENCY_MAPPING[price_currency.lower()] except KeyError: pass if price_currency in vocab.instances: amnt.currency = vocab.instances[price_currency] else: warnings.warn('*** No currency instance defined for %s' % (price_currency,)) if price_amount and price_currency: amnt._label = '%s %s' % (price_amount, price_currency) elif price_amount: amnt._label = '%s' % (price_amount,) return amnt return None
def __call__(self, data:dict, event_properties, date_modifiers): '''Add modeling data for an auction event''' cno = data['catalog_number'] auction_locations = event_properties['auction_locations'] event_experts = event_properties['experts'] event_commissaires = event_properties['commissaire'] auction = get_crom_object(data) catalog = data['_catalog']['_LOD_OBJECT'] location_data = data['location'] current = self.auction_event_location(location_data) if not current: print(f'*** Empty location data: {pprint.pformat(location_data)}') pprint.pprint(data) # helper.make_place is called here instead of using make_la_place as a separate graph node because the Place object # gets stored in the `auction_locations` object to be used in the second graph component # which uses the data to associate the place with auction lots. base_uri = self.helper.make_proj_uri('AUCTION-EVENT', cno, 'PLACE', '') record = get_crom_object(data.get('_record')) current_p = current locs = [] while current_p: l = current_p.get('name') if l: locs.append(l) current_p = current_p.get('part_of') loc = ', '.join(locs) if len(locs) else None canonical_place = self.helper.get_canonical_place(loc) if canonical_place: place = canonical_place place_data = add_crom_data(data={'uri': place.id}, what=place) else: place_data = self.helper.make_place(current, base_uri=base_uri, record=record) place = get_crom_object(place_data) if place: data['_locations'] = [place_data] auction.took_place_at = place auction_locations[cno] = place.clone(minimal=True) ts, begin, end = timespan_from_bound_components( data, date_modifiers, 'sale_begin_', 'begin', 'sale_end_', 'eoe' ) event_record = get_crom_object(data['_record']) for seq_no, expert in enumerate(data.get('expert', [])): self.helper.copy_source_information(expert, data), person = self.helper.add_person( expert, record=event_record, relative_id=f'expert-{seq_no+1}', role='expert' ) event_experts[cno].append(person.clone(minimal=True)) data['_organizers'].append(add_crom_data(data={}, what=person)) role_id = '' # self.helper.make_proj_uri('AUCTION-EVENT', cno, 'Expert', seq_no) role = vocab.Expert(ident=role_id, label=f'Role of Expert in the event {cno}') role.carried_out_by = person auction.part = role for seq_no, commissaire in enumerate(data.get('commissaire', [])): self.helper.copy_source_information(commissaire, data), person = self.helper.add_person( commissaire, record=event_record, relative_id=f'commissaire-{seq_no+1}', role='commissaire' ) event_commissaires[cno].append(person.clone(minimal=True)) data['_organizers'].append(add_crom_data(data={}, what=person)) role_id = '' # self.helper.make_proj_uri('AUCTION-EVENT', cno, 'Commissaire', seq_no) role = vocab.CommissairePriseur(ident=role_id, label=f'Role of Commissaire-priseur in the event {cno}') role.carried_out_by = person auction.part = role notes = data.get('notes') if notes: auction.referred_to_by = vocab.Note(ident='', content=notes) if 'links' in data: event_record = get_crom_object(data['_record']) links = data['links'] link_keys = set(links.keys()) - {'portal'} for p in links.get('portal', []): url = p['portal_url'] if url.startswith('http'): event_record.referred_to_by = vocab.WebPage(ident=url, label=url) else: warnings.warn(f'*** Portal URL value does not appear to be a valid URL: {url}') for k in link_keys: url = links[k] if isinstance(url, str): event_record.referred_to_by = vocab.WebPage(ident=url, label=url) else: print(f'*** not a URL string: {k}: {url}') if ts: auction.timespan = ts auction.referred_to_by = catalog return data
def model_imprint_group(self, record, data): if not data: return record.setdefault('referred_to_by', []) record.setdefault('used_for', []) record.setdefault('part_of', []) record.setdefault('_activities', []) record.setdefault('_groups', []) record.setdefault('_places', []) record.setdefault('identifiers', []) edition = data.get('edition') series_number = data.get('series_number') doi = data.get('doi') coden = data.get('coden') website = data.get('website_address') publishers = _as_list(data.get('publisher')) distributors = _as_list(data.get('distributor')) journal = data.get('journal_info') # imprint_group/journal_info/aata_journal_id # imprint_group/journal_info/aata_issue_id degree = data.get('thesis_degree') tr = data.get('technical_report_number') if edition: record['referred_to_by'].append( vocab.EditionStatement(ident='', content=edition)) if series_number: record['referred_to_by'].append( vocab.Note(ident='', content=series_number)) # TODO: classify this Note if doi: record['identifiers'].append( vocab.DoiIdentifier(ident='', content=doi)) if coden: record['identifiers'].append( vocab.CodenIdentifier(ident='', content=coden)) if website: record['referred_to_by'].append( vocab.Note(ident='', content=website)) article_label = record['label'] for i, publisher in enumerate(publishers): corp_id = publisher.get('gaia_corp_id') geog_id = publisher.get('publisher_location', {}).get('gaia_geog_id') a_uri = record['uri'] + f'-pub-{i}' a = vocab.Publishing(ident=a_uri, label=f'Publishing of {article_label}') if corp_id: uri = self.helper.corporate_body_uri(corp_id) g = model.Group(ident=uri) a.carried_out_by = g record['_groups'].append(add_crom_data({}, g)) if geog_id: uri = self.helper.place_uri(geog_id) p = model.Place(ident=uri) a.took_place_at = p record['_places'].append(add_crom_data({}, p)) record['used_for'].append(a) # record['_activities'].append(add_crom_data({}, a)) for i, distributor in enumerate(distributors): corp_id = distributor.get('gaia_corp_id') geog_id = distributor.get('distributor_location', {}).get('gaia_geog_id') a_uri = record['uri'] + f'-dist-{i}' a = vocab.Distributing(ident=a_uri, label=f'Distribution of {article_label}') if corp_id: uri = self.helper.corporate_body_uri(corp_id) g = model.Group(ident=uri) a.carried_out_by = g record['_groups'].append(add_crom_data({}, g)) if geog_id: uri = self.helper.place_uri(geog_id) p = model.Place(ident=uri) a.took_place_at = p record['_places'].append(add_crom_data({}, p)) record['used_for'].append(a) # record['_activities'].append(add_crom_data({}, a)) if journal: journal_id = journal.get('aata_journal_id') issue_id = journal.get('aata_issue_id') issue_uri = self.helper.issue_uri(journal_id, issue_id) issue = vocab.IssueText(ident=issue_uri) record['part_of'].append(add_crom_data({'uri': issue_uri}, issue)) if degree: record['referred_to_by'].append( vocab.Note(ident='', content=degree)) if tr: record['identifiers'].append(model.Identifier( ident='', content=tr)) # TODO: classify this Identifier
def extract_monetary_amount(data, add_citations=False, currency_mapping=CURRENCY_MAPPING, source_mapping=None, truncate_label_digits=2): ''' Returns a `MonetaryAmount`, `StartingPrice`, or `EstimatedPrice` object based on properties of the supplied `data` dict. If no amount or currency data is found in found, returns `None`. For `EstimatedPrice`, values will be accessed from these keys: - amount: `est_price_amount` or `est_price` - currency: `est_price_currency` or `est_price_curr` - note: `est_price_note` or `est_price_desc` - bibliographic statement: `est_price_citation` For `StartingPrice`, values will be accessed from these keys: - amount: `start_price_amount` or `start_price` - currency: `start_price_currency` or `start_price_curr` - note: `start_price_note` or `start_price_desc` - bibliographic statement: `start_price_citation` For `MonetaryAmount` prices, values will be accessed from these keys: - amount: `price_amount` or `price` - currency: `price_currency` or `price_curr` - note: `price_note` or `price_desc` - bibliographic statement: `price_citation` ''' amount_type = 'Price' if 'price' in data or 'price_amount' in data or 'amount' in data: amnt = model.MonetaryAmount(ident='') price_amount = data.get('price_amount', data.get('price', data.get('amount'))) price_currency = data.get( 'currency', data.get('price_currency', data.get('price_curr'))) note = data.get('price_note', data.get('price_desc', data.get('note'))) cite = data.get('price_citation', data.get('citation')) source = data.get('price_source', '') elif 'est_price' in data or 'est_price_amount' in data: amnt = vocab.EstimatedPrice(ident='') price_amount = data.get('est_price_amount', data.get('est_price')) price_currency = data.get( 'est_price_currency', data.get('est_price_curr', data.get('currency'))) amount_type = 'Estimated Price' note = data.get('est_price_note', data.get('est_price_desc', data.get('note'))) cite = data.get('est_price_citation', data.get('citation')) source = data.get('est_price_source', data.get('est_price_so', '')) elif 'start_price' in data or 'start_price_amount' in data: amnt = vocab.StartingPrice(ident='') price_amount = data.get('start_price_amount', data.get('start_price')) price_currency = data.get( 'start_price_currency', data.get('start_price_curr', data.get('currency'))) amount_type = 'Starting Price' note = data.get('start_price_note', data.get('start_price_desc', data.get('note'))) cite = data.get('start_price_citation', data.get('citation')) source = data.get('start_price_source', data.get('start_price_so', '')) elif 'ask_price' in data or 'ask_price_amount' in data: amnt = vocab.AskingPrice(ident='') price_amount = data.get('ask_price_amount', data.get('ask_price')) price_currency = data.get( 'ask_price_currency', data.get('ask_price_curr', data.get('currency'))) amount_type = 'Asking Price' note = data.get('ask_price_note', data.get('ask_price_desc', data.get('note'))) cite = data.get('ask_price_citation', data.get('citation')) source = data.get('ask_price_source', data.get('ask_price_so', '')) else: return None price_amount_label = price_amount if price_amount or price_currency: if cite and add_citations: amnt.referred_to_by = vocab.BibliographyStatement(ident='', content=cite) if note: amnt.referred_to_by = vocab.Note(ident='', content=note) if price_amount: try: value = price_amount value = value.replace('[?]', '') value = value.replace('?', '') value = value.strip() if re.search(re.compile(r',\d\d\d'), value): value = value.replace(',', '') value = float(value) label_fmt = '{:,.%df}' % truncate_label_digits price_amount_label = label_fmt.format(value) amnt.value = value except ValueError: amnt._label = price_amount amnt.identified_by = model.Name(ident='', content=price_amount) # warnings.warn(f'*** Not a numeric price amount: {value}') if price_currency: price_currency_key = price_currency try: price_currency_key = currency_mapping[ price_currency_key.lower()] except KeyError: pass if isinstance(price_currency_key, model.BaseResource): amnt.currency = price_currency_key elif price_currency_key in vocab.instances: amnt.currency = vocab.instances[price_currency_key] else: warnings.warn('*** No currency instance defined for %s' % (price_currency_key, )) if price_amount_label and price_currency: amnt._label = '%s %s' % (price_amount_label, price_currency) elif price_amount: amnt._label = '%s' % (price_amount, ) return amnt return None
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