def __call__(self, data: dict, post_sale_map, unique_catalogs, subject_genre, destruction_types_map): '''Add modeling for an object described by a sales record''' hmo = get_crom_object(data) parent = data['parent_data'] auction_data = parent.get('auction_of_lot') if auction_data: lno = str(auction_data['lot_number']) data.setdefault('identifiers', []) if not lno: warnings.warn(f'Setting empty identifier on {hmo.id}') data['identifiers'].append(vocab.LotNumber(ident='', content=lno)) else: warnings.warn(f'***** NO AUCTION DATA FOUND IN populate_object') cno = auction_data['catalog_number'] lno = auction_data['lot_number'] date = implode_date(auction_data, 'lot_sale_') lot = self.helper.shared_lot_number_from_lno( lno ) # the current key for this object; may be associated later with prev and post object keys now_key = (cno, lno, date) data['_locations'] = [] data['_events'] = [] record = self._populate_object_catalog_record(data, parent, lot, cno, parent['pi_record_no']) self._populate_object_visual_item(data, subject_genre) self._populate_object_destruction(data, parent, destruction_types_map) self.populate_object_statements(data) self._populate_object_present_location(data, now_key, destruction_types_map) self._populate_object_notes(data, parent, unique_catalogs) self._populate_object_prev_post_sales(data, now_key, post_sale_map) for p in data.get('portal', []): url = p['portal_url'] hmo.referred_to_by = vocab.WebPage(ident=url, label=url) if 'title' in data: title = data['title'] if not hasattr(hmo, '_label'): typestring = data.get('object_type', 'Object') hmo._label = f'{typestring}: “{title}”' del data['title'] shorter = truncate_with_ellipsis(title, 100) if shorter: description = vocab.Description(ident='', content=title) description.referred_to_by = record hmo.referred_to_by = description title = shorter t = vocab.PrimaryName(ident='', content=title) t.classified_as = model.Type( ident='http://vocab.getty.edu/aat/300417193', label='Title') t.referred_to_by = record data['identifiers'].append(t) for d in data.get('other_titles', []): title = d['title'] t = vocab.Name(ident='', content=title) data['identifiers'].append(t) return data
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
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 __call__(self, data: dict, event_properties): '''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) # 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', '') place_data = self.helper.make_place(current, base_uri=base_uri) place = get_crom_object(place_data) if place: data['_locations'] = [place_data] auction.took_place_at = place auction_locations[cno] = place.clone(minimal=True) begin = implode_date(data, 'sale_begin_', clamp='begin') end = implode_date(data, 'sale_end_', clamp='eoe') ts = timespan_from_outer_bounds(begin=begin, end=end, inclusive=True) event_properties['auction_dates'][cno] = (begin, end) 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 begin and end: ts.identified_by = model.Name(ident='', content=f'{begin} to {end}') elif begin: ts.identified_by = model.Name(ident='', content=f'{begin} onwards') elif end: ts.identified_by = model.Name(ident='', content=f'up to {end}') for p in data.get('portal', []): url = p['portal_url'] if url.startswith('http'): auction.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}' ) if ts: auction.timespan = ts auction.referred_to_by = catalog return data