Example #1
0
 def __call__(self, field: TableField, **kwargs: Any) -> TableSelect:
     selection = ''
     if field.id in ('cidoc_domain', 'cidoc_property', 'cidoc_range'):
         table = Table(['code', 'name'],
                       order=[[0, 'desc']],
                       defs=[{
                           'orderDataType': 'cidoc-model',
                           'targets': [0]
                       }, {
                           'sType': 'numeric',
                           'targets': [0]
                       }])
         for id_, entity in (g.properties if field.id == 'cidoc_property'
                             else g.cidoc_classes).items():
             table.rows.append([
                 f"""
                 <a href="#" onclick="selectFromTable(
                     this,
                     '{field.id}',
                     '{id_}',
                     '{entity.code} {entity.name}');">{entity.code}</a>""",
                 entity.name
             ])
     else:
         aliases = current_user.settings['table_show_aliases']
         if 'place' in field.id \
                 or field.id in ['begins_in', 'ends_in', 'residence']:
             class_ = 'place'
             entities = Entity.get_by_class('place',
                                            types=True,
                                            aliases=aliases)
         elif field.id.startswith('event_'):
             class_ = 'event'
             entities = Entity.get_by_view('event',
                                           types=True,
                                           aliases=aliases)
         else:
             class_ = field.id
             entities = Entity.get_by_view(class_,
                                           types=True,
                                           aliases=aliases)
         table = Table(g.table_headers[class_])
         for entity in entities:
             if field.data and entity.id == int(field.data):
                 selection = entity.name
             data = get_base_table_data(entity, show_links=False)
             data[0] = self.format_name_and_aliases(entity, field.id)
             table.rows.append(data)
     return super().__call__(field, **kwargs) + render_template(
         'forms/table_select.html',
         field=field,
         table=table.display(field.id),
         selection=selection)
Example #2
0
def build_table_form(class_: str, linked_entities: List[Entity]) -> str:
    """ Returns a form with a list of entities with checkboxes."""
    if class_ == 'file':
        entities = Entity.get_by_class('file', nodes=True)
    elif class_ == 'place':
        entities = Entity.get_by_class('place', nodes=True, aliases=True)
    else:
        entities = Entity.get_by_view(class_)

    linked_ids = [entity.id for entity in linked_entities]
    table = Table([''] + g.table_headers[class_], order=[[1, 'asc']])
    file_stats = get_file_stats() if class_ == 'file' else None
    for entity in entities:
        if entity.id in linked_ids:
            continue  # Don't show already linked entries
        input_ = '<input id="selection-{id}" name="values" type="checkbox" value="{id}">'.format(
            id=entity.id)
        table.rows.append([input_] + get_base_table_data(entity, file_stats))
    if not table.rows:
        return uc_first(_('no entries'))
    return """
        <form class="table" id="checkbox-form" method="post">
            <input id="csrf_token" name="csrf_token" type="hidden" value="{token}">
            <input id="checkbox_values" name="checkbox_values" type="hidden">
            {table}
            <input id="save" class="{class_}" name="save" type="submit" value="{link}">
        </form>""".format(link=uc_first(_('link')),
                          token=generate_csrf(),
                          class_=app.config['CSS']['button']['primary'],
                          table=table.display(class_))
Example #3
0
 def __call__(self, field: TableField, **kwargs: Any) -> TableMultiSelect:
     if field.data and isinstance(field.data, str):
         field.data = ast.literal_eval(field.data)
     class_ = field.id if field.id != 'given_place' else 'place'
     aliases = current_user.settings['table_show_aliases']
     if class_ in ['group', 'person', 'place']:
         entities = Entity.get_by_class(class_, types=True, aliases=aliases)
     else:
         entities = Entity.get_by_view(class_, types=True, aliases=aliases)
     table = Table([''] + g.table_headers[class_],
                   order=[[0, 'desc'], [1, 'asc']],
                   defs=[{
                       'orderDataType': 'dom-checkbox',
                       'targets': 0
                   }])
     for e in entities:
         data = get_base_table_data(e, show_links=False)
         data.insert(
             0, f"""
             <input type="checkbox" id="{e.id}" value="{e.name}"
             {'checked' if field.data and e.id in field.data else ''}>""")
         table.rows.append(data)
     return super().__call__(field, **kwargs) + render_template(
         'forms/table_multi_select.html',
         field=field,
         selection=[
             e.name for e in entities if field.data and e.id in field.data
         ],
         table=table)
