def relation_insert(origin_id: int) -> Union[str, Response]: origin = Entity.get_by_id(origin_id) form = build_form('actor_actor_relation') form.relation_origin_id.data = origin.id if form.validate_on_submit(): Transaction.begin() try: for actor in Entity.get_by_ids(ast.literal_eval(form.actor.data)): if form.inverse.data: link_ = Link.get_by_id( actor.link('OA7', origin, form.description.data)[0]) else: link_ = Link.get_by_id( origin.link('OA7', actor, form.description.data)[0]) link_.set_dates(form) link_.type = get_link_type(form) link_.update() Transaction.commit() flash(_('entity created'), 'info') except Exception as e: # pragma: no cover Transaction.rollback() logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') if hasattr(form, 'continue_') and form.continue_.data == 'yes': return redirect(url_for('relation_insert', origin_id=origin_id)) return redirect( url_for('entity_view', id_=origin.id) + '#tab-relation') return render_template( 'display_form.html', form=form, title=_('relation'), crumbs=[[_('actor'), url_for('index', view='actor')], origin, '+ ' + uc_first(_('relation'))])
def reference_link_update(link_: Link, origin: Entity) -> Union[str, Response]: origin = Entity.get_by_id(origin.id) form = AddReferenceForm() del form.reference if form.validate_on_submit(): link_.description = form.page.data link_.update() flash(_('info update'), 'info') tab = link_.range.class_.view if origin.class_.view == 'reference' \ else 'reference' return redirect(f"{url_for('view', id_=origin.id)}#tab-{tab}") form.save.label.text = _('save') form.page.data = link_.description if link_.domain.class_.name == 'external_reference': form.page.label.text = uc_first(_('link text')) return render_template( 'display_form.html', form=form, crumbs=[ [_(origin.class_.view), url_for('index', view=origin.class_.view)], origin, link_.domain if link_.domain.id != origin.id else link_.range, _('edit')])
def member_update(id_: int, origin_id: int) -> Union[str, Response]: link_ = Link.get_by_id(id_) domain = Entity.get_by_id(link_.domain.id) range_ = Entity.get_by_id(link_.range.id) origin = range_ if origin_id == range_.id else domain form = build_form('actor_function', link_) if form.validate_on_submit(): Transaction.begin() try: link_.delete() link_ = Link.get_by_id( domain.link('P107', range_, form.description.data)[0]) link_.set_dates(process_form_dates(form)) link_.type = get_link_type(form) link_.update() Transaction.commit() except Exception as e: # pragma: no cover Transaction.rollback() logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') return redirect( f"{url_for('view', id_=origin.id)}" f"#tab-member{'-of' if origin.id == range_.id else ''}") form.save.label.text = _('save') related = range_ if origin_id == domain.id else domain return render_template( 'display_form.html', form=form, crumbs=[[_('actor'), url_for('index', view='actor')], origin, related, _('edit')])
def save(entity: Entity, data: dict[str, str], types: list[dict[str, Any]]) -> None: for dict_ in types: Link.delete_(dict_['link_id']) SexEstimation.prepare_feature_types() for name, item in data.items(): entity.link('P2', g.types[SexEstimation.features[name]['id']], item)
def get_entities_linked_to_special_type(id_: int) -> list[Entity]: domain_ids = [ link_['domain_id'] for link_ in Link.get_links_by_type(g.types[id_]) ] range_ids = [ link_['range_id'] for link_ in Link.get_links_by_type(g.types[id_]) ] return get_entities_by_ids(range_ids + domain_ids)
def admin_check_dates() -> str: tabs = { 'dates': Tab('invalid_dates', table=Table( ['name', 'class', 'type', 'created', 'updated', 'description'])), 'link_dates': Tab('invalid_link_dates', table=Table(['link', 'domain', 'range'])), 'involvement_dates': Tab('invalid_involvement_dates', table=Table( ['actor', 'event', 'class', 'involvement', 'description'])) } for entity in Entity.get_invalid_dates(): tabs['dates'].table.rows.append([ link(entity), entity.class_.label, link(entity.standard_type), format_date(entity.created), format_date(entity.modified), entity.description ]) for link_ in Link.get_invalid_link_dates(): name = '' if link_.property.code == 'OA7': # pragma: no cover name = 'relation' elif link_.property.code == 'P107': # pragma: no cover name = 'member' elif link_.property.code in ['P11', 'P14', 'P22', 'P23']: name = 'involvement' tabs['link_dates'].table.rows.append([ link( _(name), url_for('link_update', id_=link_.id, origin_id=link_.domain.id)), link(link_.domain), link(link_.range) ]) for link_ in Link.invalid_involvement_dates(): event = link_.domain actor = link_.range data = [ link(actor), link(event), event.class_.label, link_.type.name if link_.type else '', link_.description, link(_('edit'), url_for('link_update', id_=link_.id, origin_id=actor.id)) ] tabs['involvement_dates'].table.rows.append(data) for tab in tabs.values(): tab.buttons = [manual('admin/data_integrity_checks')] if not tab.table.rows: # pragma: no cover tab.content = _('Congratulations, everything looks fine!') return render_template( 'tabs.html', tabs=tabs, title=_('admin'), crumbs=[[_('admin'), f"{url_for('admin_index')}#tab-data"], _('check dates')])
def relation_update(link_: Link, domain: Entity, range_: Entity, origin: Entity) -> Union[str, Response]: origin = range_ if origin.id == range_.id else domain related = range_ if origin.id == domain.id else domain form = build_form('actor_actor_relation', link_) if form.validate_on_submit(): Transaction.begin() try: link_.delete() if form.inverse.data: link_ = Link.get_by_id( related.link('OA7', origin, form.description.data)[0]) else: link_ = Link.get_by_id( origin.link('OA7', related, form.description.data)[0]) link_.set_dates(process_form_dates(form)) link_.type = get_link_type(form) link_.update() Transaction.commit() flash(_('info update'), 'info') except Exception as e: # pragma: no cover Transaction.rollback() logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') return redirect(f"{url_for('view', id_=origin.id)}#tab-relation") if origin.id == range_.id: form.inverse.data = True return render_template( 'display_form.html', form=form, title=_('relation'), crumbs=[[_('actor'), url_for('index', view='actor')], origin, related, _('edit')])
def get_links(entity: Entity) -> Dict[Any, list]: d = defaultdict(list) for link in Link.get_links(entity.id): d[link.property.i18n['en'].replace(' ', '_') + '_' + link.range.class_.name].append(link.range.name) for link in Link.get_links(entity.id, inverse=True): d[link.property.i18n_inverse['en'].replace(' ', '_') + '_' + link.domain.class_.name if link.property.i18n_inverse['en'] else link.property.i18n['en'].replace(' ', '_') + '_' + link.domain.class_.name].append(link.domain.name) d.pop('has_type_type', None) return d
def link_string( self, code: str, range_: str, description: Optional[str] = None, inverse: bool = False) -> None: if not range_: return # pragma: no cover # range_ = string value from a form, can be empty, int or int list # e.g. '', '1', '[]', '[1, 2]' ids = ast.literal_eval(range_) ids = [int(id_) for id_ in ids] if isinstance(ids, list) else [int(ids)] Link.insert(self, code, Entity.get_by_ids(ids), description, inverse)
def admin_check_link_duplicates( delete: Optional[str] = None) -> Union[str, Response]: if delete: count = Link.delete_link_duplicates() logger.log('info', 'admin', f"Deleted duplicate links: {count}") flash(f"{_('deleted links')}: {count}", 'info') return redirect(url_for('admin_check_link_duplicates')) table = Table([ 'domain', 'range', 'property_code', 'description', 'type_id', 'begin_from', 'begin_to', 'begin_comment', 'end_from', 'end_to', 'end_comment', 'count']) for row in Link.check_link_duplicates(): table.rows.append([ link(Entity.get_by_id(row['domain_id'])), link(Entity.get_by_id(row['range_id'])), link(g.properties[row['property_code']]), row['description'], link(g.types[row['type_id']]) if row['type_id'] else '', format_date(row['begin_from']), format_date(row['begin_to']), row['begin_comment'], format_date(row['end_from']), format_date(row['end_to']), row['end_comment'], row['count']]) if not table.rows: # Check single types for multiple use table = Table( ['entity', 'class', 'base type', 'incorrect multiple types']) for row in Link.check_single_type_duplicates(): remove_links = [] for type_ in row['offending_types']: url = url_for( 'admin_delete_single_type_duplicate', entity_id=row['entity'].id, type_id=type_.id) remove_links.append( f'<a href="{url}">{uc_first(_("remove"))}</a>' f'{type_.name}') table.rows.append([ link(row['entity']), row['entity'].class_.name, link(g.types[row['type'].id]), '<br><br><br><br><br>'.join(remove_links)]) return render_template( 'admin/check_link_duplicates.html', table=table, title=_('admin'), crumbs=[ [_('admin'), f"{url_for('admin_index')}#tab-data"], _('check link duplicates')])
def involvement_insert(origin_id: int) -> Union[str, Response]: origin = Entity.get_by_id(origin_id) form = build_form('involvement', origin=origin) form.activity.choices = [('P11', g.properties['P11'].name_inverse)] if origin.class_.name in ['acquisition', 'activity', 'production']: form.activity.choices.append(('P14', g.properties['P14'].name_inverse)) if origin.class_.name == 'acquisition': form.activity.choices.append( ('P22', g.properties['P22'].name_inverse)) form.activity.choices.append( ('P23', g.properties['P23'].name_inverse)) if form.validate_on_submit(): Transaction.begin() try: if origin.class_.view == 'event': for actor in Entity.get_by_ids( ast.literal_eval(form.actor.data)): link_ = Link.get_by_id( origin.link(form.activity.data, actor, form.description.data)[0]) link_.set_dates(process_form_dates(form)) link_.type = get_link_type(form) link_.update() else: for event in Entity.get_by_ids( ast.literal_eval(form.event.data)): link_ = Link.get_by_id( event.link(form.activity.data, origin, form.description.data)[0]) link_.set_dates(process_form_dates(form)) link_.type = get_link_type(form) link_.update() Transaction.commit() except Exception as e: # pragma: no cover Transaction.rollback() logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') if hasattr(form, 'continue_') and form.continue_.data == 'yes': return redirect(url_for('involvement_insert', origin_id=origin_id)) return redirect( f"{url_for('view', id_=origin.id)}" f"#tab-{'actor' if origin.class_.view == 'event' else 'event'}") return render_template('display_form.html', form=form, crumbs=[[ _(origin.class_.view), url_for('index', view=origin.class_.view) ], origin, _('involvement')])
def get_links(entity: Entity) -> Dict[str, Any]: links: Dict[str, Any] = defaultdict(list) for link in Link.get_links(entity.id): key = f"""{link.property.i18n['en'].replace(' ', '_')} _{link.range.class_.name}""" links[key].append(link.range.name) for link in Link.get_links(entity.id, inverse=True): key = f"""{link.property.i18n['en'].replace(' ', '_')} _{link.range.class_.name}""" if link.property.i18n_inverse['en']: key = link.property.i18n_inverse['en'].replace(' ', '_') key += '_' + link.domain.class_.name links[key].append(link.domain.name) links.pop('has_type_type', None) return links
def add_tabs_for_type(entity: Type) -> dict[str, Tab]: tabs = { 'subs': Tab('subs', entity=entity), 'entities': Tab('entities', entity=entity)} for sub_id in entity.subs: sub = g.types[sub_id] tabs['subs'].table.rows.append([ link(sub), sub.count, sub.description]) if entity.category == 'value': tabs['entities'].table.header = \ [_('name'), _('value'), _('class'), _('info')] for item in entity.get_linked_entities( ['P2', 'P89'], inverse=True, types=True): if item.class_.name in ['location', 'reference_system']: continue # pragma: no cover if item.class_.name == 'object_location': item = item.get_linked_entity_safe('P53', inverse=True) data = [link(item)] if entity.category == 'value': data.append(format_number(item.types[entity])) data.append(item.class_.label) data.append(item.description) tabs['entities'].table.rows.append(data) if not tabs['entities'].table.rows: # If no entities available get links with this type_id tabs['entities'].table.header = [_('domain'), _('range')] for row in Link.get_links_by_type(entity): tabs['entities'].table.rows.append([ link(Entity.get_by_id(row['domain_id'])), link(Entity.get_by_id(row['range_id']))]) return tabs
def get_special_node(id_: int, data: list[int]) -> list[int]: for link_ in Link.get_links_by_type(g.types[id_]): data.append(link_['domain_id']) data.append(link_['range_id']) for sub_id in g.types[id_].subs: GetTypeEntitiesAll.get_special_node(sub_id, data) return data
def build_move_form(node: Node) -> FlaskForm: class Form(FlaskForm): # type: ignore is_node_form = HiddenField() checkbox_values = HiddenField() selection = SelectMultipleField( '', [InputRequired()], coerce=int, option_widget=widgets.CheckboxInput(), widget=widgets.ListWidget(prefix_label=False)) save = SubmitField(uc_first(_('move entities'))) root = g.nodes[node.root[-1]] setattr(Form, str(root.id), TreeField(str(root.id))) form = Form(obj=node) choices = [] if root.class_.name == 'administrative_unit': for entity in node.get_linked_entities('P89', True): place = entity.get_linked_entity('P53', True) if place: choices.append((entity.id, place.name)) elif root.name in app.config['PROPERTY_TYPES']: for row in Link.get_entities_by_node(node): domain = Entity.get_by_id(row['domain_id']) range_ = Entity.get_by_id(row['range_id']) choices.append((row['id'], domain.name + ' - ' + range_.name)) else: for entity in node.get_linked_entities('P2', True): choices.append((entity.id, entity.name)) form.selection.choices = choices return form
def link(self, code: str, range_: Union[Entity, List[Entity]], description: Optional[str] = None, inverse: bool = False, type_id: Optional[int] = None) -> List[int]: return Link.insert(self, code, range_, description, inverse, type_id)
def get_geom(entity: Entity) -> Union[list[dict[str, Any]], list[Any]]: if entity.class_.view == 'place' or entity.class_.name == 'artifact': return Gis.get_by_id( Link.get_linked_entity_safe(entity.id, 'P53').id) if entity.class_.name == 'object_location': return Gis.get_by_id(entity.id) return []
def test_member(self) -> None: with app.app_context(): with app.test_request_context(): app.preprocess_request() # type: ignore actor = Entity.insert('person', 'Ripley') group = Entity.insert('group', 'Space Marines') # Add membership rv = self.app.get(url_for('member_insert', origin_id=group.id)) assert b'Actor function' in rv.data rv = self.app.post(url_for('member_insert', origin_id=actor.id, code='membership'), data={'group': str([group.id])}, follow_redirects=True) assert b'Space Marines' in rv.data rv = self.app.post(url_for('member_insert', origin_id=actor.id, code='membership'), data={ 'group': str([group.id]), 'continue_': 'yes' }, follow_redirects=True) assert b'Space Marines' in rv.data rv = self.app.post(url_for('member_insert', origin_id=group.id, code='membership'), data={'group': str([group.id])}) assert b"link to itself" in rv.data rv = self.app.post(url_for('member_insert', origin_id=actor.id), data={'actor': str([actor.id])}, follow_redirects=True) assert b"link to itself" in rv.data # Add member to group data = {'actor': str([actor.id])} rv = self.app.post(url_for('member_insert', origin_id=group.id), data=data, follow_redirects=True) assert b'Ripley' in rv.data data['continue_'] = 'yes' rv = self.app.post(url_for('member_insert', origin_id=group.id), data=data, follow_redirects=True) assert b'Ripley' in rv.data # Update with app.test_request_context(): app.preprocess_request() # type: ignore link_id = Link.get_links(group.id, 'P107')[0].id rv = self.app.get( url_for('member_update', id_=link_id, origin_id=group.id)) assert b'Ripley' in rv.data rv = self.app.post( url_for('member_update', id_=link_id, origin_id=group.id), data={'description': 'We are here to help you.'}, follow_redirects=True) assert b'here to help' in rv.data
def get_linked_entity(self, code: str, inverse: bool = False, types: bool = False) -> Optional[Entity]: return Link.get_linked_entity(self.id, code, inverse=inverse, types=types)
def get_linked_entities(self, code: Union[str, list[str]], inverse: bool = False, types: bool = False) -> list[Entity]: return Link.get_linked_entities(self.id, code, inverse=inverse, types=types)
def get_entities_linked_to_special_type_recursive( id_: int, data: list[int]) -> list[int]: for link_ in Link.get_links_by_type(g.types[id_]): data.append(link_['domain_id']) data.append(link_['range_id']) for sub_id in g.types[id_].subs: get_entities_linked_to_special_type_recursive(sub_id, data) return data
def get_csv_geom_entry(entity: Entity) -> dict[str, None]: geom = {'type': None, 'coordinates': None} if entity.class_.view == 'place' or entity.class_.name == 'artifact': geom = get_csv_geometry( Link.get_linked_entity_safe(entity.id, 'P53')) elif entity.class_.name == 'object_location': geom = get_csv_geometry(entity) return geom
def admin_check_links() -> str: return render_template( 'admin/check_links.html', table=Table(['domain', 'property', 'range'], rows=[[x['domain'], x['property'], x['range']] for x in Link.get_invalid_cidoc_links()]), title=_('admin'), crumbs=[[_('admin'), f"{url_for('admin_index')}#tab-data"], _('check links')])
def get_geom(entity: Entity) -> Union[List[Dict[str, Any]], List[Any]]: if entity.class_.view == 'place' or entity.class_.name in [ 'find', 'artifact' ]: return Gis.get_by_id(Link.get_linked_entity(entity.id, 'P53').id) if entity.class_.name == 'object_location': print("here") return Gis.get_by_id(entity.id) return []
def get_geom_entry(entity: Entity) -> Dict[str, None]: geom = {'type': None, 'coordinates': None} if entity.class_.view == 'place' \ or entity.class_.name in ['find', 'artifact']: geom = ApiExportCSV.get_geometry( Link.get_linked_entity(entity.id, 'P53')) elif entity.class_.name == 'object_location': geom = ApiExportCSV.get_geometry(entity) return geom
def link_string(self, code: str, range_: str, description: Optional[str] = None, inverse: bool = False) -> List[int]: # range_ = string value from a form, can be empty, an int or an int list presentation # e.g. '', '1', '[]', '[1, 2]' ids = ast.literal_eval(range_) ids = [int(id_) for id_ in ids] if isinstance(ids, list) else [int(ids)] return Link.insert(self, code, Entity.get_by_ids(ids), description, inverse)
def involvement_update(id_: int, origin_id: int) -> Union[str, Response]: link_ = Link.get_by_id(id_) form = build_form('involvement', link_) form.activity.choices = [('P11', g.properties['P11'].name)] event = Entity.get_by_id(link_.domain.id) actor = Entity.get_by_id(link_.range.id) origin = event if origin_id == event.id else actor if event.class_.name in ['acquisition', 'activity']: form.activity.choices.append(('P14', g.properties['P14'].name)) if event.class_.name == 'acquisition': form.activity.choices.append(('P22', g.properties['P22'].name)) form.activity.choices.append(('P23', g.properties['P23'].name)) if form.validate_on_submit(): Transaction.begin() try: link_.delete() link_ = Link.get_by_id( event.link(form.activity.data, actor, form.description.data)[0]) link_.set_dates(form) link_.type = get_link_type(form) link_.update() Transaction.commit() except Exception as e: # pragma: no cover Transaction.rollback() logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') tab = 'actor' if origin.class_.view == 'event' else 'event' return redirect(f"{url_for('entity_view', id_=origin.id)}#tab-{tab}") form.save.label.text = _('save') form.activity.data = link_.property.code form.description.data = link_.description return render_template('display_form.html', origin=origin, form=form, crumbs=[[ _(origin.class_.view), url_for('index', view=origin.class_.view) ], origin, event if origin_id != event.id else actor, _('edit')])
def link_update(id_: int, origin_id: int) -> Union[str, Response]: link_ = Link.get_by_id(id_) domain = Entity.get_by_id(link_.domain.id) range_ = Entity.get_by_id(link_.range.id) origin = Entity.get_by_id(origin_id) if 'reference' in [domain.class_.view, range_.class_.view]: return reference_link_update(link_, origin) if 'event' in [domain.class_.view, range_.class_.view]: return involvement_update(link_, origin) if domain.class_.view == 'actor' and range_.class_.view == 'actor': return relation_update(link_, domain, range_, origin) abort(403) # pragma: no cover
def test_links(self) -> None: from openatlas.database.entity import Entity from openatlas.database.link import Link with app.app_context(): # type: ignore with app.test_request_context(): app.preprocess_request() # type: ignore id_ = Entity.insert({ 'name': 'Invalid linked entity', 'system_class': 'artifact', 'code': 'E13', 'description': '' }) Link.insert({ 'property_code': 'P86', 'domain_id': id_, 'range_id': id_, 'description': '', 'type_id': None }) rv = self.app.get(url_for('admin_check_links')) assert b'Invalid linked entity' in rv.data
def get_links(entity: Entity) -> Optional[List[Dict[str, str]]]: links = [] for link in Link.get_links(entity.id): links.append({ 'label': link.range.name, 'relationTo': url_for('entity', id_=link.range.id, _external=True), 'relationType': 'crm:' + link.property.code + '_' + link.property.i18n['en'].replace(' ', '_'), 'relationSystemClass': link.range.class_.name, 'type': link.type.name if link.type else None, 'when': { 'timespans': [GeoJsonEntity.get_time(link)] } }) for link in Link.get_links(entity.id, inverse=True): link_property = link.property.i18n_inverse[ 'en'] if link.property.i18n_inverse[ 'en'] else link.property.i18n['en'] links.append({ 'label': link.domain.name, 'relationTo': url_for('entity', id_=link.domain.id, _external=True), 'relationType': 'crm:' + link.property.code + 'i_' + link_property.replace(' ', '_'), 'relationSystemClass': link.domain.class_.name, 'type': link.type.name if link.type else None, 'when': { 'timespans': [GeoJsonEntity.get_time(link)] } }) return links if links else None