Beispiel #1
0
    def _static_place_instances(self):
        '''
		Create static instances for every place mentioned in the unique_locations service data.
		'''
        places = self.helper.services.get('unique_locations',
                                          {}).get('places', {})
        instances = {}
        for name, data in places.items():
            place_data = None
            place = None
            components = []
            for k in ('Sovereign', 'Country', 'Province', 'State', 'County',
                      'City'):
                component_name = data.get(k.lower())
                if component_name:
                    components = [component_name] + components
                    rev = components
                    rev.reverse()
                    place_data = {
                        'name': component_name,
                        'type': k,
                        'part_of': place_data,
                        'uri': self.helper.make_shared_uri('PLACE', *rev)
                    }
                    parent = place
                    place_data = self.helper.make_place(place_data)
                    place = get_crom_object(place_data)
                    instances[', '.join(components)] = place
            if place:
                instances[name] = place
        return instances
Beispiel #2
0
 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
Beispiel #3
0
def add_imprint_orgs(data):
	'''
	Given a `dict` representing an "article," extract the "imprint organization" records
	and their role (e.g. publisher, distributor), and add add a new 'organizations' key
	to the dictionary containing an array of `dict`s representing the organizations.
	Also construct an Activity for each organization's role, and associate it with the
	article and organization (article --role--> organization).

	The resulting organization `dict` will contain these keys:

	* `_aata_record_id`: The identifier of the corresponding article
	* `_aata_record_organization_seq`: A integer identifying this organization
	                                   (unique within the scope of the article)
	* `label`: The name of the organization
	* `role`: The role the organization played in the article's creation (e.g. `'Publishing'`)
	* `properties`: A `dict` of additional properties associated with this organization's
	                role in the article creation (e.g. `DatesOfPublication`)
	* `names`: A `list` of names this organization may be identified by
	* `identifiers`: A `list` of (identifier, identifier type) pairs
	* `uid`: A unique ID for this organization
	* `uuid`: A unique UUID for this organization used in assigning it a URN

	'''
	lod_object = get_crom_object(data)
	organizations = []
	for o in data.get('_organizations', []):
		org = {k: v for k, v in o.items()}
		org_obj = vocab.Group(ident=org['uri'])
		add_crom_data(data=org, what=org_obj)

		event = model.Activity() # TODO: change to vocab.Publishing for publishing activities
		lod_object.used_for = event
		event.carried_out_by = org_obj

		properties = o.get('properties')
		role = o.get('role')
		if role is not None:
			activity_names = {
				'distributor': 'Distributing',
				'publisher': 'Publishing',
				# TODO: Need to also handle roles: Organization, Sponsor, University
			}
			if role.lower() in activity_names:
				event_label = activity_names[role.lower()]
				event._label = event_label
			else:
				print('*** No/unknown organization role (%r) found for imprint_group in %s:' % (
					role, lod_object,))
# 				pprint.pprint(o)

			if role == 'Publisher' and 'DatesOfPublication' in properties:
				pubdate = properties['DatesOfPublication']
				span = CleanDateToSpan.string_to_span(pubdate)
				if span is not None:
					event.timespan = span
		organizations.append(org)
	data['organizations'] = organizations
	return data
Beispiel #4
0
    def make_place(self, data: dict, base_uri=None):
        '''
		Given a dictionary representing data about a place, construct a model.Place object,
		assign it as the crom data in the dictionary, and return the dictionary.

		The dictionary keys used to construct the place object are:

		- name
		- type (one of: 'City', 'State', 'Province', or 'Country')
		- part_of (a recursive place dictionary)
		'''
        unique_locations = self.unique_locations
        TYPES = {
            'city': vocab.instances['city'],
            'province': vocab.instances['province'],
            'state': vocab.instances['province'],
            'country': vocab.instances['nation'],
        }

        if data is None:
            return None
        type_name = data.get('type', 'place').lower()

        name = data.get('name')
        label = name
        parent_data = data.get('part_of')

        place_type = TYPES.get(type_name)
        parent = None
        if parent_data:
            parent_data = self.make_place(parent_data, base_uri=base_uri)
            parent = get_crom_object(parent_data)
            if label:
                label = f'{label}, {parent._label}'

        placeargs = {}
        if label:
            placeargs['label'] = label
        if data.get('uri'):
            placeargs['ident'] = data['uri']
        elif label in unique_locations:
            data['uri'] = self.make_proj_uri('PLACE', label)
            placeargs['ident'] = data['uri']
        elif base_uri:
            data['uri'] = base_uri + urllib.parse.quote(label)
            placeargs['ident'] = data['uri']

        p = model.Place(**placeargs)
        if place_type:
            p.classified_as = place_type
        if name:
            p.identified_by = model.Name(ident='', content=name)
        else:
            warnings.warn(f'Place with missing name on {p.id}')
        if parent:
            p.part_of = parent
            data['part_of'] = parent_data
        return add_crom_data(data=data, what=p)
