def get_all_nodes():
        """Get and return all type and place nodes"""
        sql = """
            SELECT
                e.id,
                e.name,
                e.class_code,
                e.description,
                e.system_type,
                e.created,
                e.modified,
                es.id AS super_id,
                COUNT(e2.id) AS count,
                COUNT(lp.id) AS count_property
            FROM model.entity e                

            -- get super
            LEFT JOIN model.link l ON e.id = l.domain_id AND l.property_code = %(property_code)s
            LEFT JOIN model.entity es ON l.range_id = es.id

            -- get count
            LEFT JOIN model.link l2 ON e.id = l2.range_id
            LEFT JOIN model.entity e2 ON l2.domain_id = e2.id AND
                (l2.property_code = 'P2' OR
                    (l2.property_code = 'P89' AND e2.system_type = 'place location'))
            LEFT JOIN model.link_property lp ON e.id = lp.range_id AND lp.property_code = 'P2'
            
            WHERE e.class_code = %(class_code)s
                AND (e.system_type IS NULL OR e.system_type != 'place location')
            GROUP BY e.id, es.id                        
            ORDER BY e.name;"""
        g.cursor.execute(sql, {'class_code': 'E55', 'property_code': 'P127'})
        debug_model['div sql'] += 1
        types = g.cursor.fetchall()
        g.cursor.execute(sql, {'class_code': 'E53', 'property_code': 'P89'})
        debug_model['div sql'] += 1
        places = g.cursor.fetchall()
        nodes = OrderedDict()
        for row in types + places:
            node = Entity(row)
            nodes[node.id] = node
            node.count = row.count + row.count_property
            node.count_subs = 0
            node.subs = []
            node.root = [row.super_id] if row.super_id else []
        NodeMapper.populate_subs(nodes)
        return nodes
Example #2
0
def file_add(id_: int, view: str) -> Union[str, Response]:
    entity = Entity.get_by_id(id_)
    if request.method == 'POST':
        if request.form['checkbox_values']:
            entity.link_string('P67', request.form['checkbox_values'])
        return redirect(f"{url_for('entity_view', id_=entity.id)}#tab-{view}")
    return render_template(
        'form.html',
        form=build_table_form(view, entity.get_linked_entities('P67')),
        title=entity.name,
        crumbs=[
            [_(entity.class_.view), url_for('index', view=entity.class_.view)],
            entity,
            f"{_('link')} {_(view)}"])
Example #3
0
 def insert_system(form: FlaskForm) -> Entity:
     entity = Entity.insert('reference_system', form.name.data,
                            form.description.data)
     Db.insert_system({
         'entity_id':
         entity.id,
         'name':
         entity.name,
         'website_url':
         form.website_url.data if form.website_url.data else None,
         'resolver_url':
         form.resolver_url.data if form.resolver_url.data else None
     })
     return ReferenceSystem.get_all()[entity.id]
Example #4
0
 def insert_system(data: dict[str, str]) -> Entity:
     entity = Entity.insert('reference_system', data['name'],
                            data['description'])
     Db.insert_system({
         'entity_id':
         entity.id,
         'name':
         entity.name,
         'website_url':
         data['website_url'] if data['website_url'] else None,
         'resolver_url':
         data['resolver_url'] if data['resolver_url'] else None
     })
     return ReferenceSystem.get_all()[entity.id]
Example #5
0
def display_file_api(filename: str) -> Any:  # pragma: no cover
    parser = image_parser.parse_args()
    from pathlib import Path as Pathlib_path
    entity = Entity.get_by_id(int(Pathlib_path(filename).stem), nodes=True)
    license_ = None
    for node in entity.nodes:
        if node.root and node.root[-1] == Node.get_hierarchy('License').id:
            license_ = node.name
    if not license_:
        raise AccessDeniedError
    if parser['download']:
        return send_file(str(app.config['UPLOAD_DIR']) + '/' + filename,
                         as_attachment=True)
    return send_from_directory(app.config['UPLOAD_DIR'], filename)