Example #4
0
def build_table_form(class_: str, linked_entities: list[Entity]) -> str:
    """Returns a form with a list of entities with checkboxes."""
    if class_ == 'place':
        entities = Entity.get_by_class('place', types=True, aliases=True)
    else:
        entities = Entity.get_by_view(class_, types=True, aliases=True)
    linked_ids = [entity.id for entity in linked_entities]
    table = Table([''] + g.table_headers[class_], order=[[1, 'asc']])
    for entity in entities:
        if entity.id in linked_ids:
            continue  # Don't show already linked entries
        input_ = f"""
            <input
                id="selection-{entity.id}"
                name="values"
                type="checkbox" value="{entity.id}">"""
        table.rows.append([input_] +
                          get_base_table_data(entity, show_links=False))
    if not table.rows:
        return uc_first(_('no entries'))
    return render_template('forms/form_table.html',
                           table=table.display(class_))
Example #5
0
    def test_artifact(self) -> None:
        with app.app_context():
            with app.test_request_context():
                app.preprocess_request()  # type: ignore
                source = Entity.insert('source', 'Necronomicon')
                actor = Entity.insert('person', 'Conan')

            rv = self.app.get(url_for('insert', class_='artifact'))
            assert b'+ Artifact' in rv.data
            rv = self.app.post(
                url_for('insert', class_='artifact'),
                data={'name': 'Love-letter', 'actor': actor.id},
                follow_redirects=True)
            assert b'Love-letter' in rv.data
            rv = self.app.get(url_for('index', view='artifact'))
            assert b'Love-letter' in rv.data
            with app.test_request_context():
                app.preprocess_request()  # type: ignore
                artifact = Entity.get_by_view('artifact')[0]
            rv = self.app.get(url_for('update', id_=artifact.id))
            assert b'Love-letter' in rv.data
            rv = self.app.post(
                url_for('update', id_=artifact.id),
                follow_redirects=True,
                data={
                    'name': 'A little hate',
                    'description': 'makes nothing better'})
            assert b'Changes have been saved' in rv.data

            # Add to source
            rv = self.app.get(url_for('entity_add_source', id_=artifact.id))
            assert b'Link source' in rv.data
            rv = self.app.post(
                url_for('entity_add_source', id_=artifact.id),
                data={'checkbox_values': str([source.id])},
                follow_redirects=True)
            assert b'Necronomicon' in rv.data

            # Add to event
            rv = self.app.get(
                url_for('insert', class_='move', origin_id=artifact.id))
            assert b'A little hate' in rv.data
            rv = self.app.post(
                url_for('insert', class_='move', origin_id=artifact.id),
                data={'name': 'Event Horizon', 'artifact': [artifact.id]},
                follow_redirects=True)
            assert b'Event Horizon' in rv.data

            # Add to actor as owner
            rv = self.app.get(
                url_for('link_insert', id_=actor.id, view='artifact'))
            assert b'A little hate' in rv.data
            rv = self.app.post(
                url_for('link_insert', id_=actor.id, view='artifact'),
                data={'checkbox_values': [artifact.id]},
                follow_redirects=True)
            assert b'A little hate' in rv.data
            rv = self.app.get(url_for('view', id_=artifact.id))
            assert b'Owned by' in rv.data and b'Conan' in rv.data
            rv = self.app.get(
                url_for('insert', class_='artifact', origin_id=actor.id))
            assert b'Conan' in rv.data

            rv = self.app.get(
                url_for('index', view='artifact', delete_id=artifact.id))
            assert b'has been deleted' in rv.data

            # Insert and continue
            rv = self.app.post(
                url_for('insert', class_='artifact'),
                data={'name': 'This will be continued', 'continue_': 'yes'},
                follow_redirects=True)
            assert b'An entry has been created' in rv.data