Beispiel #5
0
    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)
Beispiel #6
0
 def add_group(self, a, record=None, relative_id=None, **kwargs):
     self.add_uri(a, record_id=relative_id)
     self.add_names(a, referrer=record, group=True, **kwargs)
     self.add_props(a, **kwargs)
     self.make_la_org(a)
     g = get_crom_object(a)
     if record:
         g.referred_to_by = record
     return g
Beispiel #7
0
def add_aata_authors(data):
	'''
	Given a `dict` representing an "article," extract the authorship records
	and their role (e.g. author, editor). yield a new `dict`s for each such
	creator (subsequently referred to as simply "author").

	The resulting author `dict` will contain these keys:

	* `_aata_record_id`: The identifier of the corresponding article
	* `_aata_record_author_seq`: A integer identifying this author
	                             (unique within the scope of the article)
	* `label`: The name of the author
	* `creation_role`: The role the author played in the creation of the "article"
	                   (e.g. `'Author'`)
	* `names`: A `list` of names this organization may be identified by
	* `identifiers`: A `list` of (identifier, identifier type) pairs
	* `uid`: A unique ID for this organization
	* `parent`: The model object representing the corresponding article
	* `parent_data`: The `dict` representing the corresponding article
	* `events`: A `list` of `model.Creation` objects representing the part played by
	            the author in the article's creation event.
	'''
	lod_object = get_crom_object(data)
	event = model.Creation()
	lod_object.created_by = event

	authors = data.get('_authors', [])
	make_la_person = MakeLinkedArtPerson()
	for a in authors:
		make_la_person(a)
		person_name = a['label']
		person = get_crom_object(a)
		subevent = model.Creation()
		# TODO: The should really be asserted as object -created_by-> CreationEvent -part-> SubEvent
		# however, right now that assertion would get lost as it's data that belongs to the object,
		# and we're on the author's chain in the bonobo graph; object serialization has already happened.
		# we need to serialize the object's relationship to the creation event, and let it get merged
		# with the rest of the object's data.
		event.part = subevent
		role = a.get('creation_role')
		if role is not None:
			subevent._label = f'Creation sub-event for {role} by “{person_name}”'
		subevent.carried_out_by = person
	yield data
Beispiel #8
0
 def add_person(self, a, record=None, relative_id=None, **kwargs):
     self.add_uri(a, record_id=relative_id)
     self.add_names(a, referrer=record, **kwargs)
     self.add_props(a, **kwargs)
     auth_name = a.get('auth_name')
     if auth_name and self.is_anonymous_group(auth_name):
         self.make_la_org(a)
     else:
         self.make_la_person(a)
     return get_crom_object(a)
Beispiel #9
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 #10
0
    def __call__(self, data: dict, location_codes, unique_catalogs):
        '''Add information about the ownership of a physical copy of an auction catalog'''
        # Add the URI of this physical catalog to `unique_catalogs`. This data will be used
        # later to figure out which catalogs can be uniquely identified by a catalog number
        # and owner code (e.g. for owners who do not have multiple copies of a catalog).
        cno = data['catalog_number']
        owner_code = data['owner_code']
        copy_number = data.get('copy_number', '')
        owner_name = None
        entry_record = get_crom_object(data.get('_catalog'))
        with suppress(KeyError):
            owner_name = location_codes[owner_code]
            owner_uri = self.helper.make_proj_uri('ORGANIZATION',
                                                  'LOCATION-CODE', owner_code)
            data['_owner'] = {
                'label':
                owner_name,
                'uri':
                owner_uri,
                'referred_to_by': [entry_record],
                'identifiers': [
                    model.Name(ident='', content=owner_name),
                    model.Identifier(ident='', content=str(owner_code))
                ],
            }
            owner = model.Group(ident=owner_uri)
            owner.referred_to_by = entry_record
            add_crom_data(data['_owner'], owner)
            if not owner_code:
                warnings.warn(f'Setting empty identifier on {owner.id}')
            add_crom_data(data=data['_owner'], what=owner)
            catalog = get_crom_object(data)
            catalog.current_owner = owner

        owner_uri = self.helper.physical_catalog_uri(
            cno, owner_code, None
        )  # None here because we want a key that will stand in for all the copies belonging to a single owner
        copy_uri = self.helper.physical_catalog_uri(cno, owner_code,
                                                    copy_number)
        unique_catalogs[owner_uri].add(copy_uri)
        return data
