def test_list_my_props(self): p1 = model.Person() p1.classified_as = model.Type() props = p1.list_my_props() self.assertEqual(set(props), set(['classified_as', 'id'])) props = p1.list_my_props(filter=model.Type) self.assertEqual(props, ['classified_as'])
def _populate_object_visual_item(self, data: dict, subject_genre): hmo = get_crom_object(data) title = data.get('title') title = truncate_with_ellipsis(title, 100) or title # The visual item 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 visual items (of which there should only be one per object) vi_uri = hmo.id + '-VisItem' vi = model.VisualItem(ident=vi_uri) vidata = {'uri': vi_uri} if title: vidata['label'] = f'Visual work of “{title}”' sales_record = get_crom_object(data['_record']) vidata['names'] = [(title, {'referred_to_by': [sales_record]})] for key in ('genre', 'subject'): if key in data: values = [v.strip() for v in data[key].split(';')] for value in values: for prop, mapping in subject_genre.items(): if value in mapping: aat_url = mapping[value] type = model.Type(ident=aat_url, label=value) setattr(vi, prop, type) data['_visual_item'] = add_crom_data(data=vidata, what=vi) hmo.shows = vi
def test_aa_check(self): # Make sure that some other test hasn't set it try: del model.AttributeAssignment.set_assigned_property except: pass t = model.Type() aa = model.AttributeAssignment() # First check that aa accepts a type aa.assigned_property = t # And will not accept a string self.assertRaises(model.DataError, aa.__setattr__, "assigned_property", "classified_as") # Check we can set anything to assigned / assigned_to aa.assigned_property = None aa.assigned = aa aa.assigned_to = aa self.assertEqual(aa.assigned, aa) self.assertEqual(aa.assigned_to, aa) vocab.add_attribute_assignment_check() # This should fail right now as can't classify as an AA self.assertRaises(model.DataError, aa.__setattr__, "assigned_property", "classified_as") aa.assigned = None aa.assigned_to = None aa.assigned = t aa.assigned_to = t aa.assigned_property = "classified_as" self.assertEqual(aa.assigned_property, 'classified_as')
def set_properties(self, data, thing): super().set_properties(data, thing) title_type = model.Type( ident='http://vocab.getty.edu/aat/300417193', label='Title') # TODO: is this the right aat URI? if 'label' in data: set_la_name(thing, data['label'], title_type, set_label=True) if 'title' in data: # TODO: This needs to be a PrimaryName, not a Name classified as a Title title = data['title'] if isinstance(title, str): set_la_name(thing, title, title_type, set_label=True) elif isinstance(title, (list, tuple)): value, *properties = title n = set_la_name(thing, value, title_type, set_label=True) n.classified_as = title_type self.set_lo_properties(n, *properties) thing.identified_by = n parents = data.get('part_of', []) for parent_data in parents: parent = get_crom_object(parent_data) thing.part_of = parent for carried in data.get('carries', []): lo = get_crom_object(carried) thing.carries = lo for coll in data.get('member_of', []): thing.member_of = coll for annotation in data.get('annotations', []): a = model.Annotation(ident='', content=annotation) thing.carries = a
def role_type(self, role): author_roles = self.services['author_roles'] code = author_roles.get(role) if code: t = model.Type(ident='http://vocab.getty.edu/aat/' + code, label=role) return t return None
def place_classification(self, name): place_types = self.services['place_types'] if name in place_types: aat = place_types[name] return model.Type(ident=f'http://vocab.getty.edu/aat/{aat}', label=name) else: warnings.warn(f'*** No matching AAT code for place type: {name}') return None
def model_classification_group(self, record, data): record.setdefault('classified_as', []) code = data['class_code'] name = data['class_name'] uri = self.helper.make_proj_uri('Classification', code) t = model.Type(ident=uri, label=name) record['classified_as'].append(t)
def test_collapse_json(self): p = model.Person() p.classified_as = model.Type(ident="http://example.org/Type", label="Test") res1 = model.factory.toString(p, compact=False, collapse=60) # all new lines res2 = model.factory.toString(p, compact=False, collapse=120) # compact list of type self.assertEqual(len(res1.splitlines()), 12) self.assertEqual(len(res2.splitlines()), 6)
def set_properties(self, data, thing): super().set_properties(data, thing) with suppress(ValueError, TypeError): ulan = int(data.get('ulan')) if ulan: thing.exact_match = model.BaseResource( ident=f'http://vocab.getty.edu/ulan/{ulan}') if 'name' in data: title_type = model.Type( ident='http://vocab.getty.edu/aat/300417193', label='Title') name = data['name'] if name: if isinstance(name, str): set_la_name(thing, name, title_type, set_label=True) elif isinstance(name, (list, tuple)): value, *properties = name n = model.Name(ident='', content=value) n.classified_as = title_type self.set_lo_properties(n, *properties) thing.identified_by = n for uri in data.get('exact_match', []): thing.exact_match = uri for sdata in data.get('sojourns', []): label = sdata.get('label', 'Sojourn activity') stype = sdata.get('type', model.Activity) act = stype(ident='', label=label) ts = get_crom_object(sdata.get('timespan')) place = get_crom_object(sdata.get('place')) act.timespan = ts act.took_place_at = place thing.carried_out = act self.set_referred_to_by(sdata, act) # Locations are names of residence places (P74 -> E53) # XXX FIXME: Places are their own model if 'places' in data: for p in data['places']: if isinstance(p, model.Place): pl = p elif isinstance(p, dict): pl = get_crom_object(p) else: pl = model.Place(ident='', label=p) #pl._label = p['label'] #nm = model.Name() #nm.content = p['label'] #pl.identified_by = nm #for s in p['sources']: # l = model.LinguisticObject(ident="urn:uuid:%s" % s[1]) # l._label = _row_label(s[2], s[3], s[4]) # pl.referred_to_by = l thing.residence = pl
def test_collapse_json(self): model.factory.auto_id_type = "uuid" model.factory.base_url = "http://lod.example.org/museum/" model.factory.context_uri = "https://linked.art/ns/v1/linked-art.json" p = model.Person() p.classified_as = model.Type(ident="http://example.org/Type", label="Test") res1 = model.factory.toString(p, compact=False, collapse=60) # all new lines res2 = model.factory.toString(p, compact=False, collapse=120) # compact list of type self.assertEqual(len(res1.splitlines()), 12) self.assertEqual(len(res2.splitlines()), 6)
def model_index_group(record, data): record.setdefault('indexing', []) term = data['index_term'] auth_type = data.get('gaia_auth_type') opids = _as_list(data.get('other_persistent_id')) for opid in opids: eid = opid['external_id'] ename = opid['external_name'] if ename in ('AAT', 'ULAN', 'TGN'): v = ename.lower() uri = f'http://vocab.getty.edu/{v}/{eid}' t = model.Type(ident=uri, label=term) record['indexing'].append(t)
def test_ordering(self): p = model.Person(label="Person") p.classified_as = model.Type(ident="type-uri") p.referred_to_by = model.LinguisticObject(content="text") p.dimension = model.Dimension(value=1) outstr = model.factory.toString(p) lbl = outstr.index("_label") clsf = outstr.index("classified_as") r2b = outstr.index("referred_to_by") dim = outstr.index("dimension") self.assertTrue(lbl < clsf) self.assertTrue(clsf < r2b) self.assertTrue(r2b < dim)
def _populate_object_visual_item(self, data: dict, subject_genre): hmo = get_crom_object(data) title = data.get('title') title = truncate_with_ellipsis(title, 100) or title vi_id = hmo.id + '-VisItem' vi = model.VisualItem(ident=vi_id) vidata = {'uri': vi_id} if title: vidata['label'] = f'Visual work of “{title}”' sales_record = get_crom_object(data['_record']) vidata['names'] = [(title, {'referred_to_by': [sales_record]})] for key in ('genre', 'subject'): if key in data: values = [v.strip() for v in data[key].split(';')] for value in values: for prop, mapping in subject_genre.items(): if value in mapping: aat_url = mapping[value] type = model.Type(ident=aat_url, label=value) setattr(vi, prop, type) data['_visual_item'] = add_crom_data(data=vidata, what=vi) hmo.shows = vi
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 set_properties(self, data, thing): super().set_properties(data, thing) # TODO: this whole title_type thing isn't right. most of the identifiers below aren't titles title_type = model.Type(ident='http://vocab.getty.edu/aat/300417193', label='Title') name = None if 'label' in data: name = set_la_name(thing, data['label'], title_type, set_label=True) for author in data.get('created_by', []): thing.created_by = author for a in data.get('used_for', []): thing.used_for = a for a in data.get('about', []): thing.about = a for c in data.get('classified_as', []): thing.classified_as = c for t in data.get('translations', []): n = set_la_name(thing, t, title_type) if name is not None: n.translation_of = name for content, itype, notes in data.get('qualified_identifiers', []): ident = itype(content=content) if not content: warnings.warn(f'Setting empty identifier on {thing.id}') thing.identified_by = ident for n in notes: ident.referred_to_by = n code_type = None # TODO: is there a model.Type value for this sort of code? for c in data.get('classifications', []): if isinstance(c, model.Type): classification = c else: cid, label = c name = model.Name() name.classified_as = title_type name.content = label classification = model.Type(label=label) if not label: warnings.warn(f'Setting empty name on {classification.id}') classification.identified_by = name code = model.Identifier() code.classified_as = code_type if not cid: warnings.warn(f'Setting empty identifier on {code.id}') code.content = cid classification.identified_by = code thing.about = classification for c in data.get('indexing', []): if isinstance(c, tuple): cid, label = c name = model.Name() name.classified_as = title_type name.content = label indexing = model.Type(label=label) if not label: warnings.warn(f'Setting empty name on {indexing.id}') indexing.identified_by = name code = model.Identifier() code.classified_as = code_type code.content = cid if not cid: warnings.warn(f'Setting empty identifier on {code.id}') indexing.identified_by = code else: indexing = c thing.about = indexing parents = data.get('part_of', []) for parent_data in parents: parent = get_crom_object(parent_data) thing.part_of = parent children = data.get('part', []) for child_data in children: child = get_crom_object(child_data) thing.part = child for carrier in data.get('carried_by', []): hmo = get_crom_object(carrier) thing.carried_by = hmo for dimension in data.get('dimensions', []): thing.dimension = dimension
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 test_type_patch(self): t = model.Type("http://vocab.getty.edu/aat/5") nt = t._toJSON() self.assertEqual(nt, "aat:5")