Example #6
0
    def test_source(self) -> None:
        with app.app_context():  # type: ignore
            # Source insert
            rv = self.app.get(url_for('insert', class_='source'))
            assert b'+ Source' in rv.data
            with app.test_request_context():
                app.preprocess_request()  # type: ignore
                origin = Entity.insert('person', 'David Duchovny', 'person')
                actor = Entity.insert('person', 'Gillian Anderson Gillian Anderson', 'person')
                artifact = Entity.insert('artifact', 'I care for you')
                inscribed_artifact = Entity.insert('artifact', 'Artifact with text')
                file = Entity.insert('file', 'X-Files')
                reference = Entity.insert('external_reference', 'https://openatlas.eu')

            rv = self.app.post(
                url_for('insert', class_='source', origin_id=origin.id),
                data={'name': 'Test source'},
                follow_redirects=True)
            assert b'An entry has been created' in rv.data
            with app.test_request_context():
                app.preprocess_request()  # type: ignore
                source = Entity.get_by_view('source')[0]
            rv = self.app.post(
                url_for('insert', class_='source', origin_id=reference.id),
                data={'name': 'Test source'},
                follow_redirects=True)
            assert b'https://openatlas.eu' in rv.data
            rv = self.app.post(
                url_for('insert', class_='source', origin_id=file.id),
                data={'name': 'Test source'},
                follow_redirects=True)
            assert b'An entry has been created' in rv.data and b'X-Files' in rv.data
            data = {'name': 'Test source', 'continue_': 'yes'}
            rv = self.app.post(url_for('insert', class_='source'), data=data, follow_redirects=True)
            assert b'An entry has been created' in rv.data

            rv = self.app.get(url_for('insert', class_='source', origin_id=artifact.id))
            assert b'I care for you' in rv.data
            rv = self.app.post(
                url_for('insert', class_='source', origin_id=artifact.id),
                data={'name': 'Necronomicon', 'artifact': [artifact.id]},
                follow_redirects=True)
            assert b'I care for you' in rv.data

            rv = self.app.get(url_for('index', view='source'))
            assert b'Test source' in rv.data

            # Link source
            rv = self.app.post(
                url_for('insert', class_='external_reference', origin_id=source.id),
                data={'name': 'https://openatlas.eu'},
                follow_redirects=True)
            assert b'Test source' in rv.data

            self.app.get(url_for('source_add', id_=source.id, origin_id=actor.id, view='actor'))
            rv = self.app.post(
                url_for('source_add', id_=source.id, view='actor'),
                data={'checkbox_values': [actor.id]},
                follow_redirects=True)
            assert b'Gillian Anderson' in rv.data
            rv = self.app.get(url_for('entity_view', id_=source.id))
            assert b'Gillian Anderson' in rv.data
            rv = self.app.get(url_for('source_add', id_=source.id, view='place'))
            assert b'Place' in rv.data

            # Update source
            rv = self.app.get(url_for('update', id_=source.id))
            assert b'Test source' in rv.data
            data = {
                'name': 'Source updated',
                'description': 'some description',
                'artifact': str([inscribed_artifact.id])}
            rv = self.app.post(url_for('update', id_=source.id), data=data, follow_redirects=True)
            assert b'Source updated' in rv.data
            assert b'Artifact with text' in rv.data
            rv = self.app.get(url_for('entity_view', id_=source.id))
            assert b'some description' in rv.data

            # Add to source
            rv = self.app.get(url_for('entity_add_reference', id_=source.id))
            assert b'Link reference' in rv.data
            rv = self.app.post(
                url_for('entity_add_reference', id_=source.id),
                data={'reference': reference.id, 'page': '777'},
                follow_redirects=True)
            assert b'777' in rv.data

            # Translations
            rv = self.app.get(url_for('translation_insert', source_id=source.id))
            assert b'+ Text' in rv.data
            data = {'name': 'Test translation'}
            rv = self.app.post(url_for('translation_insert', source_id=source.id), data=data)
            with app.test_request_context():
                app.preprocess_request()  # type: ignore
                translation_id = rv.location.split('/')[-1]
            rv = self.app.get(url_for('entity_view', id_=source.id))
            assert b'Test translation' in rv.data
            self.app.get(url_for('translation_update', id_=translation_id, source_id=source.id))
            rv = self.app.post(
                url_for('translation_update', id_=translation_id, source_id=source.id),
                data={'name': 'Translation updated'},
                follow_redirects=True)
            assert b'Translation updated' in rv.data
            rv = self.app.get(
                url_for('translation_delete', id_=translation_id, source_id=source.id),
                follow_redirects=True)
            assert b'The entry has been deleted.' in rv.data
            data = {'name': 'Translation continued', 'continue_': 'yes'}
            self.app.post(url_for('translation_insert', source_id=source.id), data=data)

            # Delete source
            rv = self.app.get(url_for('index', view='source', delete_id=source.id))
            assert b'The entry has been deleted.' in rv.data