Beispiel #11
0
 def set_possible_attribute(self, obj, prop, data):
     value = get_crom_object(data)
     if not value:
         return
     uncertain = data.get('uncertain', False)
     if uncertain:
         assignment = vocab.PossibleAssignment(ident='')
         assignment.assigned_property = prop
         assignment.assigned = value
         obj.attributed_by = assignment
     else:
         setattr(obj, prop, value)
Beispiel #12
0
    def set_lot_objects(self, lot, cno, lno, auction_of_lot_uri, data,
                        sale_type):
        '''Associate the set of objects with the auction lot.'''
        shared_lot_number = self.helper.shared_lot_number_from_lno(lno)
        set_type = vocab.AuctionLotSet if sale_type == 'Auction' else vocab.CollectionSet
        coll_label = f'Object Set for Lot {cno} {shared_lot_number}'
        coll = set_type(ident=f'{auction_of_lot_uri}-Set', label=coll_label)
        coll.identified_by = model.Name(ident='', content=coll_label)
        est_price = data.get('estimated_price')
        if est_price:
            coll.dimension = get_crom_object(est_price)
        start_price = data.get('start_price')
        if start_price:
            coll.dimension = get_crom_object(start_price)

        ask_price = data.get('ask_price')
        if ask_price:
            coll.dimension = get_crom_object(ask_price)

        lot.used_specific_object = coll
        data['_lot_object_set'] = add_crom_data(data={}, what=coll)
Beispiel #13
0
    def __call__(self, data, language_code_map):
        '''
		Given a `dict` representing an "article," extract the abstract records.
		yield a new `dict`s for each such record.

		The resulting asbtract `dict` will contain these keys:

		* `_LOD_OBJECT`: A `model.LinguisticObject` object representing the abstract
		* `_aata_record_id`: The identifier of the corresponding article
		* `_aata_record_author_seq`: A integer identifying this abstract
									 (unique within the scope of the article)
		* `content`: The text content of the abstract
		* `language`: A model object representing the declared langauge of the abstract (if any)
		* `author_abstract_flag`: A boolean value indicating whether the article's authors also
								  authored the abstract
		* `identifiers`: A `list` of (identifier, identifier type) pairs
		* `_authors`: The authorship information from the input article `dict`
		* `uid`: A unique ID for this abstract
		* `parent`: The model object representing the corresponding article
		* `parent_data`: The `dict` representing the corresponding article
		'''
        lod_object = get_crom_object(data)
        for a in data.get('_abstracts', []):
            abstract_dict = {
                k: v
                for k, v in a.items() if k not in ('language', )
            }
            abstract_uri = self.helper.make_proj_uri(
                'Abstract', data['_aata_record_id'],
                a['_aata_record_abstract_seq'])
            content = a.get('content')
            abstract = vocab.Abstract(ident=abstract_uri, content=content)
            abstract.refers_to = lod_object
            langcode = a.get('language')
            if langcode is not None:
                language = self.helper.language_object_from_code(
                    langcode, language_code_map)
                if language is not None:
                    abstract.language = language
                    abstract_dict['language'] = language

            if '_authors' in data:
                abstract_dict['_authors'] = data['_authors']

            # create a uid based on the AATA record id, the sequence number of the abstract
            # in that record, and which author we're handling right now
            abstract_dict.update({
                'parent_data': data,
                'uri': abstract_uri,
            })
            add_crom_data(data=abstract_dict, what=abstract)
            yield abstract_dict
Beispiel #14
0
    def __call__(self, data: dict, non_auctions):
        '''Add modeling for physical copies of an auction catalog'''
        catalog = get_crom_object(data['_catalog'])
        record = get_crom_object(data['_catalog_record'])
        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,
                                                     add_name=True)
        catalogObject.referred_to_by = record
        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