Example #6
0
def translation_update(id_: int) -> Union[str, Response]:
    translation = Entity.get_by_id(id_, nodes=True)
    form = build_form('source_translation', translation)
    if form.validate_on_submit():
        save(form, translation)
        flash(_('info update'), 'info')
        return redirect(url_for('entity_view', id_=translation.id))
    return render_template(
        'display_form.html',
        form=form,
        title=translation.name,
        crumbs=[[_('source'), url_for('index', view='source')],
                translation.get_linked_entity('P73', True), translation,
                _('edit')])
Example #7
0
 def get_by_class_code_api(code: Union[str, List[str]],
                           parser: Dict[str, Any]) -> List[Entity]:
     parameters = {
         'codes': tuple(code if isinstance(code, list) else [code])
     }
     sql = Db.build_sql(nodes=True) + """
         WHERE class_code IN %(codes)s {clause} 
         GROUP BY e.id
         ORDER BY {order} {sort};""".format(
         clause=Filter.get_filter(parameters=parameters, parser=parser),
         order=', '.join(parser['column']),
         sort=parser['sort'])
     g.cursor.execute(sql, parameters)
     return [Entity(row) for row in g.cursor.fetchall()]
Example #8
0
def translation_insert(source_id: int) -> Union[str, Response]:
    source = Entity.get_by_id(source_id)
    form = build_form('source_translation')
    if form.validate_on_submit():
        translation = save(form, source=source)
        flash(_('entity created'), 'info')
        if hasattr(form, 'continue_') and form.continue_.data == 'yes':
            return redirect(url_for('translation_insert', source_id=source.id))
        return redirect(url_for('entity_view', id_=translation.id))
    return render_template(
        'display_form.html',
        form=form,
        crumbs=[[_('source'), url_for('index', view='source')], source,
                f"+ {uc_first(_('text'))}"])
Example #9
0
def note_insert(entity_id: int) -> Union[str, Response]:
    entity = Entity.get_by_id(entity_id)
    form = build_form('note')
    if form.validate_on_submit():
        User.insert_note(entity_id, form.description.data, form.public.data)
        flash(_('note added'), 'info')
        return redirect(f"{url_for('view', id_=entity.id)}#tab-note")
    return render_template('display_form.html',
                           form=form,
                           entity=entity,
                           crumbs=[[
                               _(entity.class_.view),
                               url_for('index', view=entity.class_.view)
                           ], entity, f"+ {uc_first(_('note'))}"])
Example #10
0
 def get_by_system_class(classes: str, parser: Dict[str,
                                                    Any]) -> List[Entity]:
     parameters = {
         'class': tuple(classes if isinstance(classes, list) else [classes])
     }
     sql = Db.build_sql(nodes=True, aliases=True) + """
         WHERE e.system_class
         IN %(class)s {clause}
         GROUP BY e.id ORDER BY {order} {sort};""".format(
         clause=Filter.get_filter(parameters=parameters, parser=parser),
         order=', '.join(parser['column']),
         sort=parser['sort'])
     g.cursor.execute(sql, parameters)
     return [Entity(row) for row in g.cursor.fetchall()]
Example #11
0
def member_insert(origin_id: int,
                  code: str = 'member') -> Union[str, Response]:
    origin = Entity.get_by_id(origin_id)
    form = build_form('actor_function', code=code)
    form.member_origin_id.data = origin.id
    if form.validate_on_submit():
        Transaction.begin()
        try:
            member_field = getattr(form, 'actor') \
                if code == 'member' else getattr(form, 'group')
            for actor in Entity.get_by_ids(ast.literal_eval(
                    member_field.data)):
                if code == 'membership':
                    link_ = Link.get_by_id(
                        actor.link('P107', origin, form.description.data)[0])
                else:
                    link_ = Link.get_by_id(
                        origin.link('P107', actor, form.description.data)[0])
                link_.set_dates(process_form_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('member_insert', origin_id=origin_id, code=code))
        return redirect(f"{url_for('view', id_=origin.id)}"
                        f"#tab-member{'' if code == 'member' else '-of'}")
    return render_template(
        'display_form.html',
        form=form,
        crumbs=[[_('actor'), url_for('index', view='actor')], origin,
                _('member')])