Example #7
0
    def test_event(self) -> None:
        with app.app_context():
            # Create entities for event
            place_name = 'Lewis and Clark'
            rv: Any = self.app.post(url_for('insert', class_='place'),
                                    data={
                                        'name': place_name,
                                        self.precision_geonames: '',
                                        self.precision_wikidata: ''
                                    })
            residence_id = rv.location.split('/')[-1]
            actor_name = 'Captain Miller'
            with app.test_request_context():
                app.preprocess_request()  # type: ignore
                actor = Entity.insert('person', actor_name)
                file = Entity.insert('file', 'X-Files')
                source = Entity.insert('source', 'Necronomicon')
                carrier = Entity.insert('artifact', 'Artifact')
                reference = Entity.insert('external_reference',
                                          'https://openatlas.eu')

            # Insert
            rv = self.app.get(url_for('insert', class_='activity'))
            assert b'+ Activity' in rv.data
            data = {
                'name': 'Event Horizon',
                'place': residence_id,
                self.precision_wikidata: ''
            }
            rv = self.app.post(url_for('insert',
                                       class_='activity',
                                       origin_id=reference.id),
                               data=data,
                               follow_redirects=True)
            assert bytes('Event Horizon', 'utf-8') in rv.data
            with app.test_request_context():
                app.preprocess_request()  # type: ignore
                activity_id = Entity.get_by_view('event')[0].id
            self.app.post(url_for('insert',
                                  class_='activity',
                                  origin_id=actor.id),
                          data=data)
            self.app.post(url_for('insert',
                                  class_='activity',
                                  origin_id=file.id),
                          data=data)
            self.app.post(url_for('insert',
                                  class_='activity',
                                  origin_id=source.id),
                          data=data)
            rv = self.app.get(
                url_for('insert', class_='activity', origin_id=residence_id))
            assert b'Location' in rv.data
            rv = self.app.get(
                url_for('insert', class_='move', origin_id=residence_id))
            assert b'Location' not in rv.data

            # Acquisition
            event_name2 = 'Second event'
            wikidata = \
                f"reference_system_id_" \
                f"{ReferenceSystem.get_by_name('Wikidata').id}"
            precision = Type.get_hierarchy('External reference match').subs[0]
            rv = self.app.post(url_for('insert', class_='acquisition'),
                               data={
                                   'name': event_name2,
                                   'given_place': [residence_id],
                                   'place': residence_id,
                                   'event': activity_id,
                                   'begin_year_from': '1949',
                                   'begin_month_from': '10',
                                   'begin_day_from': '8',
                                   'end_year_from': '1951',
                                   wikidata: 'Q123',
                                   self.precision_wikidata: precision
                               })
            event_id = rv.location.split('/')[-1]
            rv = self.app.get(url_for('view', id_=event_id))
            assert b'Event Horizon' in rv.data

            # Move
            rv = self.app.post(url_for('insert', class_='move'),
                               data={
                                   'name': 'Keep it moving',
                                   'place_to': residence_id,
                                   'place_from': residence_id,
                                   'artifact': carrier.id,
                                   'person': actor.id,
                                   self.precision_wikidata: ''
                               })
            move_id = rv.location.split('/')[-1]
            rv = self.app.get(url_for('view', id_=move_id))
            assert b'Keep it moving' in rv.data
            rv = self.app.get(url_for('view', id_=carrier.id))
            assert b'Keep it moving' in rv.data
            rv = self.app.get(url_for('update', id_=move_id))
            assert b'Keep it moving' in rv.data

            # Production
            rv = self.app.post(url_for('insert', class_='production'),
                               data={
                                   'name': 'A very productive event',
                                   'artifact': carrier.id,
                                   self.precision_wikidata: ''
                               })
            production_id = rv.location.split('/')[-1]
            rv = self.app.get(url_for('view', id_=production_id))
            assert b'Artifact' in rv.data
            rv = self.app.get(url_for('view', id_=carrier.id))
            assert b'A very productive event' in rv.data
            rv = self.app.get(url_for('update', id_=production_id))
            assert b'A very productive event' in rv.data

            # Add another event and test if events are seen at place
            event_name3 = 'Third event'
            self.app.post(url_for('insert', class_='acquisition'),
                          data={
                              'name': event_name3,
                              'given_place': [residence_id],
                              self.precision_geonames: '',
                              self.precision_wikidata: ''
                          })
            rv = self.app.get(url_for('view', id_=residence_id))
            assert bytes(place_name, 'utf-8') in rv.data
            rv = self.app.get(url_for('view', id_=actor.id))
            assert bytes(actor_name, 'utf-8') in rv.data
            rv = self.app.post(url_for('insert', class_='acquisition'),
                               follow_redirects=True,
                               data={
                                   'name': 'Event Horizon',
                                   'continue_': 'yes',
                                   self.precision_geonames: '',
                                   self.precision_wikidata: ''
                               })
            assert b'An entry has been created' in rv.data
            rv = self.app.get(url_for('index', view='event'))
            assert b'Event' in rv.data
            self.app.get(url_for('view', id_=activity_id))

            # Add to event
            rv = self.app.get(url_for('entity_add_file', id_=event_id))
            assert b'Link file' in rv.data
            rv = self.app.post(url_for('entity_add_file', id_=event_id),
                               data={'checkbox_values': str([file.id])},
                               follow_redirects=True)
            assert b'X-Files' in rv.data

            rv = self.app.get(url_for('entity_add_reference', id_=event_id))
            assert b'Link reference' in rv.data
            rv = self.app.post(url_for('entity_add_reference', id_=event_id),
                               data={
                                   'reference': reference.id,
                                   'page': '777'
                               },
                               follow_redirects=True)
            assert b'777' in rv.data

            # Update
            rv = self.app.get(url_for('update', id_=activity_id))
            assert b'Event Horizon' in rv.data
            rv = self.app.get(url_for('update', id_=event_id))
            assert b'Event Horizon' in rv.data
            data['name'] = 'Event updated'
            rv = self.app.post(url_for('update', id_=event_id),
                               data=data,
                               follow_redirects=True)
            assert b'Changes have been saved' in rv.data

            # Test super event validation
            rv = self.app.post(url_for('update', id_=event_id),
                               data={
                                   'name': 'Event',
                                   'event': event_id,
                                   'event_id': event_id
                               },
                               follow_redirects=True)
            assert b'Self as super not allowed' in rv.data

            # Preceding event
            rv = self.app.post(url_for('update', id_=event_id),
                               data={
                                   'name': 'Event',
                                   'event_preceding': event_id,
                                   'event_id': event_id
                               },
                               follow_redirects=True)
            assert b'Self as preceding not allowed' in rv.data
            rv = self.app.post(url_for('update', id_=event_id),
                               data={
                                   'name': 'Event with preceding',
                                   'event_preceding': activity_id,
                                   'event_id': event_id
                               },
                               follow_redirects=True)
            assert b'Event with preceding' in rv.data
            rv = self.app.get(url_for('view', id_=activity_id))
            assert b'Event with preceding' in rv.data

            # Delete
            rv = self.app.get(
                url_for('index', view='event', delete_id=event_id))
            assert b'The entry has been deleted.' in rv.data