Beispiel #15
0
def make_issue(data: dict):
	parent_data = data['parent_data']
	data['part_of'] = [parent_data] # the issue is a part of the journal

	volume_number = data.get('volume')
	if volume_number is not None:
		volume_data = parent_data.get('volumes', {}).get(volume_number)
		volume = get_crom_object(volume_data)
		if volume:
			data['part_of'].append(volume_data) # the issue is a part of the volume


	return data
Beispiel #16
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 #17
0
 def model_place(self, data):
     country = data.get('country')
     state = data.get('state')
     if country:
         data['part_of'] = country
     if state:
         state['part_of'] = data.get('part_of')
         data['part_of'] = state
     pid = data['concept_group']['gaia_auth_id']
     data.setdefault('label', f'Place ({pid})')
     place_base = self.helper.make_proj_uri('Place:')
     mlap = MakeLinkedArtPlace(base_uri=place_base)
     mlap(data)
     place = get_crom_object(data)
Beispiel #18
0
    def __call__(self, data: dict):
        '''Add modeling for an auction event based on properties of the supplied `data` dict.'''
        cno = data['catalog_number']
        sale_type = data.get('non_auction_flag', 'Auction')
        auction, uid, uri = self.helper.sale_event_for_catalog_number(
            cno, sale_type)
        auction.identified_by = model.Name(ident='', content=auction._label)
        data['uid'] = uid
        data['uri'] = uri
        add_crom_data(data=data, what=auction)

        catalog = get_crom_object(data['_catalog'])
        data['_record'] = data['_catalog']
        return data
Beispiel #19
0
    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
Beispiel #20
0
    def add_person(self, a, record=None, relative_id=None, **kwargs):
        self.add_uri(a, record_id=relative_id)
        auth_name = a.get('auth_name')

        # is_group will be true here if this person record is a stand-in
        # for a group of people (e.g. all French people, or 17th century Germans)
        is_group = auth_name and self.is_anonymous_group(auth_name)
        self.add_names(a, group=is_group, referrer=record, **kwargs)
        self.add_props(a, **kwargs)
        if is_group:
            self.make_la_org(a)
        else:
            self.make_la_person(a)
        p = get_crom_object(a)
        if record:
            p.referred_to_by = record
        return p
Beispiel #21
0
def make_aata_org_event(o: dict):
	'''
	Given a `dict` representing an organization, create an `model.Activity` object to
	represent the organization's part in the "article" creation (associating any
	applicable publication timespan to the activity), associate the activity with the
	organization and the corresponding "article", and return a new `dict` that combines
	the input data with an `'events'` key having a `list` value containing the new
	activity object.

	For example,

	```
	make_aata_org_event({
		'event_label': 'Publishing',
		'publication_date_span': model.TimeSpan(...),
		...
	})
	```

	will return:

	```
	{
		'event_label': 'Publishing',
		'publication_date_span': model.TimeSpan(...),
		'events': [model.Activity(_label: 'Publishing', 'timespan': ts.TimeSpan(...))],
		...
	}
	```

	and also set the article object's `used_for` property to the new activity.
	'''
	event = model.Activity()
	lod_object = get_crom_object(o['parent_data'])
	lod_object.used_for = event
	event._label = o.get('event_label')
	if 'publication_date_span' in o:
		ts = o['publication_date_span']
		event.timespan = ts
	org = {k: v for k, v in o.items()}
	org.update({
		'events': [event],
	})
	yield org
Beispiel #22
0
    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)
Beispiel #23
0
    def _price_note(self, price):
        '''
		For lots with multiple payment records, the first is asserted as the real payment.
		The rest are turned into LinguisticObjects and associated with the payment as
		`referred_to_by`. This function constructs the content for that LinguisticObject,
		containing price, currency, citations, and notes.
		'''
        amnt = get_crom_object(price)
        try:
            value = amnt.value
        except:
            return None

        label = f'{value}'
        if hasattr(amnt, 'currency'):
            currency = amnt.currency
            label = f'{value} {currency._label}'

        notes = []
        cites = []
        if hasattr(amnt, 'referred_to_by'):
            for ref in amnt.referred_to_by:
                content = ref.content
                classification = {c._label for c in ref.classified_as}
                if 'Note' in classification:
                    notes.append(content)
                else:
                    cites.append(content)

        strings = []
        if notes:
            strings.append(', '.join(notes))
        if cites:
            strings.append(', '.join(cites))
        if strings:
            content = '; '.join(strings)
            label += f'; {content}'
        return label