Example #12
0
def admin_check_link_duplicates(
        delete: Optional[str] = None) -> Union[str, Response]:
    if delete:
        delete_count = str(Link.delete_link_duplicates())
        logger.log('info', 'admin', 'Deleted duplicate links: ' + delete_count)
        flash(_('deleted links') + ': ' + delete_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.nodes[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']
        ])
    duplicates = False
    if table.rows:
        duplicates = True
    else:  # If no exact duplicates where found check if single types are used multiple times
        table = Table(
            ['entity', 'class', 'base type', 'incorrect multiple types'],
            rows=Link.check_single_type_duplicates())
    return render_template(
        'admin/check_link_duplicates.html',
        table=table,
        duplicates=duplicates,
        title=_('admin'),
        crumbs=[[_('admin'), url_for('admin_index') + '#tab-data'],
                _('check link duplicates')])
Example #13
0
def relation_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
    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(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(
            url_for('entity_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')])
Example #14
0
 def test_duplicates(self) -> None:
     with app.app_context():
         with app.test_request_context():
             app.preprocess_request()  # type: ignore
             event = Entity.insert('acquisition', 'Event Horizon')
             source = Entity.insert('source', 'Tha source')
             source.link('P67', event)
             source.link('P67', event)
             source_type = Type.get_hierarchy('Source')
             source.link('P2', g.types[source_type.subs[0]])
             source.link('P2', g.types[source_type.subs[1]])
         rv = self.app.get(url_for('admin_check_link_duplicates'))
         assert b'Event Horizon' in rv.data
         rv = self.app.get(
             url_for('admin_check_link_duplicates', delete='delete'),
             follow_redirects=True)
         assert b'Remove' in rv.data
         rv = self.app.get(
             url_for(
                 'admin_delete_single_type_duplicate',
                 entity_id=source.id,
                 type_id=source_type.subs[0]),
             follow_redirects=True)
         assert b'Congratulations, everything looks fine!' in rv.data
    def get_by_object(object_: Entity) -> Dict[int, Overlay]:
        ids = [object_.id]

        # Get overlays of parents
        if object_.system_type == 'find':
            stratigraphic_unit = object_.get_linked_entity_safe('P46', True)
            ids.append(stratigraphic_unit.id)
            feature = stratigraphic_unit.get_linked_entity_safe('P46', True)
            ids.append(feature.id)
            ids.append(feature.get_linked_entity_safe('P46', True).id)
        elif object_.system_type == 'stratigraphic unit':
            feature = object_.get_linked_entity_safe('P46', True)
            ids.append(feature.id)
            ids.append(feature.get_linked_entity_safe('P46', True).id)
        elif object_.system_type == 'feature':
            ids.append(object_.get_linked_entity_safe('P46', True).id)

        sql = """
            SELECT o.id, o.place_id, o.image_id, o.bounding_box, i.name
            FROM web.map_overlay o
            JOIN model.entity i ON o.image_id = i.id
            WHERE o.place_id IN %(place_ids)s;"""
        g.execute(sql, {'place_ids': tuple(ids)})
        return {row.image_id: Overlay(row) for row in g.cursor.fetchall()}