Example #8
0
    def __call__(self, field: TableField, **kwargs: Any) -> TableMultiSelect:
        if field.data and isinstance(field.data, str):
            field.data = ast.literal_eval(field.data)
        class_ = field.id if field.id != 'given_place' else 'place'

        # Make checkbox column sortable and show selected on top
        table = Table([''] + g.table_headers[class_],
                      order=[[0, 'desc'], [1, 'asc']])

        # Table definitions (ordering and aligning)
        table.defs = [{'orderDataType': 'dom-checkbox', 'targets': 0}]
        if class_ == 'event':
            table.defs.append({
                'className': 'dt-body-right',
                'targets': [4, 5]
            })
        elif class_ in ['actor', 'group', 'feature', 'place']:
            table.defs.append({
                'className': 'dt-body-right',
                'targets': [3, 4]
            })

        if class_ in ['group', 'person', 'place']:
            entities = Entity.get_by_class(
                class_,
                nodes=True,
                aliases=current_user.settings['table_show_aliases'])
        else:
            entities = Entity.get_by_view(class_)

        for entity in entities:
            data = get_base_table_data(entity)
            data[0] = re.sub(re.compile('<a.*?>'), '', data[0])  # Remove links
            data.insert(
                0, """<input type="checkbox" id="{id}" {checked} value="{name}"
                class="multi-table-select">""".format(
                    id=str(entity.id),
                    name=entity.name,
                    checked='checked = "checked"'
                    if field.data and entity.id in field.data else ''))
            table.rows.append(data)
        selection = [
            entity.name for entity in entities
            if field.data and entity.id in field.data
        ]
        html = """
            <span
                id="{name}-button"
                class="{button_class}"
                onclick="$('#{name}-modal').modal('show')">
                    {change_label}
            </span><br>
            <div id="{name}-selection" class="selection" style="text-align:left;">{selection}</div>
            <div
                id="{name}-modal"
                class="modal fade"
                tabindex="-1"
                role="dialog"
                aria-hidden="true">
                <div class="modal-dialog" role="document" style="max-width: 100%!important;">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title">{title}</h5>
                            <button
                                type="button"
                                class="btn btn-outline-primary btn-sm"
                                data-dismiss="modal"
                                aria-label="Close">
                                    <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                        <div class="modal-body">{table}</div>
                        <div class="modal-footer">
                            <button
                                type="button"
                                class="btn btn-outline-primary btn-sm"
                                data-dismiss="modal"
                                onclick="selectFromTableMulti('{name}')">
                                    {close_label}
                            </button>
                        </div>
                    </div>
                </div>
            </div>
            <script>
            </script>""".format(
            name=field.id,
            button_class=app.config['CSS']['button']['secondary'],
            change_label=uc_first(_('change')),
            close_label=uc_first(_('close')),
            title=uc_first(_(field.id.replace('_', ' '))),
            selection='<br>'.join(selection),
            table=table.display(field.id))
        return super(TableMultiSelect, self).__call__(field, **kwargs) + html