Beispiel #24
0
    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']
        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.gri_number_id(cno)

        if not sno:
            warnings.warn(f'Setting empty identifier on {catalog.id}')
        catalog.identified_by = vocab.SystemNumber(ident='', content=sno)
        notes = data.get('notes')
        if notes:
            note = vocab.Note(ident='', content=parent['notes'])
            catalog.referred_to_by = note
        return d
Beispiel #25
0
    def handle_statements(self, data):
        record = get_crom_object(data['_entry_record'])
        data['referred_to_by'].append(record)

        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)
Beispiel #26
0
    def _populate_object_catalog_record(self, data: dict, parent, lot, cno,
                                        rec_num):
        hmo = get_crom_object(data)

        catalog_uri = self.helper.make_proj_uri('CATALOG', cno)
        catalog = vocab.AuctionCatalogText(ident=catalog_uri,
                                           label=f'Sale Catalog {cno}')

        record_uri = self.helper.make_proj_uri('CATALOG', cno, 'RECORD',
                                               rec_num)
        lot_object_id = parent['lot_object_id']

        puid = parent.get('persistent_puid')
        puid_id = self.helper.gri_number_id(puid)

        record = vocab.ParagraphText(
            ident=record_uri,
            label=
            f'Sale recorded in catalog: {lot_object_id} (record number {rec_num})'
        )
        record_data = {'uri': record_uri}
        record_data['identifiers'] = [
            model.Name(ident='', content=f'Record of sale {lot_object_id}'),
            puid_id
        ]
        record.part_of = catalog

        if parent.get('transaction'):
            record.referred_to_by = vocab.PropertyStatusStatement(
                ident='',
                label='Transaction type for sales record',
                content=parent['transaction'])
        record.about = hmo

        data['_record'] = add_crom_data(data=record_data, what=record)
        return record