Example #16
0
def delete_entity(id_: int) -> Optional[str]:
    url = None
    entity = Entity.get_by_id(id_)
    if not is_authorized(entity.class_.write_access):
        abort(403)  # pragma: no cover
    if isinstance(entity, ReferenceSystem):
        if entity.system:
            abort(403)
        if entity.forms:
            flash(_('Deletion not possible if forms are attached'), 'error')
            return url_for('entity_view', id_=id_)
    if entity.class_.view in ['artifact', 'place']:
        if entity.get_linked_entities('P46'):
            flash(_('Deletion not possible if subunits exists'), 'error')
            return url_for('entity_view', id_=id_)
        parent = None if entity.class_.name == 'place' else entity.get_linked_entity(
            'P46', True)
        entity.delete()
        logger.log_user(id_, 'delete')
        flash(_('entity deleted'), 'info')
        if parent:
            tab = '#tab-' + entity.class_.name.replace('_', '-')
            url = url_for('entity_view', id_=parent.id) + tab
    else:
        Entity.delete_(id_)
        logger.log_user(id_, 'delete')
        flash(_('entity deleted'), 'info')
        if entity.class_.name == 'file':
            try:
                path = get_file_path(id_)
                if path:  # Only delete file on disk if it exists to prevent a missing file error
                    path.unlink()
            except Exception as e:  # pragma: no cover
                logger.log('error', 'file', 'file deletion failed', e)
                flash(_('error file delete'), 'error')
    return url
Example #17
0
def populate_reference_systems(form: FlaskForm, item: Entity) -> None:
    system_links = {
        # Can't use isinstance for class_ check here
        link_.domain.id: link_
        for link_ in item.get_links('P67', True)
        if link_.domain.class_.name == 'reference_system'
    }
    for field in form:
        if field.id.startswith('reference_system_id_'):
            system_id = int(field.id.replace('reference_system_id_', ''))
            if system_id in system_links:
                field.data = system_links[system_id].description
                precision_field = getattr(
                    form, f'reference_system_precision_{system_id}')
                precision_field.data = str(system_links[system_id].type.id)
Example #18
0
def add_tabs_for_file(entity: Entity) -> dict[str, Tab]:
    tabs = {}
    for name in [
            'source', 'event', 'actor', 'place', 'feature',
            'stratigraphic_unit', 'artifact', 'human_remains', 'reference',
            'type'
    ]:
        tabs[name] = Tab(name, entity=entity)
    entity.image_id = entity.id if get_file_path(entity.id) else None
    for link_ in entity.get_links('P67'):
        range_ = link_.range
        data = get_base_table_data(range_)
        data.append(remove_link(range_.name, link_, entity,
                                range_.class_.name))
        tabs[range_.class_.view].table.rows.append(data)
    for link_ in entity.get_links('P67', True):
        data = get_base_table_data(link_.domain)
        data.append(link_.description)
        data.append(
            edit_link(url_for('link_update', id_=link_.id,
                              origin_id=entity.id)))
        data.append(remove_link(link_.domain.name, link_, entity, 'reference'))
        tabs['reference'].table.rows.append(data)
    return tabs
Example #19
0
def overlay_update(id_: int) -> Union[str, Response]:
    overlay = Overlay.get_by_id(id_)
    form = OverlayForm()
    if form.validate_on_submit():
        Overlay.update(form=form,
                       image_id=overlay.image_id,
                       place_id=overlay.place_id)
        flash(_('info update'), 'info')
        return redirect(
            url_for('entity_view', id_=overlay.place_id) + '#tab-file')
    bounding = ast.literal_eval(overlay.bounding_box)
    form.top_left_easting.data = bounding[0][1]
    form.top_left_northing.data = bounding[0][0]
    form.bottom_right_easting.data = bounding[1][1]
    form.bottom_right_northing.data = bounding[1][0]
    entity = Entity.get_by_id(overlay.place_id)
    return render_template(
        'overlay/update.html',
        form=form,
        overlay=overlay,
        entity=entity,
        crumbs=[[_('place'), url_for('index', view='place')], entity,
                Entity.get_by_id(overlay.image_id),
                _('update overlay')])
