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)
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_))
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)
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_))
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
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
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
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">×</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
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">×</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