Example #9
0
    def __call__(self, field: TableField, **kwargs: Any) -> TableSelect:
        file_stats = None
        place_fields = [
            'residence', 'begins_in', 'ends_in', 'place_to', 'place_from'
        ]
        class_ = 'place' if field.id in place_fields else field.id
        if class_ == 'place':
            aliases = current_user.settings['table_show_aliases']
            entities = Entity.get_by_class('place',
                                           nodes=True,
                                           aliases=aliases)
        elif class_ == 'reference':
            entities = Entity.get_by_class('bibliography') + \
                       Entity.get_by_class('edition') + \
                       Entity.get_by_class('external_reference')
        elif class_ == 'file':
            entities = Entity.get_by_class('file')
        else:
            entities = Entity.get_by_view(class_)
        table = Table([''] + g.table_headers[class_])

        selection = ''
        for entity in entities:
            if field.data and entity.id == int(field.data):
                selection = entity.name
            data = get_base_table_data(entity, file_stats)
            if len(entity.aliases) > 0:
                data[0] = """
                    <p>
                        <a onclick="selectFromTable(this, '{name}', {entity_id}, '{entity_name_clean}')"
                            href="#">{entity_name}</a>
                    </p>""".format(name=field.id,
                                   entity_id=entity.id,
                                   entity_name=entity.name,
                                   entity_name_clean=entity.name.replace(
                                       "'", ''))
            else:
                data[0] = """
                    <a
                        onclick="selectFromTable(this, '{name}', {entity_id}, '{entity_name_clean}')"
                        href="#">{entity_name}</a>
                    """.format(name=field.id,
                               entity_id=entity.id,
                               entity_name=entity.name,
                               entity_name_clean=entity.name.replace("'", ''))
            for i, (id_, alias) in enumerate(entity.aliases.items()):
                if i == len(entity.aliases) - 1:
                    data[0] = ''.join([data[0]] + [alias])
                else:
                    data[0] = ''.join([data[0]] + ['<p>' + alias + '</p>'])
            data.insert(
                0, """
                <div style="position: relative; top: 10px;">
                    <div
                        class="btn btn-outline-primary btn-xsm"
                        style="position: absolute; top: -22px;"
                        onclick="selectFromTable(this,'{name}', {entity_id}, '{entity_name_clean}')">
                            {label}
                    </div>
                </div>
                """.format(name=field.id,
                           entity_id=entity.id,
                           entity_name=entity.name,
                           entity_name_clean=entity.name.replace("'", ''),
                           label=uc_first(_('select'))))
            table.rows.append(data)
        html = """
            <input
                id="{name}-button"
                name="{name}-button"
                class="table-select {required}"
                type="text"
                placeholder="{change_label}"
                onfocus="this.blur()"
                readonly="readonly"
                value="{selection}"
                onclick="$('#{name}-modal').modal('show');">
            <a href="#"
                id="{name}-clear"
                class="{button_class}"
                {clear_style}
                onclick="clearSelect('{name}');">
                    {clear_label}
            </a>
            <div
                id="{name}-modal"
                class="modal fade"
                tabindex="-1"
                role="dialog"
                aria-hidden="true">
                <div class="modal-dialog" role="document" style="max-width: 100%!important;">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title">{title}</h5>
                            <button
                                type="button"
                                class="btn btn-outline-primary btn-sm"
                                data-dismiss="modal"
                                aria-label="Close">
                                    <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                        <div class="modal-body">{table}</div>
                        <div class="modal-footer">
                            <button
                                type="button"
                                class="btn btn-outline-primary btn-sm"
                                data-dismiss="modal">
                                    {close_label}
                            </button>
                        </div>
                    </div>
                </div>
            </div>
            """.format(
            name=field.id,
            title=uc_first(_(field.id.replace('_', ' '))),
            button_class=app.config['CSS']['button']['secondary'],
            change_label=uc_first(_('change')),
            clear_label=uc_first(_('clear')),
            close_label=uc_first(_('close')),
            table=table.display(field.id),
            selection=selection,
            clear_style='' if selection else ' style="display: none;" ',
            required=' required' if field.flags.required else '')
        return super(TableSelect, self).__call__(field, **kwargs) + html