Example #20
0
 def get_invalid_cidoc_links() -> List[Dict[str, str]]:
     from openatlas.models.entity import Entity
     from openatlas.util.util import link
     invalid_linking = []
     for row in Db.get_cidoc_links():
         property_ = g.properties[row['property_code']]
         domain_is_valid = property_.find_object(
             'domain_class_code',
             row['domain_code'])
         range_is_valid = property_.find_object(
             'range_class_code',
             row['range_code'])
         if not domain_is_valid or not range_is_valid:
             invalid_linking.append(row)
     invalid_links = []
     for item in invalid_linking:
         for row in Db.get_invalid_links(item):
             domain = Entity.get_by_id(row['domain_id'])
             range_ = Entity.get_by_id(row['range_id'])
             invalid_links.append({
                 'domain': f"{link(domain)} ({domain.cidoc_class.code})",
                 'property': link(g.properties[row['property_code']]),
                 'range': f"{link(range_)} ({range_.cidoc_class.code})"})
     return invalid_links
Example #21
0
 def update_links(form: FlaskForm, entity: Entity) -> None:
     for field in form:
         if field.id.startswith('reference_system_id_'):  # Recreate link
             system = Entity.get_by_id(
                 int(field.id.replace('reference_system_id_', '')))
             precision_field = getattr(
                 form,
                 field.id.replace('id_', 'precision_'))
             Db.remove_link(system.id, entity.id)
             if field.data:
                 system.link(
                     'P67',
                     entity,
                     field.data,
                     type_id=precision_field.data)
Example #22
0
def get_place_info_for_update(entity: Entity) -> dict[str, Any]:
    if entity.class_.view not in ['artifact', 'place']:
        return {
            'structure': None,
            'gis_data': None,
            'overlays': None,
            'location': None
        }
    structure = get_structure(entity)
    return {
        'structure': structure,
        'gis_data': Gis.get_all([entity], structure),
        'overlays': Overlay.get_by_object(entity),
        'location': entity.get_linked_entity_safe('P53', types=True)
    }
Example #23
0
 def get(filename: str) -> Response:  # pragma: no cover
     entity = Entity.get_by_id(int(Pathlib_path(filename).stem), types=True)
     license_ = get_license(entity)
     if not license_:
         raise AccessDeniedError
     parser = image.parse_args()
     if parser['download']:
         return send_file(
             f"{app.config['UPLOAD_DIR']}/{filename}",
             as_attachment=True)
     if parser['image_size'] and check_processed_image(filename):
         size = app.config['IMAGE_SIZE'][parser['image_size']]
         return send_from_directory(
             f"{app.config['RESIZED_IMAGES']}/{size}",
             filename)
     return send_from_directory(app.config['UPLOAD_DIR'], filename)
Example #24
0
def entity_add_reference(id_: int) -> Union[str, Response]:
    entity = Entity.get_by_id(id_)
    form = AddReferenceForm()
    if form.validate_on_submit():
        entity.link_string('P67',
                           form.reference.data,
                           description=form.page.data,
                           inverse=True)
        return redirect(f"{url_for('entity_view', id_=id_)}#tab-reference")
    form.page.label.text = uc_first(_('page / link text'))
    return render_template('display_form.html',
                           entity=entity,
                           form=form,
                           crumbs=[[
                               _(entity.class_.view),
                               url_for('index', view=entity.class_.view)
                           ], entity, f"{_('link')} {_('reference')}"])
Example #25
0
def entity_add_source(id_: int) -> Union[str, Response]:
    entity = Entity.get_by_id(id_)
    if request.method == 'POST':
        if request.form['checkbox_values']:
            entity.link_string('P67',
                               request.form['checkbox_values'],
                               inverse=True)
        return redirect(f"{url_for('entity_view', id_=id_)}#tab-source")
    form = build_table_form('source',
                            entity.get_linked_entities('P67', inverse=True))
    return render_template('form.html',
                           form=form,
                           title=entity.name,
                           crumbs=[[
                               _(entity.class_.view),
                               url_for('index', view=entity.class_.view)
                           ], entity, f"{_('link')} {_('source')}"])
