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 hierarchy_update(id_: int) -> Union[str, Response]: hierarchy = g.nodes[id_] if hierarchy.standard: abort(403) form = build_form('hierarchy', hierarchy) form.forms.choices = Node.get_form_choices(hierarchy) if hasattr(form, 'multiple') and form.multiple.data: form.multiple.render_kw = {'disabled': 'disabled'} if form.validate_on_submit(): if form.name.data != hierarchy.name and Node.get_nodes(form.name.data): flash(_('error name exists'), 'error') else: save(form, hierarchy) flash(_('info update'), 'info') tab = 'value' if g.nodes[id_].value_type else 'custom' return redirect( f"{url_for('node_index')}#menu-tab-{tab}_collapse-{hierarchy.id}") form.multiple = hierarchy.multiple table = Table(paging=False) for form_id, form_ in hierarchy.forms.items(): count = Node.get_form_count(hierarchy, form_id) table.rows.append([ g.classes[form_['name']].label, format_number(count) if count else link( _('remove'), url_for('remove_form', id_=hierarchy.id, form_id=form_id)) ]) return render_template('display_form.html', form=form, table=table, manual_page='entity/type', title=_('types'), crumbs=[[_('types'), url_for('node_index')], hierarchy, _('edit')])
def build_move_form(form: Any, node: Node) -> FlaskForm: root = g.nodes[node.root[-1]] setattr(form, str(root.id), TreeField(str(root.id))) form_instance = form(obj=node) # Delete custom fields except the ones specified for the form delete_list = [ ] # Can't delete fields in the loop so creating a list for later deletion for field in form_instance: if type(field) is TreeField and int(field.id) != root.id: delete_list.append(field.id) for item in delete_list: delattr(form_instance, item) choices = [] if root.class_.code == 'E53': 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 LinkMapper.get_entities_by_node(node): domain = EntityMapper.get_by_id(row.domain_id) range_ = EntityMapper.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_instance.selection.choices = choices return form_instance
def node_move_entities(id_: int) -> Union[str, Response]: node = g.nodes[id_] root = g.nodes[node.root[-1]] if root.value_type: # pragma: no cover abort(403) form = build_move_form(node) if form.validate_on_submit(): Transaction.begin() Node.move_entities(node, getattr(form, str(root.id)).data, form.checkbox_values.data) Transaction.commit() flash(_('Entities were updated'), 'success') if node.class_.name == 'administrative_unit': tab = 'places' elif root.standard: tab = 'standard' elif node.value_type: # pragma: no cover tab = 'value' else: tab = 'custom' return redirect( f"{url_for('node_index')}#menu-tab-{tab}_collapse-{root.id}") getattr(form, str(root.id)).data = node.id return render_template('types/move.html', table=Table(header=['#', _('selection')], rows=[[item, item.label.text] for item in form.selection]), root=root, form=form, entity=node, crumbs=[[_('types'), url_for('node_index')], root, node, _('move entities')])
def node_move_entities(id_: int) -> Union[str, Response]: node = g.nodes[id_] root = g.nodes[node.root[-1]] if node.class_.name == 'administrative_unit': tab_hash = '#menu-tab-places_collapse-' elif root.standard: tab_hash = '#menu-tab-standard_collapse-' elif node.value_type: # pragma: no cover tab_hash = '#menu-tab-value_collapse-' else: tab_hash = '#menu-tab-custom_collapse-' if root.value_type: # pragma: no cover abort(403) form = build_move_form(node) if form.validate_on_submit(): Transaction.begin() Node.move_entities(node, getattr(form, str(root.id)).data, form.checkbox_values.data) Transaction.commit() flash(_('Entities were updated'), 'success') return redirect(url_for('node_index') + tab_hash + str(root.id)) getattr(form, str(root.id)).data = node.id return render_template( 'types/move.html', node=node, root=root, form=form, title=_('types'), crumbs=[[_('types'), url_for('node_index')], root, node, _('move entities')])
def remove_form(id_: int, form_id: int) -> Response: root = g.nodes[id_] if Node.get_form_count(root, form_id): abort(403) # pragma: no cover try: Node.remove_form_from_hierarchy(form_id, root.id) flash(_('info update'), 'info') except Exception as e: # pragma: no cover logger.log('error', 'database', 'remove form from hierarchy failed', e) flash(_('error database'), 'error') return redirect(url_for('hierarchy_update', id_=id_))
def check_single_type_duplicates() -> List[List[str]]: from openatlas.models.node import Node from openatlas.models.entity import Entity data = [] for node in g.nodes.values(): if node.root or node.multiple or node.value_type: continue # pragma: no cover node_ids = Node.get_all_sub_ids(node) if not node_ids: continue # pragma: no cover for id_ in Db.check_single_type_duplicates(node_ids): offending_nodes = [] entity = Entity.get_by_id(id_, nodes=True) for entity_node in entity.nodes: if g.nodes[entity_node.root[-1]].id != node.id: continue # pragma: no cover offending_nodes.append( '<a href="{url}">{label}</a> {name}'.format( label=uc_first(_('remove')), name=entity_node.name, url=url_for('admin_delete_single_type_duplicate', entity_id=entity.id, node_id=entity_node.id))) data.append([ link(entity), entity.class_.name, link(g.nodes[node.id]), '<br><br><br><br><br>'.join(offending_nodes) ]) return data
def before_request() -> None: from openatlas.models.model import CidocClass, CidocProperty from openatlas.models.node import Node from openatlas.models.settings import Settings from openatlas.models.reference_system import ReferenceSystem if request.path.startswith('/static'): # pragma: no cover return # Avoid overhead for files if not using Apache with static alias open_connection(app.config) session['settings'] = Settings.get_settings() session['language'] = get_locale() g.cidoc_classes = CidocClass.get_all() g.properties = CidocProperty.get_all() from openatlas.models import system g.table_headers = system.get_table_headers() g.classes = system.get_system_classes() g.view_class_mapping = system.view_class_mapping g.class_view_mapping = system.get_class_view_mapping() g.nodes = Node.get_all_nodes() g.reference_systems = ReferenceSystem.get_all() g.file_stats = None # Set max file upload in MB app.config['MAX_CONTENT_LENGTH'] = \ session['settings']['file_upload_max_size'] * 1024 * 1024 if request.path.startswith('/api/'): ip = request.environ.get('HTTP_X_REAL_IP', request.remote_addr) if not current_user.is_authenticated \ and not session['settings']['api_public'] \ and ip not in app.config['ALLOWED_IPS']: raise AccessDeniedError # pragma: no cover
def hierarchy_insert(param: str) -> Union[str, Response]: form = build_form('hierarchy', code=param) form.forms.choices = Node.get_form_choices() if form.validate_on_submit(): if Node.check_hierarchy_exists(form.name.data): flash(_('error name exists'), 'error') return render_template('display_form.html', form=form) save(form, param=param) flash(_('entity created'), 'info') return redirect(url_for('node_index') + '#menu-tab-' + param) return render_template( 'display_form.html', form=form, manual_page='entity/type', title=_('types'), crumbs=[[_('types'), url_for('node_index')], '+ ' + uc_first(_(param))])
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
def print_standard_type(self) -> str: from openatlas.models.node import Node if not self.class_.standard_type: return '' root_id = Node.get_hierarchy(self.class_.standard_type).id for node in self.nodes: if node.root and node.root[-1] == root_id: return link(node) return ''
def __call__(self, field: TreeField, **kwargs: Any) -> TreeMultiSelect: data: List[int] = [] if field.data: data = ast.literal_eval(field.data) \ if isinstance(field.data, str) else field.data html = render_template('forms/tree_multi_select.html', field=field, root=g.nodes[int(field.id)], selection=sorted( [g.nodes[id_].name for id_ in data]), data=Node.get_tree_data(int(field.id), data)) return super(TreeMultiSelect, self).__call__(field, **kwargs) + html
def add_types(form: Any, class_: str) -> None: types = OrderedDict(Node.get_nodes_for_form(class_)) for node in types.values(): # Move standard type to top if node.standard and node.class_.name == 'type': types.move_to_end(node.id, last=False) break for node in types.values(): if node.multiple: setattr(form, str(node.id), TreeMultiField(str(node.id))) else: setattr(form, str(node.id), TreeField(str(node.id))) if node.value_type: add_value_type_fields(form, node.subs)
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)
def __call__(self, field: TreeField, **kwargs: Any) -> TreeSelect: selection = '' selected_ids = [] if field.data: field.data = field.data[0] \ if isinstance(field.data, list) else field.data selection = g.nodes[int(field.data)].name selected_ids.append(g.nodes[int(field.data)].id) html = render_template('forms/tree_select.html', field=field, selection=selection, data=Node.get_tree_data(int(field.id), selected_ids)) return super(TreeSelect, self).__call__(field, **kwargs) + html
def show_untyped_entities(id_: int) -> str: hierarchy = g.nodes[id_] table = Table(['name', 'class', 'first', 'last', 'description']) for entity in Node.get_untyped(hierarchy.id): table.rows.append([ link(entity), entity.class_.label, entity.first, entity.last, entity.description ]) return render_template('table.html', entity=hierarchy, table=table, crumbs=[[_('types'), url_for('node_index')], link(hierarchy), _('untyped entities')])
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)
def get_node_overview() -> Dict[str, Dict[Entity, str]]: nodes: Dict[str, Any] = { 'standard': {}, 'custom': {}, 'places': {}, 'value': {}} for node in g.nodes.values(): if node.root: continue type_ = 'custom' if node.class_.name == 'administrative_unit': type_ = 'places' elif node.standard: type_ = 'standard' elif node.value_type: type_ = 'value' nodes[type_][node.name] = GetNodeOverview.walk_tree( Node.get_nodes(node.name)) return nodes
def node_index() -> str: nodes: Dict[str, Dict[Entity, str]] = \ {'standard': {}, 'custom': {}, 'places': {}, 'value': {}} for node in g.nodes.values(): if node.root: continue type_ = 'custom' if node.class_.name == 'administrative_unit': type_ = 'places' elif node.standard: type_ = 'standard' elif node.value_type: type_ = 'value' nodes[type_][node] = render_template('forms/tree_select_item.html', name=sanitize(node.name), data=walk_tree( Node.get_nodes(node.name))) return render_template('types/index.html', nodes=nodes, title=_('types'), crumbs=[_('types')])
def test_duplicates(self) -> None: with app.app_context(): # type: ignore 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_node = Node.get_hierarchy('Source') source.link('P2', g.nodes[source_node.subs[0]]) source.link('P2', g.nodes[source_node.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, node_id=source_node.subs[0]), follow_redirects=True) assert b'Congratulations, everything looks fine!' in rv.data
def add_reference_systems(form: Any, form_name: str) -> None: precisions = [('', '')] for id_ in Node.get_hierarchy('External reference match').subs: precisions.append((str(g.nodes[id_].id), g.nodes[id_].name)) for system in g.reference_systems.values(): if form_name not in [ form_['name'] for form_ in system.get_forms().values() ]: continue setattr( form, 'reference_system_id_{id}'.format(id=system.id), StringField(system.name, validators=[OptionalValidator()], description=system.description, render_kw={ 'autocomplete': 'off', 'placeholder': system.placeholder })) setattr( form, 'reference_system_precision_{id}'.format(id=system.id), SelectField(_('precision'), choices=precisions, default=system.precision_default_id))
def add_reference_systems(form: Any, form_name: str) -> None: precision_nodes = Node.get_hierarchy('External reference match').subs precisions = [('', '')] + [(str(g.nodes[id_].id), g.nodes[id_].name) for id_ in precision_nodes] systems = list(g.reference_systems.values()) systems.sort(key=lambda x: x.name.casefold()) for system in systems: if form_name \ not in [form_['name'] for form_ in system.get_forms().values()]: continue setattr( form, f'reference_system_id_{system.id}', StringField(uc_first(system.name), [OptionalValidator()], description=system.description, render_kw={ 'autocomplete': 'off', 'placeholder': system.placeholder })) setattr( form, f'reference_system_precision_{system.id}', SelectField(_('precision'), choices=precisions, default=system.precision_default_id))
def tree_select(name: str) -> str: from openatlas.models.node import Node return """ <div id="{name}-tree"></div> <script> $(document).ready(function () {{ $("#{name}-tree").jstree({{ "search": {{ "case_insensitive": true, "show_only_matches": true }}, "plugins" : ["core", "html_data", "search"], "core": {{ "data": {tree_data} }} }}); $("#{name}-tree").on("select_node.jstree", function (e, data) {{ document.location.href = data.node.original.href; }}); $("#{name}-tree-search").keyup(function() {{ if (this.value.length >= {min_chars}) {{ $("#{name}-tree").jstree("search", $(this).val()); }} }}); }}); </script>""".format( min_chars=session['settings']['minimum_jstree_search'], name=sanitize(name), tree_data=walk_tree(Node.get_nodes(name)))
def before_request() -> None: from openatlas.models.model import CidocClass, CidocProperty from openatlas.models.node import Node from openatlas.models.settings import Settings from openatlas.models.reference_system import ReferenceSystem if request.path.startswith('/static'): # pragma: no cover return # Only needed if not running with Apache and static alias open_connection(app.config) session['settings'] = Settings.get_settings() session['language'] = get_locale() g.cidoc_classes = CidocClass.get_all() g.properties = CidocProperty.get_all() from openatlas.models import system g.table_headers = system.get_table_headers() g.classes = system.get_system_classes() g.view_class_mapping = system.view_class_mapping g.class_view_mapping = system.get_class_view_mapping() g.nodes = Node.get_all_nodes() g.reference_systems = ReferenceSystem.get_all() # Set max file upload in MB app.config['MAX_CONTENT_LENGTH'] = session['settings'][ 'file_upload_max_size'] * 1024 * 1024
def get_type_tree() -> List[Dict[int, Dict[str, Any]]]: return [{ id_: GetTypeTree.serialize_to_json(node) } for id_, node in Node.get_all_nodes().items()]
def test_place(self) -> None: with app.app_context(): # type: ignore rv = self.app.get(url_for('insert', class_='place')) assert b'+ Place' in rv.data with app.test_request_context(): app.preprocess_request() # type: ignore unit_node = Node.get_hierarchy('Administrative unit') unit_sub1 = g.nodes[unit_node.subs[0]] unit_sub2 = g.nodes[unit_node.subs[1]] reference = Entity.insert('external_reference', 'https://openatlas.eu') place_node = Node.get_hierarchy('Place') source = Entity.insert('source', 'Necronomicon') geonames = \ f"reference_system_id_" \ f"{ReferenceSystem.get_by_name('GeoNames').id}" precision = Node.get_hierarchy('External reference match').subs[0] data = { 'name': 'Asgard', 'alias-0': 'Valhöll', unit_node.id: str([unit_sub1.id, unit_sub2.id]), geonames: '123456', self.precision_geonames: precision, self.precision_wikidata: '' } rv = self.app.post(url_for('insert', class_='place', origin_id=reference.id), data=data, follow_redirects=True) assert b'Asgard' in rv.data \ and b'An entry has been created' in rv.data rv = self.app.get(url_for('entity_view', id_=precision)) assert b'Asgard' in rv.data rv = self.app.get( url_for('entity_view', id_=ReferenceSystem.get_by_name('GeoNames').id)) assert b'Asgard' in rv.data data['gis_points'] = """[{ "type": "Feature", "geometry": {"type":"Point","coordinates":[9,17]}, "properties": { "name": "Valhalla", "description": "", "shapeType": "centerpoint"}}]""" data['gis_lines'] = """[{ "type": "Feature", "geometry":{ "type": "LineString", "coordinates": [ [9.75307425847859,17.8111792731339], [9.75315472474904,17.8110005175436], [9.75333711496205,17.8110873417098]]}, "properties": { "name": "", "description": "", "shapeType": "line"}}]""" data['gis_polygons'] = """[{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[ [9.75307425847859,17.8111792731339], [9.75315472474904,17.8110005175436], [9.75333711496205,17.8110873417098], [9.75307425847859,17.8111792731339]]]}, "properties":{ "name": "", "description": "", "shapeType": "shape"}}]""" data[place_node.id] = place_node.subs data['continue_'] = 'yes' rv = self.app.post(url_for('insert', class_='place', origin_id=source.id), data=data, follow_redirects=True) assert b'Necronomicon' in rv.data with app.test_request_context(): app.preprocess_request() # type: ignore places = Entity.get_by_class('place') place = places[0] place2 = places[1] location = place2.get_linked_entity_safe('P53') actor = Entity.insert('person', 'Milla Jovovich') actor.link('P74', location) assert b'Necronomicon' in rv.data rv = self.app.get(url_for('index', view='place')) assert b'Asgard' in rv.data rv = self.app.get(url_for('update', id_=place.id)) assert b'Valhalla' in rv.data data['continue_'] = '' data['alias-1'] = 'Val-hall' data['geonames_id'] = '321' rv = self.app.post(url_for('update', id_=place.id), data=data, follow_redirects=True) assert b'Val-hall' in rv.data # Test error when viewing the corresponding location rv = self.app.get(url_for('entity_view', id_=place.id + 1)) assert b'be viewed directly' in rv.data # Test with same GeoNames id rv = self.app.post(url_for('update', id_=place.id), data=data, follow_redirects=True) assert b'Val-hall' in rv.data # Test with same GeoNames id but different precision data['geonames_precision'] = '' rv = self.app.post(url_for('update', id_=place.id), data=data, follow_redirects=True) assert b'Val-hall' in rv.data # Test update without the previous GeoNames id data['geonames_id'] = '' rv = self.app.post(url_for('update', id_=place.id), data=data, follow_redirects=True) assert b'Val-hall' in rv.data with app.test_request_context(): app.preprocess_request() # type: ignore event = Entity.insert('acquisition', 'Valhalla rising') event.link('P7', location) event.link('P24', location) rv = self.app.get(url_for('entity_view', id_=place2.id)) assert rv.data and b'Valhalla rising' in rv.data # Test invalid geom data['gis_polygons'] = """[{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[ [298.9893436362036, -5.888919049309554], [299.00444983737543, -5.9138487869408545], [299.00650977389887, -5.893358673645309], [298.9848804404028, -5.9070188333813585], [298.9893436362036, -5.888919049309554]]]}, "properties": { "name": "", "description": "", "shapeType": "shape"}}]""" rv = self.app.post(url_for('insert', class_='place', origin_id=source.id), data=data, follow_redirects=True) assert b'An invalid geometry was entered' in rv.data # Test Overlays path = \ pathlib.Path(app.root_path) \ / 'static' / 'images' / 'layout' / 'logo.png' with open(path, 'rb') as img: rv = self.app.post(url_for('insert', class_='file', origin_id=place.id), data={ 'name': 'X-Files', 'file': img }, follow_redirects=True) assert b'An entry has been created' in rv.data with app.test_request_context(): app.preprocess_request() # type: ignore file = Entity.get_by_class('file')[0] link_id = Link.insert(file, 'P67', place)[0] rv = self.app.get( url_for('overlay_insert', image_id=file.id, place_id=place.id, link_id=link_id)) assert b'X-Files' in rv.data data = { 'top_left_easting': 42, 'top_left_northing': 12, 'top_right_easting': 43, 'top_right_northing': 13, 'bottom_left_easting': 10, 'bottom_left_northing': 20 } rv = self.app.post(url_for('overlay_insert', image_id=file.id, place_id=place.id, link_id=link_id), data=data, follow_redirects=True) assert b'Edit' in rv.data if os.name == "posix": # Ignore for other OS e.g. Windows with app.test_request_context(): app.preprocess_request() # type: ignore overlay = Overlay.get_by_object(place) overlay_id = overlay[list(overlay.keys())[0]].id rv = self.app.get( url_for('overlay_update', id_=overlay_id, place_id=place.id, link_id=link_id)) assert b'42' in rv.data rv = self.app.post(url_for('overlay_update', id_=overlay_id, place_id=place.id, link_id=link_id), data=data, follow_redirects=True) assert b'Changes have been saved' in rv.data self.app.get(url_for('overlay_remove', id_=overlay_id, place_id=place.id), follow_redirects=True) # Add to place rv = self.app.get(url_for('entity_add_file', id_=place.id)) assert b'Link file' in rv.data rv = self.app.post(url_for('entity_add_file', id_=place.id), data={'checkbox_values': str([file.id])}, follow_redirects=True) assert b'X-Files' in rv.data rv = self.app.get( url_for('reference_add', id_=reference.id, view='place')) assert b'Val-hall' in rv.data rv = self.app.get(url_for('entity_add_reference', id_=place.id)) assert b'Link reference' in rv.data rv = self.app.post(url_for('entity_add_reference', id_=place.id), data={ 'reference': reference.id, 'page': '777' }, follow_redirects=True) assert b'777' in rv.data # Place types rv = self.app.get(url_for('node_move_entities', id_=unit_sub1.id)) assert b'Asgard' in rv.data # Test move entities of multiple node if link to new node exists rv = self.app.post(url_for('node_move_entities', id_=unit_sub1.id), follow_redirects=True, data={ unit_node.id: unit_sub2.id, 'selection': location.id, 'checkbox_values': str([location.id]) }) assert b'Entities were updated' in rv.data # Test move entities of multiple node rv = self.app.post(url_for('node_move_entities', id_=unit_sub2.id), follow_redirects=True, data={ unit_node.id: unit_sub1.id, 'selection': location.id, 'checkbox_values': str([location.id]) }) assert b'Entities were updated' in rv.data # Subunits data = { 'name': "Try continue", 'continue_': 'sub', self.precision_geonames: precision, self.precision_wikidata: '' } self.app.get(url_for('insert', class_='place')) rv = self.app.post(url_for('insert', class_='place'), data=data, follow_redirects=True) assert b'Insert and add strati' in rv.data data['name'] = "It's not a bug, it's a feature!" rv = self.app.get( url_for('insert', class_='stratigraphic_unit', origin_id=place.id)) assert b'Insert and add find' in rv.data rv = self.app.post(url_for('insert', class_='place', origin_id=place.id), data=data) feat_id = rv.location.split('/')[-1] self.app.get(url_for('insert', class_='place', origin_id=feat_id)) self.app.get(url_for('update', id_=feat_id)) self.app.post(url_for('update', id_=feat_id), data=data) data['name'] = "I'm a stratigraphic unit" rv = self.app.post(url_for('insert', class_='place', origin_id=feat_id), data=data) stratigraphic_id = rv.location.split('/')[-1] self.app.get( url_for('insert', class_='place', origin_id=stratigraphic_id)) self.app.get(url_for('update', id_=stratigraphic_id)) self.app.post(url_for('update', id_=stratigraphic_id), data={'name': "I'm a stratigraphic unit"}) dimension_node_id = Node.get_hierarchy('Dimensions').subs[0] data = { 'name': 'You never find me', dimension_node_id: 50, self.precision_geonames: precision, self.precision_wikidata: '' } rv = self.app.post(url_for('insert', class_='find', origin_id=stratigraphic_id), data=data) find_id = rv.location.split('/')[-1] rv = self.app.post(url_for('update', id_=find_id), data=data, follow_redirects=True) assert b'50' in rv.data self.app.get(url_for('update', id_=find_id)) data = { 'name': 'My human remains', self.precision_geonames: precision, self.precision_wikidata: '' } rv = self.app.post(url_for('insert', class_='human_remains', origin_id=stratigraphic_id), data=data) human_remains_id = rv.location.split('/')[-1] rv = self.app.get(url_for('update', id_=human_remains_id)) assert b'My human remains' in rv.data rv = self.app.get('/') assert b'My human remains' in rv.data rv = self.app.get(url_for('entity_view', id_=feat_id)) assert b'not a bug' in rv.data rv = self.app.get(url_for('entity_view', id_=stratigraphic_id)) assert b'a stratigraphic unit' in rv.data rv = self.app.get(url_for('entity_view', id_=find_id)) assert b'You never' in rv.data rv = self.app.get(url_for('index', view='place', delete_id=place.id), follow_redirects=True) assert b'not possible if subunits' in rv.data rv = self.app.get(url_for('index', view='place', delete_id=find_id), follow_redirects=True) assert b'The entry has been deleted.' in rv.data rv = self.app.get( url_for('index', view='place', delete_id=place2.id)) assert b'The entry has been deleted.' in rv.data
def test_reference_system(self) -> None: with app.app_context(): # type: ignore rv = self.app.get(url_for('index', view='reference_system')) assert b'GeoNames' in rv.data geonames = ReferenceSystem.get_by_name('GeoNames') wikidata = ReferenceSystem.get_by_name('Wikidata') precision_id = Node.get_hierarchy( 'External reference match').subs[0] rv = self.app.get(url_for('insert', class_='reference_system')) assert b'Resolver URL' in rv.data data = { 'name': 'Wikipedia', 'website_url': 'https://wikipedia.org', 'resolver_url': 'https://wikipedia.org', 'forms': [geonames.forms[0]] } rv = self.app.post(url_for('insert', class_='reference_system'), follow_redirects=True, data=data) assert b'An entry has been created.' in rv.data wikipedia_id = ReferenceSystem.get_by_name('Wikipedia').id rv = self.app.get(url_for('index', view='reference_system', delete_id=wikipedia_id), follow_redirects=True) assert b'Deletion not possible if forms are attached' in rv.data rv = self.app.get(url_for('reference_system_remove_form', system_id=wikipedia_id, form_id=geonames.forms[0]), follow_redirects=True) assert b'Changes have been saved' in rv.data rv = self.app.get( url_for('index', view='reference_system', delete_id=wikipedia_id)) assert b'The entry has been deleted' in rv.data rv = self.app.post(url_for('update', id_=geonames.id)) assert b'Website URL' in rv.data data = { 'name': 'GeoNames', Node.get_hierarchy('External reference match').id: precision_id, 'website_url': 'https://www.geonames2.org/', 'resolver_url': 'https://www.geonames2.org/' } rv = self.app.post(url_for('update', id_=geonames.id), follow_redirects=True, data=data) assert b'Changes have been saved.' in rv.data rv = self.app.post(url_for('update', id_=geonames.id), follow_redirects=True, data=data) assert b'https://www.geonames2.org/' in rv.data rv = self.app.post(url_for('insert', class_='person'), data={ 'name': 'Actor test', 'reference_system_id_' + str(wikidata.id): 'Q123', self.precision_geonames: '', self.precision_wikidata: precision_id }) person_id = rv.location.split('/')[-1] rv = self.app.get(url_for('entity_view', id_=wikidata.id), follow_redirects=True) assert b'Actor test' in rv.data rv = self.app.get(url_for('entity_view', id_=person_id), follow_redirects=True) assert b'Wikidata' in rv.data rv = self.app.get(url_for('update', id_=person_id)) assert b'Q123' in rv.data # Testing errors rv = self.app.post(url_for('insert', class_='reference_system'), follow_redirects=True, data={'name': 'GeoNames'}) assert b'A transaction error occurred' in rv.data rv = self.app.get( url_for('index', view='reference_system', delete_id=geonames.id)) assert b'403' in rv.data rv = self.app.post(url_for('insert', class_='person'), data={ 'name': 'Actor with Wikidata but without precision', 'reference_system_id_' + str(wikidata.id): 'Q123', self.precision_geonames: '', self.precision_wikidata: '' }) assert b'required' in rv.data rv = self.app.post(url_for('insert', class_='person'), data={ 'name': 'Actor with invalid Wikidata id', 'reference_system_id_' + str(wikidata.id): 'invalid id', self.precision_geonames: '', self.precision_wikidata: precision_id }) assert b'Wrong id format' in rv.data rv = self.app.post(url_for('insert', class_='place'), data={ 'name': 'Reference test', 'reference_system_id_' + str(geonames.id): 'invalid id', self.precision_geonames: '', self.precision_wikidata: '' }) assert b'Wrong id format' in rv.data rv = self.app.get(url_for('reference_system_remove_form', system_id=geonames.id, form_id=geonames.forms[0]), follow_redirects=True) assert b'Changes have been saved' in rv.data rv = self.app.get( url_for('index', view='reference_system', delete_id=geonames.id)) assert b'403 - Forbidden' in rv.data
def add_fields(form: Any, class_: str, code: Union[str, None], item: Union[Entity, Node, Link, None], origin: Union[Entity, Node, None]) -> None: if class_ == 'actor_actor_relation': setattr(form, 'inverse', BooleanField(_('inverse'))) if not item: setattr(form, 'actor', TableMultiField(_('actor'), [InputRequired()])) setattr(form, 'relation_origin_id', HiddenField()) elif class_ in ['activity', 'acquisition', 'move']: setattr(form, 'event_id', HiddenField()) setattr(form, 'event', TableField(_('sub event of'))) if class_ == 'activity': setattr(form, 'place', TableField(_('location'))) if class_ == 'acquisition': setattr(form, 'place', TableField(_('location'))) setattr(form, 'given_place', TableMultiField(_('given place'))) elif class_ == 'move': setattr(form, 'place_from', TableField(_('from'))) setattr(form, 'place_to', TableField(_('to'))) setattr(form, 'artifact', TableMultiField()) setattr(form, 'person', TableMultiField()) elif class_ == 'file' and not item: setattr(form, 'file', FileField(_('file'), [InputRequired()])) elif class_ == 'group': setattr(form, 'residence', TableField(_('residence'))) setattr(form, 'begins_in', TableField(_('begins in'))) setattr(form, 'ends_in', TableField(_('ends in'))) elif class_ == 'hierarchy': if code == 'custom' or (item and not item.value_type): setattr( form, 'multiple', BooleanField(_('multiple'), description=_('tooltip hierarchy multiple'))) setattr( form, 'forms', SelectMultipleField(_('classes'), render_kw={'disabled': True}, description=_('tooltip hierarchy forms'), choices=[], option_widget=widgets.CheckboxInput(), widget=widgets.ListWidget(prefix_label=False), coerce=int)) elif class_ == 'involvement': if not item and origin: involved_with = 'actor' if origin.class_.view == 'event' else 'event' setattr(form, involved_with, TableMultiField(_(involved_with), [InputRequired()])) setattr(form, 'activity', SelectField(_('activity'))) elif class_ == 'member' and not item: setattr(form, 'member_origin_id', HiddenField()) setattr(form, 'actor' if code == 'member' else 'group', TableMultiField(_('actor'), [InputRequired()])) elif class_ in g.classes and g.classes[class_].view == 'type': setattr(form, 'is_node_form', HiddenField()) node = item if item else origin root = g.nodes[node.root[-1]] if node.root else node setattr(form, str(root.id), TreeField(str(root.id))) if root.directional: setattr(form, 'name_inverse', StringField(_('inverse'))) elif class_ == 'person': setattr(form, 'residence', TableField(_('residence'))) setattr(form, 'begins_in', TableField(_('born in'))) setattr(form, 'ends_in', TableField(_('died in'))) elif class_ == 'reference_system': setattr( form, 'website_url', StringField(_('website URL'), validators=[OptionalValidator(), URL()])) setattr( form, 'resolver_url', StringField(_('resolver URL'), validators=[OptionalValidator(), URL()])) setattr(form, 'placeholder', StringField(_('example ID'))) precision_node_id = str( Node.get_hierarchy('External reference match').id) setattr(form, precision_node_id, TreeField(precision_node_id)) choices = ReferenceSystem.get_form_choices(item) if choices: setattr( form, 'forms', SelectMultipleField( _('forms'), render_kw={'disabled': True}, choices=choices, option_widget=widgets.CheckboxInput(), widget=widgets.ListWidget(prefix_label=False), coerce=int)) elif class_ == 'source': setattr(form, 'artifact', TableMultiField())
def save_nodes(self, form: FlaskForm) -> None: from openatlas.models.node import Node Node.save_entity_nodes(self, form)
def test_event(self) -> None: with app.app_context(): # type: ignore # Create entities for file with app.test_request_context(): app.preprocess_request() # type: ignore actor = Entity.insert('person', 'File keeper') reference = Entity.insert('edition', 'Ancient Books') node_id = Node.get_hierarchy('Sex').subs[0] # Insert rv = self.app.get( url_for('insert', class_='file', origin_id=actor.id)) assert b'+ File' in rv.data logo = \ pathlib.Path(app.root_path) \ / 'static' / 'images' / 'layout' / 'logo.png' with open(logo, 'rb') as img: rv = self.app.post(url_for('insert', class_='file', origin_id=actor.id), data={ 'name': 'OpenAtlas logo', 'file': img }, follow_redirects=True) assert b'An entry has been created' in rv.data with open(logo, 'rb') as img1, open(logo, 'rb') as img2: rv = self.app.post(url_for('insert', class_='file', origin_id=actor.id), data={ 'name': 'OpenAtlas logo', 'file': [img1, img2] }, follow_redirects=True) assert b'An entry has been created' in rv.data with open(logo, 'rb') as img: rv = self.app.post(url_for('insert', class_='file', origin_id=reference.id), data={ 'name': 'OpenAtlas logo', 'file': img }, follow_redirects=True) assert b'An entry has been created' in rv.data with app.test_request_context(): app.preprocess_request() # type: ignore files = Entity.get_by_class('file') file_id = files[0].id file_id2 = files[1].id # Logo rv = self.app.get(url_for('admin_logo'), data={'file': file_id}, follow_redirects=True) assert b'OpenAtlas logo' in rv.data with self.app.get( url_for('display_logo', filename=str(file_id) + '.png')): pass # Test logo with "with" to prevent unclosed files warning rv = self.app.get(url_for('admin_logo', id_=file_id), follow_redirects=True) assert b'Remove custom logo' in rv.data rv = self.app.get(url_for('admin_index', action="remove_logo", id_=0), follow_redirects=True) assert b'Logo' in rv.data with open( pathlib.Path(app.root_path) / 'views' / 'index.py', 'rb') \ as invalid_file: rv = self.app.post(url_for('insert', class_='file', origin_id=actor.id), data={ 'name': 'Invalid file', 'file': invalid_file }, follow_redirects=True) assert b'File type not allowed' in rv.data rv = self.app.post(url_for('insert', class_='file', origin_id=actor.id), follow_redirects=True, data={'name': 'This is not a file'}) assert b'This field is required' in rv.data # View rv = self.app.get(url_for('entity_view', id_=file_id)) assert b'OpenAtlas logo' in rv.data rv = self.app.get(url_for('entity_view', id_=file_id2)) assert b'OpenAtlas logo' in rv.data with self.app.get( url_for('download_file', filename=str(file_id) + '.png')): pass # Calling with "with" to prevent unclosed files warning with self.app.get( url_for('display_file', filename=str(file_id) + '.png')): pass # Calling with "with" to prevent unclosed files warning # Index rv = self.app.get(url_for('index', view='file')) assert b'OpenAtlas logo' in rv.data # Set and unset as main image self.app.get(url_for('set_profile_image', id_=file_id, origin_id=actor.id), follow_redirects=True) self.app.get( url_for('file_remove_profile_image', entity_id=actor.id)) # Add to reference rv = self.app.get( url_for('reference_add', id_=reference.id, view='file')) assert b'OpenAtlas logo' in rv.data rv = self.app.post(url_for('reference_add', id_=reference.id, view='file'), data={ 'file': file_id, 'page': '777' }, follow_redirects=True) assert b'777' in rv.data # Update rv = self.app.get(url_for('update', id_=file_id)) assert b'OpenAtlas logo' in rv.data rv = self.app.post(url_for('update', id_=file_id), data={'name': 'Updated file'}, follow_redirects=True) assert b'Changes have been saved' in rv.data \ and b'Updated file' in rv.data rv = self.app.get(url_for('file_add', id_=file_id, view='actor')) assert b'Link actor' in rv.data rv = self.app.post(url_for('file_add', id_=file_id, view='actor'), data={'checkbox_values': [actor.id]}, follow_redirects=True) assert b'File keeper' in rv.data rv = self.app.post(url_for('entity_add_file', id_=node_id), data={'checkbox_values': str([file_id])}, follow_redirects=True) assert b'Updated file' in rv.data # Delete for file in files: rv = self.app.get( url_for('index', view='file', delete_id=file.id)) assert b'The entry has been deleted' in rv.data