Beispiel #27
0
    def __call__(self, data: dict, *, attribution_modifiers,
                 attribution_group_types):
        '''Add modeling for artists as people involved in the production of an object'''
        hmo = get_crom_object(data)
        data['_organizations'] = []
        data['_original_objects'] = []

        try:
            hmo_label = f'{hmo._label}'
        except AttributeError:
            hmo_label = 'object'
        event_id = hmo.id + '-Prod'
        event = model.Production(ident=event_id,
                                 label=f'Production event for {hmo_label}')
        hmo.produced_by = event

        artists = data.get('_artists', [])

        sales_record = get_crom_object(data['_record'])
        pi = self.helper.person_identity

        for a in artists:
            a.setdefault('referred_to_by', [])
            a.update({
                'pi_record_no': data['pi_record_no'],
                'ulan': a['artist_ulan'],
                'auth_name': a['art_authority'],
                'name': a['artist_name']
            })
            if a.get('biography'):
                bio = a['biography']
                del a['biography']
                cite = vocab.BiographyStatement(ident='', content=bio)
                a['referred_to_by'].append(cite)

        def is_or_anon(data: dict):
            if pi.is_anonymous(data):
                mods = {
                    m.lower().strip()
                    for m in data.get('attrib_mod_auth', '').split(';')
                }
                return 'or' in mods
            return False

        or_anon_records = [is_or_anon(a) for a in artists]
        uncertain_attribution = any(or_anon_records)
        for seq_no, a in enumerate(artists):
            attribute_assignment_id = event.id + f'-artist-assignment-{seq_no}'
            if is_or_anon(a):
                # do not model the "or anonymous" records; they turn into uncertainty on the other records
                continue
            person = self.helper.add_person(a,
                                            record=sales_record,
                                            relative_id=f'artist-{seq_no+1}',
                                            role='artist')
            artist_label = a.get('role_label')

            mod = a.get('attrib_mod_auth', '')
            mods = CaseFoldingSet({m.strip() for m in mod.split(';')} - {''})
            attrib_assignment_classes = [model.AttributeAssignment]

            if uncertain_attribution or 'or' in mods:
                attrib_assignment_classes.append(vocab.PossibleAssignment)

            if mods:
                # TODO: this should probably be in its own JSON service file:
                STYLE_OF = attribution_modifiers['style of']
                FORMERLY_ATTRIBUTED_TO = attribution_modifiers[
                    'formerly attributed to']
                ATTRIBUTED_TO = attribution_modifiers['attributed to']
                COPY_AFTER = attribution_modifiers['copy after']
                PROBABLY = attribution_modifiers['probably by']
                POSSIBLY = attribution_modifiers['possibly by']
                UNCERTAIN = attribution_modifiers['uncertain']

                GROUP_TYPES = set(attribution_group_types.values())
                GROUP_MODS = {
                    k
                    for k, v in attribution_group_types.items()
                    if v in GROUP_TYPES
                }

                if 'copy by' in mods:
                    # equivalent to no modifier
                    pass
                elif ATTRIBUTED_TO.intersects(mods):
                    # equivalent to no modifier
                    pass
                elif STYLE_OF.intersects(mods):
                    assignment = vocab.make_multitype_obj(
                        *attrib_assignment_classes,
                        ident=attribute_assignment_id,
                        label=f'In the style of {artist_label}')
                    event.attributed_by = assignment
                    assignment.assigned_property = 'influenced_by'
                    assignment.property_classified_as = vocab.instances[
                        'style of']
                    assignment.assigned = person
                    continue
                elif mods.intersects(GROUP_MODS):
                    mod_name = list(GROUP_MODS
                                    & mods)[0]  # TODO: use all matching types?
                    clsname = attribution_group_types[mod_name]
                    cls = getattr(vocab, clsname)
                    group_label = f'{clsname} of {artist_label}'
                    group_id = a['uri'] + f'-{clsname}'
                    group = cls(ident=group_id, label=group_label)
                    formation = model.Formation(
                        ident='', label=f'Formation of {group_label}')
                    formation.influenced_by = person
                    group.formed_by = formation
                    group_data = add_crom_data({'uri': group_id}, group)
                    data['_organizations'].append(group_data)

                    subevent_id = event_id + f'-{seq_no}'  # TODO: fix for the case of post-sales merging
                    subevent = model.Production(
                        ident=subevent_id,
                        label=f'Production sub-event for {group_label}')
                    subevent.carried_out_by = group

                    if uncertain_attribution:
                        assignment = vocab.make_multitype_obj(
                            *attrib_assignment_classes,
                            ident=attribute_assignment_id,
                            label=f'Possibly attributed to {group_label}')
                        event.attributed_by = assignment
                        assignment.assigned_property = 'part'
                        assignment.assigned = subevent
                    else:
                        event.part = subevent
                    continue
                elif FORMERLY_ATTRIBUTED_TO.intersects(mods):
                    # the {uncertain_attribution} flag does not apply to this branch, because this branch is not making a statement
                    # about a previous attribution. the uncertainty applies only to the current attribution.
                    assignment = vocab.ObsoleteAssignment(
                        ident=attribute_assignment_id,
                        label=f'Formerly attributed to {artist_label}')
                    event.attributed_by = assignment
                    assignment.assigned_property = 'carried_out_by'
                    assignment.assigned = person
                    continue
                elif UNCERTAIN.intersects(mods):
                    if POSSIBLY.intersects(mods):
                        attrib_assignment_classes.append(
                            vocab.PossibleAssignment)
                        assignment = vocab.make_multitype_obj(
                            *attrib_assignment_classes,
                            ident=attribute_assignment_id,
                            label=f'Possibly attributed to {artist_label}')
                        assignment._label = f'Possibly by {artist_label}'
                    else:
                        attrib_assignment_classes.append(
                            vocab.ProbableAssignment)
                        assignment = vocab.make_multitype_obj(
                            *attrib_assignment_classes,
                            ident=attribute_assignment_id,
                            label=f'Probably attributed to {artist_label}')
                        assignment._label = f'Probably by {artist_label}'
                    event.attributed_by = assignment
                    assignment.assigned_property = 'carried_out_by'
                    assignment.assigned = person
                    continue
                elif COPY_AFTER.intersects(mods):
                    # the {uncertain_attribution} flag does not apply to this branch, because this branch is not making a statement
                    # about the artist of the work, but about the artist of the original work that this work is a copy of.
                    cls = type(hmo)
                    original_id = hmo.id + '-Orig'
                    original_label = f'Original of {hmo_label}'
                    original_hmo = cls(ident=original_id, label=original_label)
                    original_event_id = original_hmo.id + '-Prod'
                    original_event = model.Production(
                        ident=original_event_id,
                        label=f'Production event for {original_label}')
                    original_hmo.produced_by = original_event

                    original_subevent_id = original_event_id + f'-{seq_no}'  # TODO: fix for the case of post-sales merging
                    original_subevent = model.Production(
                        ident=original_subevent_id,
                        label=f'Production sub-event for {artist_label}')
                    original_event.part = original_subevent
                    original_subevent.carried_out_by = person

                    event.influenced_by = original_hmo
                    data['_original_objects'].append(
                        add_crom_data(data={}, what=original_hmo))
                    continue
                elif mods & {'or', 'and'}:
                    pass
                else:
                    print(f'UNHANDLED attrib_mod_auth VALUE: {mods}')
                    pprint.pprint(a)
                    continue

            subprod_path = self.helper.make_uri_path(*a["uri_keys"])
            subevent_id = event_id + f'-{subprod_path}'
            subevent = model.Production(
                ident=subevent_id,
                label=f'Production sub-event for {artist_label}')
            subevent.carried_out_by = person
            if uncertain_attribution or 'or' in mods:
                assignment = vocab.make_multitype_obj(
                    *attrib_assignment_classes,
                    ident=attribute_assignment_id,
                    label=f'Possibly attributed to {artist_label}')
                event.attributed_by = assignment
                assignment.assigned_property = 'part'
                assignment.assigned = subevent
            else:
                event.part = subevent
        data['_artists'] = [a for a in artists if not is_or_anon(a)]
        return data