Example #26
0
 def get_subunits(id_: int) -> List[Dict[str, Any]]:
     # Get first level of subunits
     try:
         entity = Entity.get_by_id(id_, nodes=True, aliases=True)
     except Exception:  # pragma: no cover
         raise EntityDoesNotExistError
     structure = get_structure(entity)
     data = []
     if not structure or not structure['subunits']:
         raise InvalidSubunitError  # pragma: no cover
     for n in structure['subunits']:
         data.append({
             'id': n.id,
             'label': n.name,
             'url': url_for('entity', id_=n.id, _external=True)
         })
     return data
Example #27
0
def save(form: FlaskForm,
         node: Optional[Node] = None,
         param: Optional[str] = None) -> Optional[Node]:
    Transaction.begin()
    try:
        if node:
            Node.update_hierarchy(node, form)
        else:
            node = Entity.insert('type', sanitize(form.name.data))
            Node.insert_hierarchy(node, form, value_type=(param == 'value'))
        node.update(form)
        Transaction.commit()
    except Exception as e:  # pragma: no cover
        Transaction.rollback()
        logger.log('error', 'database', 'transaction failed', e)
        flash(_('error transaction'), 'error')
        abort(418)
    return node
Example #28
0
 def get(filename: str) -> Response:
     from pathlib import Path as Pathlib_path
     entity = Entity.get_by_id(int(Pathlib_path(filename).stem), nodes=True)
     license_ = None
     for node in entity.nodes:
         if node.root and node.root[-1] == Node.get_hierarchy('License').id:
             license_ = node.name
     if not license_:
         raise AccessDeniedError
     parser = image.parse_args()
     if parser['download']:
         return send_file(f"{app.config['UPLOAD_DIR']}/{filename}",
                          as_attachment=True)
     if parser['image_size']:
         size = app.config['IMAGE_SIZE'][parser['image_size']]
         return send_from_directory(
             f"{app.config['RESIZED_IMAGES']}/{size}", filename)
     return send_from_directory(app.config['UPLOAD_DIR'], filename)
Example #29
0
 def get_untyped(hierarchy_id: int) -> list[Entity]:
     hierarchy = g.types[hierarchy_id]
     classes = hierarchy.classes
     if hierarchy.name in ('Administrative unit', 'Historical place'):
         classes = 'object_location'  # pragma: no cover
     untyped = []
     for entity in Entity.get_by_class(classes, types=True):
         linked = False
         for type_ in entity.types:
             if type_.root[0] == hierarchy_id:
                 linked = True
                 break
         if not linked:
             if classes == 'object_location':  # pragma: no cover
                 if entity.get_linked_entity('P53', True):
                     untyped.append(entity)
             else:
                 untyped.append(entity)
     return untyped
Example #30
0
def admin_check_similar() -> str:
    form = SimilarForm()
    form.classes.choices = [(x.name, x.label) for name, x in g.classes.items()
                            if x.label]
    table = None
    if form.validate_on_submit():
        table = Table(['name', _('count')])
        for sample in Entity.get_similar_named(form).values():
            html = link(sample['entity'])
            for entity in sample['entities']:  # Table linebreaks workaround
                html += f'<br><br><br><br><br>{link(entity)}'
            table.rows.append([html, len(sample['entities']) + 1])
    return render_template(
        'admin/check_similar.html',
        table=table,
        form=form,
        title=_('admin'),
        crumbs=[[_('admin'), f"{url_for('admin_index')}#tab-data"],
                _('check similar names')])
Example #31
0
def search(data: dict[str, Any]) -> list[Entity]:
    if not data['term']:
        return []
    if 'person' in data['classes'] \
            or 'place' in data['classes'] \
            or 'group' in data['classes']:
        data['classes'].append('appellation')
    entities = []
    for row in Db.search(data['term'], data['classes'], data['desc'],
                         data['own'], current_user.id):
        if row['openatlas_class_name'] == 'appellation':
            entity = Link.get_linked_entity_safe(row['id'], 'P1', True)
            if entity.class_.name not in data['classes']:
                continue
        else:
            entity = Entity(row)
        if entity and check_dates(entity, data):
            entities.append(entity)
    return list({d.id: d for d in entities}.values())  # Remove duplicates