Beispiel #28
0
    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
Beispiel #29
0
    def _populate_object_present_location(self, data: dict, now_key,
                                          destruction_types_map):
        hmo = get_crom_object(data)
        location = data.get('present_location')
        if location:
            loc = location.get('geog')
            note = location.get('note')
            if loc:
                if 'destroyed ' in loc.lower():
                    self.populate_destruction_events(
                        data, loc, type_map=destruction_types_map)
                elif isinstance(note, str) and 'destroyed ' in note.lower():
                    # the object was destroyed, so any "present location" data is actually
                    # an indication of the location of destruction.
                    self.populate_destruction_events(
                        data,
                        note,
                        type_map=destruction_types_map,
                        location=loc)
                else:
                    # TODO: if `parse_location_name` fails, still preserve the location string somehow
                    current = parse_location_name(
                        loc, uri_base=self.helper.uid_tag_prefix)
                    inst = location.get('inst')
                    if inst:
                        owner_data = {
                            'label': f'{inst} ({loc})',
                            'identifiers':
                            [model.Name(ident='', content=inst)]
                        }
                        ulan = None
                        with suppress(ValueError, TypeError):
                            ulan = int(location.get('insi'))
                        if ulan:
                            owner_data['ulan'] = ulan
                            owner_data['uri'] = self.helper.make_proj_uri(
                                'ORG', 'ULAN', ulan)
                        else:
                            owner_data['uri'] = self.helper.make_proj_uri(
                                'ORG', 'NAME', inst, 'PLACE', loc)
                    else:
                        owner_data = {
                            'label':
                            '(Anonymous organization)',
                            'uri':
                            self.helper.make_proj_uri('ORG', 'CURR-OWN',
                                                      *now_key),
                        }

                    if note:
                        owner_data['note'] = note

                    base_uri = hmo.id + '-Place,'
                    place_data = self.helper.make_place(current,
                                                        base_uri=base_uri)
                    place = get_crom_object(place_data)

                    make_la_org = pipeline.linkedart.MakeLinkedArtOrganization(
                    )
                    owner_data = make_la_org(owner_data)
                    owner = get_crom_object(owner_data)

                    acc = location.get('acc')
                    if acc:
                        acc_number = vocab.AccessionNumber(ident='',
                                                           content=acc)
                        hmo.identified_by = acc_number
                        assignment = model.AttributeAssignment(ident='')
                        assignment.carried_out_by = owner
                        acc_number.assigned_by = assignment

                    owner.residence = place
                    data['_locations'].append(place_data)
                    data['_final_org'] = owner_data
            else:
                pass  # there is no present location place string
Beispiel #30
0
    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))