def involvement_update(id_, origin_id): link_ = LinkMapper.get_by_id(id_) event = EntityMapper.get_by_id(link_.domain.id) actor = EntityMapper.get_by_id(link_.range.id) origin = event if origin_id == event.id else actor form = build_form(ActorForm, 'Involvement', link_, request) form.save.label.text = _('save') del form.actor, form.event, form.insert_and_continue form.activity.choices = [('P11', g.properties['P11'].name)] if event.class_.code in ['E7', 'E8', 'E12']: form.activity.choices.append(('P14', g.properties['P14'].name)) if event.class_.code == 'E8': form.activity.choices.append(('P22', g.properties['P22'].name)) form.activity.choices.append(('P23', g.properties['P23'].name)) if form.validate_on_submit(): g.cursor.execute('BEGIN') try: link_.delete() link_id = event.link(form.activity.data, actor, form.description.data) DateMapper.save_link_dates(link_id, form) NodeMapper.save_link_nodes(link_id, form) g.cursor.execute('COMMIT') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') tab = 'actor' if origin.view_name == 'event' else 'event' return redirect(url_for(origin.view_name + '_view', id_=origin.id) + '#tab-' + tab) form.activity.data = link_.property.code form.description.data = link_.description link_.set_dates() form.populate_dates(link_) return render_template('involvement/update.html', origin=origin, form=form, linked_object=event if origin_id != event.id else actor)
def relation_update(id_, origin_id): link_ = LinkMapper.get_by_id(id_) domain = EntityMapper.get_by_id(link_.domain.id) range_ = EntityMapper.get_by_id(link_.range.id) origin = range_ if origin_id == range_.id else domain related = range_ if origin_id == domain.id else domain form = build_form(RelationForm, 'Actor Actor Relation', link_, request) del form.actor, form.insert_and_continue, form.origin_id if form.validate_on_submit(): g.cursor.execute('BEGIN') try: link_.delete() if form.inverse.data: link_id = related.link('OA7', origin, form.description.data) else: link_id = origin.link('OA7', related, form.description.data) DateMapper.save_link_dates(link_id, form) NodeMapper.save_link_nodes(link_id, form) g.cursor.execute('COMMIT') flash(_('info update'), 'info') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') return redirect(url_for('actor_view', id_=origin.id) + '#tab-relation') if origin.id == range_.id: form.inverse.data = True form.save.label.text = _('save') link_.set_dates() form.populate_dates(link_) return render_template('relation/update.html', origin=origin, form=form, related=related)
def hierarchy_update(id_: int) -> str: root = g.nodes[id_] if root.system: abort(403) form = build_form(HierarchyForm, 'hierarchy', root) # type: HierarchyForm form.forms.choices = NodeMapper.get_form_choices(root) if root.value_type: del form.multiple elif root.multiple: form.multiple.render_kw = {'disabled': 'disabled'} if form.validate_on_submit(): if form.name.data != root.name and NodeMapper.get_nodes(form.name.data): flash(_('error name exists'), 'error') return redirect(url_for('node_index') + '#tab-' + str(root.id)) save(form, root) flash(_('info update'), 'info') return redirect(url_for('node_index') + '#tab-' + str(root.id)) form.multiple = root.multiple table = Table(['form', 'count'], paging=False) for form_id, form_ in root.forms.items(): url = url_for('hierarchy_remove_form', id_=root.id, remove_id=form_id) link = '<a href="' + url + '">' + uc_first(_('remove')) + '</a>' count = NodeMapper.get_form_count(root, form_id) table.rows.append([form_['name'], format_number(count) if count else link]) return render_template('hierarchy/update.html', node=root, form=form, table=table, forms=[form.id for form in form.forms])
def relation_insert(origin_id): origin = EntityMapper.get_by_id(origin_id) form = build_form(RelationForm, 'Actor Actor Relation') form.origin_id.data = origin.id if form.validate_on_submit(): g.cursor.execute('BEGIN') try: for actor_id in ast.literal_eval(form.actor.data): if form.inverse.data: link_id = LinkMapper.insert(actor_id, 'OA7', origin.id, form.description.data) else: link_id = origin.link('OA7', actor_id, form.description.data) DateMapper.save_link_dates(link_id, form) NodeMapper.save_link_nodes(link_id, form) g.cursor.execute('COMMIT') flash(_('entity created'), 'info') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') if form.continue_.data == 'yes': return redirect(url_for('relation_insert', origin_id=origin_id)) return redirect(url_for('actor_view', id_=origin.id) + '#tab-relation') return render_template('relation/insert.html', origin=origin, form=form)
def member_update(id_, origin_id): link_ = LinkMapper.get_by_id(id_) domain = EntityMapper.get_by_id(link_.domain.id) range_ = EntityMapper.get_by_id(link_.range.id) origin = range_ if origin_id == range_.id else domain related = range_ if origin_id == domain.id else domain form = build_form(MemberForm, 'Member', link_, request) del form.actor, form.group, form.insert_and_continue if form.validate_on_submit(): g.cursor.execute('BEGIN') try: link_.delete() link_id = domain.link('P107', range_, form.description.data) DateMapper.save_link_dates(link_id, form) NodeMapper.save_link_nodes(link_id, form) g.cursor.execute('COMMIT') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') tab = '#tab-member-of' if origin.id == range_.id else '#tab-member' return redirect(url_for('actor_view', id_=origin.id) + tab) form.save.label.text = _('save') link_.set_dates() form.populate_dates(link_) return render_template('member/update.html', origin=origin, form=form, related=related)
def member_update(id_, origin_id): link_ = LinkMapper.get_by_id(id_) domain = EntityMapper.get_by_id(link_.domain.id) range_ = EntityMapper.get_by_id(link_.range.id) origin = range_ if origin_id == range_.id else domain form = build_form(MemberForm, 'Member', link_, request) del form.actor, form.group, form.insert_and_continue if form.validate_on_submit(): g.cursor.execute('BEGIN') try: link_.delete() link_id = domain.link('P107', range_, form.description.data) DateMapper.save_link_dates(link_id, form) NodeMapper.save_link_nodes(link_id, form) g.cursor.execute('COMMIT') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') tab = '#tab-member-of' if origin.id == range_.id else '#tab-member' return redirect(url_for('actor_view', id_=origin.id) + tab) form.save.label.text = _('save') link_.set_dates() form.populate_dates(link_) related = range_ if origin_id == domain.id else domain return render_template('member/update.html', origin=origin, form=form, related=related)
def hierarchy_update(id_): root = g.nodes[id_] if root.system: abort(403) form = build_form(HierarchyForm, 'hierarchy', root) form.forms.choices = NodeMapper.get_form_choices(root) if root.value_type: del form.multiple elif root.multiple: form.multiple.render_kw = {'disabled': 'disabled'} if form.validate_on_submit(): if form.name.data != root.name and NodeMapper.get_nodes(form.name.data): flash(_('error name exists'), 'error') return redirect(url_for('node_index') + '#tab-' + str(root.id)) save(form, root) flash(_('info update'), 'info') return redirect(url_for('node_index') + '#tab-' + str(root.id)) form.multiple = root.multiple table = {'id': 'used_forms', 'show_pager': False, 'data': [], 'sort': 'sortList: [[0, 0]]', 'header': ['form', 'count']} for form_id, form_ in root.forms.items(): url = url_for('hierarchy_remove_form', id_=root.id, remove_id=form_id) link = '<a href="' + url + '">' + uc_first(_('remove')) + '</a>' count = NodeMapper.get_form_count(root, form_id) table['data'].append([form_['name'], format_number(count) if count else link]) return render_template('hierarchy/update.html', node=root, form=form, table=table, forms=[form.id for form in form.forms])
def test_node(self): with app.app_context(): self.login() with app.test_request_context(): app.preprocess_request() actor_node = NodeMapper.get_hierarchy_by_name('Actor Actor Relation') sex_node = NodeMapper.get_hierarchy_by_name('Sex') rv = self.app.get(url_for('node_index')) assert b'Actor Actor Relation' in rv.data rv = self.app.get(url_for('node_insert', root_id=actor_node.id, super_id=actor_node.id)) assert b'Actor Actor Relation' in rv.data rv = self.app.post( url_for('node_insert', root_id=actor_node.id), data={'name_search': 'new'}) assert b'Inverse' in rv.data data = { 'name': 'My secret node', 'name_inverse': 'Do I look inverse?', 'description': 'Very important!'} rv = self.app.post(url_for('node_insert', root_id=actor_node.id), data=data) node_id = rv.location.split('/')[-1].replace('node#tab-', '') rv = self.app.get(url_for('node_update', id_=node_id)) assert b'My secret node' in rv.data and b'Super' in rv.data self.app.post(url_for('node_insert', root_id=sex_node.id), data=data) rv = self.app.post( url_for('node_update', id_=node_id), data=data, follow_redirects=True) assert b'Changes have been saved.' in rv.data # Test insert an continue data['continue_'] = 'yes' rv = self.app.post( url_for('node_insert', root_id=actor_node.id), data=data, follow_redirects=True) assert b'An entry has been created' in rv.data data['continue_'] = '' # Test forbidden system node rv = self.app.post( url_for('node_update', id_=actor_node.id), data=data, follow_redirects=True) assert b'Forbidden' in rv.data # Test update with self as root data[str(actor_node.id)] = node_id rv = self.app.post( url_for('node_update', id_=node_id), data=data, follow_redirects=True) assert b'Type can't have itself as super.' in rv.data # Test update with a child as root rv = self.app.post(url_for('node_insert', root_id=actor_node.id), data=data) child_node_id = rv.location.split('/')[-1].replace('node#tab-', '') data[str(actor_node.id)] = child_node_id rv = self.app.post( url_for('node_update', id_=node_id), data=data, follow_redirects=True) assert b'Type can't have a sub as super.' in rv.data # Test delete system node rv = self.app.get(url_for('node_delete', id_=actor_node.id), follow_redirects=True) assert b'Forbidden' in rv.data rv = self.app.get(url_for('node_delete', id_=child_node_id), follow_redirects=True) assert b'The entry has been deleted.' in rv.data
def hierarchy_remove_form(id_: int, remove_id: int) -> str: root = g.nodes[id_] if NodeMapper.get_form_count(root, remove_id): abort(403) # pragma: no cover try: NodeMapper.remove_form_from_hierarchy(root, remove_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 hierarchy_remove_form(id_, remove_id): root = g.nodes[id_] if NodeMapper.get_form_count(root, remove_id): abort(403) # pragma: no cover try: NodeMapper.remove_form_from_hierarchy(root, remove_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 hierarchy_insert(param): form = build_form(HierarchyForm, 'hierarchy') form.forms.choices = NodeMapper.get_form_choices() if param == 'value': del form.multiple if form.validate_on_submit(): if NodeMapper.get_nodes(form.name.data): flash(_('error name exists'), 'error') return render_template('hierarchy/insert.html', form=form) node = save(form, value_type=True if param == 'value' else False) flash(_('entity created'), 'info') return redirect(url_for('node_index') + '#tab-' + str(node.id)) return render_template('hierarchy/insert.html', form=form, param=param)
def hierarchy_insert(param: str) -> str: form = build_form(HierarchyForm, 'hierarchy') # type: HierarchyForm form.forms.choices = NodeMapper.get_form_choices() if param == 'value': del form.multiple if form.validate_on_submit(): if NodeMapper.get_nodes(form.name.data): flash(_('error name exists'), 'error') return render_template('hierarchy/insert.html', form=form) node = save(form, value_type=True if param == 'value' else False) flash(_('entity created'), 'info') return redirect(url_for('node_index') + '#tab-' + str(node.id)) return render_template('hierarchy/insert.html', form=form, param=param)
def __call__(self, field, **kwargs): from openatlas.models.node import NodeMapper 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) try: hierarchy_id = int(field.id) except ValueError: hierarchy_id = NodeMapper.get_hierarchy_by_name(uc_first(field.id)).id root = g.nodes[hierarchy_id] html = """ <input id="{name}-button" name="{name}-button" type="text" class="table-select {required}" onfocus="this.blur()" readonly="readonly" value="{selection}" placeholder="{change_label}" /> <a id="{name}-clear" {clear_style} class="button" onclick="clearSelect('{name}');">{clear_label}</a> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container"> <input class="tree-filter" id="{name}-tree-search" placeholder="Filter" /> <div id="{name}-tree"></div> </div> </div> <script> $(document).ready(function () {{ createOverlay("{name}","{title}",false,); $("#{name}-tree").jstree({{ "core" : {{"check_callback" : true, 'data':[{tree_data}]}}, "search": {{"case_insensitive": true, "show_only_matches": true}}, "plugins" : ["search"], }}); $("#{name}-tree").on("select_node.jstree", function (e, data) {{ selectFromTree("{name}", data.node.id, data.node.text); }}); $("#{name}-tree-search").keyup(function() {{ $("#{name}-tree").jstree("search", $(this).val()); }}); }}); </script>""".format( name=field.id, title=root.name, change_label=uc_first(_('change')), clear_label=uc_first(_('clear')), selection=selection, tree_data=NodeMapper.get_tree_data(hierarchy_id, selected_ids), clear_style='' if selection else ' style="display: none;" ', required=' required' if field.flags.required else '') return super(TreeSelect, self).__call__(field, **kwargs) + html
def node_move_entities(id_): node = g.nodes[id_] root = g.nodes[node.root[-1]] if root.value_type: # pragma: no cover abort(403) form = build_move_form(MoveForm, node) if form.validate_on_submit(): g.cursor.execute('BEGIN') NodeMapper.move_entities(node, getattr(form, str(root.id)).data, form.selection.data) g.cursor.execute('COMMIT') flash('Entities where updated', 'success') return redirect(url_for('node_index') + '#tab-' + str(root.id)) getattr(form, str(root.id)).data = node.id return render_template('types/move.html', node=node, root=root, form=form)
def test_hierarchy(self): with app.app_context(): self.login() rv = self.app.get(url_for('hierarchy_insert', param='custom')) assert b'+ Custom' in rv.data data = {'name': 'Geronimo', 'multiple': True, 'description': 'Very important!'} rv = self.app.post(url_for('hierarchy_insert', param='custom'), data=data) hierarchy_id = rv.location.split('/')[-1].replace('types#tab-', '') rv = self.app.get(url_for('hierarchy_update', id_=hierarchy_id)) assert b'Geronimo' in rv.data rv = self.app.post( url_for('hierarchy_update', id_=hierarchy_id), data=data, follow_redirects=True) assert b'Changes have been saved.' in rv.data data['name'] = 'Actor Actor Relation' rv = self.app.post( url_for('hierarchy_update', id_=hierarchy_id), data=data, follow_redirects=True) assert b'The name is already in use' in rv.data rv = self.app.post(url_for('hierarchy_delete', id_=hierarchy_id), follow_redirects=True) assert b'deleted' in rv.data # Test checks actor_node = NodeMapper.get_hierarchy_by_name('Actor Actor Relation') rv = self.app.get(url_for('hierarchy_update', id_=actor_node.id), follow_redirects=True) assert b'Forbidden' in rv.data rv = self.app.get(url_for('hierarchy_delete', id_=actor_node.id), follow_redirects=True) assert b'Forbidden' in rv.data rv = self.app.post(url_for('hierarchy_insert', param='custom'), data=data) assert b'The name is already in use' in rv.data
def before_request(): debug_model['div sql'] = 0 from openatlas.models.classObject import ClassMapper from openatlas.models.node import NodeMapper from openatlas.models.property import PropertyMapper from openatlas.models.settings import SettingsMapper if request.path.startswith('/static'): # pragma: no cover return # Only needed if not running with apache and static alias debug_model['current'] = time.time() g.db = connect() g.cursor = g.db.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor) g.classes = ClassMapper.get_all() g.properties = PropertyMapper.get_all() g.nodes = NodeMapper.get_all_nodes() session['settings'] = SettingsMapper.get_settings() session['language'] = get_locale() # Set max file upload in MB app.config['MAX_CONTENT_LENGTH'] = session['settings']['file_upload_max_size'] * 1024 * 1024 debug_model['by codes'] = 0 debug_model['by id'] = 0 debug_model['link sql'] = 0 debug_model['user'] = 0 debug_model['model'] = time.time() - debug_model['current'] debug_model['current'] = time.time() # Workaround overlay maps for Thanados until #978 is implemented session['settings']['overlay_hack'] = False if session['settings']['site_name'] == 'Thanados': session['settings']['overlay_hack'] = True # pragma: no cover
def check_single_type_duplicates() -> list: # Find entities with multiple types attached which should be single from openatlas.models.node import NodeMapper from openatlas.models.entity import EntityMapper data = [] for id_, node in g.nodes.items(): if not node.root and not node.multiple and not node.value_type: node_ids = NodeMapper.get_all_sub_ids(node) if node_ids: sql = """ SELECT domain_id FROM model.link WHERE property_code = 'P2' AND range_id IN %(node_ids)s GROUP BY domain_id HAVING COUNT(*) > 1;""" g.cursor.execute(sql, {'node_ids': tuple(node_ids)}) debug_model['link sql'] += 1 for row in g.cursor.fetchall(): offending_nodes = [] entity = EntityMapper.get_by_id(row.domain_id, nodes=True) for entity_node in entity.nodes: if g.nodes[entity_node.root[-1]].id == node.id: url = url_for('admin_delete_single_type_duplicate', entity_id=entity.id, node_id=entity_node.id) offending_nodes.append( '<a href="' + url + '">' + uc_first(_('remove')) + '</a> ' + entity_node.name) data.append([link(entity), entity.class_.name, link(g.nodes[id_]), '<br />'.join(offending_nodes)]) return data
def check_single_type_duplicates() -> list: # Find entities with multiple types attached which should be single from openatlas.models.node import NodeMapper from openatlas.models.entity import EntityMapper data = [] for id_, node in g.nodes.items(): if not node.root and not node.multiple and not node.value_type: node_ids = NodeMapper.get_all_sub_ids(node) if node_ids: sql = """ SELECT domain_id FROM model.link WHERE property_code = 'P2' AND range_id IN %(node_ids)s GROUP BY domain_id HAVING COUNT(*) > 1;""" g.execute(sql, {'node_ids': tuple(node_ids)}) for row in g.cursor.fetchall(): offending_nodes = [] entity = EntityMapper.get_by_id(row.domain_id, nodes=True) for entity_node in entity.nodes: if g.nodes[entity_node.root[-1]].id == node.id: url = url_for( 'admin_delete_single_type_duplicate', entity_id=entity.id, node_id=entity_node.id) offending_nodes.append('<a href="' + url + '">' + uc_first(_('remove')) + '</a> ' + entity_node.name) data.append([ link(entity), entity.class_.name, link(g.nodes[id_]), '<br />'.join(offending_nodes) ]) return data
def before_request(): from openatlas.models.classObject import ClassMapper from openatlas.models.node import NodeMapper from openatlas.models.property import PropertyMapper from openatlas.models.settings import SettingsMapper if request.path.startswith('/static'): # pragma: no cover return # only needed if not running with apache and static alias debug_model['current'] = time.time() g.db = connect() g.cursor = g.db.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor) g.classes = ClassMapper.get_all() g.properties = PropertyMapper.get_all() g.nodes = NodeMapper.get_all_nodes() session['settings'] = SettingsMapper.get_settings() session['language'] = get_locale() # Set max file upload in MB app.config['MAX_CONTENT_LENGTH'] = session['settings'][ 'file_upload_max_size'] * 1024 * 1024 debug_model['by codes'] = 0 debug_model['by id'] = 0 debug_model['by ids'] = 0 debug_model['linked'] = 0 debug_model['user'] = 0 debug_model['div sql'] = 0 debug_model['model'] = time.time() - debug_model['current'] debug_model['current'] = time.time()
def node_move_entities(id_: int) -> str: node = g.nodes[id_] root = g.nodes[node.root[-1]] if root.value_type: # pragma: no cover abort(403) form = build_move_form(MoveForm, node) if form.validate_on_submit(): g.cursor.execute('BEGIN') NodeMapper.move_entities(node, getattr(form, str(root.id)).data, form.checkbox_values.data) g.cursor.execute('COMMIT') flash('Entities where updated', 'success') return redirect(url_for('node_index') + '#tab-' + str(root.id)) form.save.label.text = uc_first(_('move')) getattr(form, str(root.id)).data = node.id return render_template('types/move.html', node=node, root=root, form=form)
def build_form(form, form_name, entity=None, request_origin=None, entity2=None): """ The entity parameter can also be a link.""" # Add custom fields custom_list = [] def add_value_type_fields(subs) -> None: for sub_id in subs: sub = g.nodes[sub_id] setattr(form, str(sub.id), ValueFloatField(sub.name, [Optional()])) add_value_type_fields(sub.subs) for id_, node in NodeMapper.get_nodes_for_form(form_name).items(): custom_list.append(id_) setattr( form, str(id_), TreeMultiField(str(id_)) if node.multiple else TreeField(str(id_))) if node.value_type: add_value_type_fields(node.subs) form_instance = form(obj=entity) # 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) in (TreeField, TreeMultiField) and int( field.id) not in custom_list: delete_list.append(field.id) for item in delete_list: delattr(form_instance, item) # Set field data if available and only if it's a GET request if entity and request_origin and request_origin.method == 'GET': # Important to use isinstance instead type check, because can be a sub type (e.g. ActorForm) if isinstance(form_instance, DateForm): form_instance.populate_dates(entity) nodes = entity.nodes if entity2: nodes.update(entity2.nodes) if hasattr(form, 'opened'): form_instance.opened.data = time.time() node_data = {} # type: dict for node, node_value in nodes.items(): root = g.nodes[node.root[-1]] if node.root else node if root.id not in node_data: node_data[root.id] = [] node_data[root.id].append(node.id) if root.value_type: getattr(form_instance, str(node.id)).data = node_value for root_id, nodes in node_data.items(): if hasattr(form_instance, str(root_id)): getattr(form_instance, str(root_id)).data = nodes return form_instance
def save(form, node=None, value_type=False): g.cursor.execute('BEGIN') try: if not node: node = NodeMapper.insert('E55', sanitize(form.name.data, 'node')) NodeMapper.insert_hierarchy(node, form, value_type) else: node = g.nodes[node.id] NodeMapper.update_hierarchy(node, form) node.name = sanitize(form.name.data, 'node') node.description = form.description.data node.update() g.cursor.execute('COMMIT') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') return node
def save(form, node=None, value_type: Optional[bool] = False): g.cursor.execute('BEGIN') try: if not node: node = NodeMapper.insert('E55', sanitize(form.name.data, 'node')) NodeMapper.insert_hierarchy(node, form, value_type) else: node = g.nodes[node.id] NodeMapper.update_hierarchy(node, form) node.name = sanitize(form.name.data, 'node') node.description = form.description.data node.update() g.cursor.execute('COMMIT') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') return node
def build_form(form: Any, form_name: str, selected_object: Union[Entity, Link, None] = None, request_origin: Optional_Type[Request] = None, entity2: Optional_Type[Entity] = None) -> Any: def add_value_type_fields(subs: List[int]) -> None: for sub_id in subs: sub = g.nodes[sub_id] setattr(form, str(sub.id), ValueFloatField(sub.name, [Optional()])) add_value_type_fields(sub.subs) # Add custom fields custom_list = [] for id_, node in NodeMapper.get_nodes_for_form(form_name).items(): custom_list.append(id_) setattr( form, str(id_), TreeMultiField(str(id_)) if node.multiple else TreeField(str(id_))) if node.value_type: add_value_type_fields(node.subs) form_instance = form(obj=selected_object) # 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) in (TreeField, TreeMultiField) and int( field.id) not in custom_list: delete_list.append(field.id) for item in delete_list: delattr(form_instance, item) # Set field data if available and only if it's a GET request if selected_object and request_origin and request_origin.method == 'GET': from openatlas.forms.date import DateForm # Important to use isinstance instead type check, because can be a sub type (e.g. ActorForm) if isinstance(form_instance, DateForm): form_instance.populate_dates(selected_object) nodes = selected_object.nodes if isinstance(entity2, Entity): nodes.update(entity2.nodes) # type: ignore if hasattr(form, 'opened'): form_instance.opened.data = time.time() node_data: Dict[int, List[int]] = {} for node, node_value in nodes.items(): # type: ignore root = g.nodes[node.root[-1]] if node.root else node if root.id not in node_data: node_data[root.id] = [] node_data[root.id].append(node.id) if root.value_type: getattr(form_instance, str(node.id)).data = node_value for root_id, nodes_ in node_data.items(): if hasattr(form_instance, str(root_id)): getattr(form_instance, str(root_id)).data = nodes_ return form_instance
def involvement_insert(origin_id): origin = EntityMapper.get_by_id(origin_id) view_name = get_view_name(origin) form = build_form(ActorForm, 'Involvement') if view_name == 'event': del form.event else: del form.actor form.activity.choices = [('P11', g.properties['P11'].name_inverse)] if origin.class_.code in ['E7', 'E8', 'E12']: form.activity.choices.append(('P14', g.properties['P14'].name_inverse)) if origin.class_.code == 'E8': form.activity.choices.append(('P22', g.properties['P22'].name_inverse)) form.activity.choices.append(('P23', g.properties['P23'].name_inverse)) if form.validate_on_submit(): g.cursor.execute('BEGIN') try: if view_name == 'event': for actor_id in ast.literal_eval(form.actor.data): link_id = origin.link(form.activity.data, actor_id, form.description.data) DateMapper.save_link_dates(link_id, form) NodeMapper.save_link_nodes(link_id, form) else: for event_id in ast.literal_eval(form.event.data): link_id = LinkMapper.insert(event_id, form.activity.data, origin.id, form.description.data) DateMapper.save_link_dates(link_id, form) NodeMapper.save_link_nodes(link_id, form) g.cursor.execute('COMMIT') flash(_('entity created'), 'info') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') if form.continue_.data == 'yes': return redirect(url_for('involvement_insert', origin_id=origin_id)) tab = 'actor' if view_name == 'event' else 'event' return redirect( url_for(view_name + '_view', id_=origin.id) + '#tab-' + tab) return render_template('involvement/insert.html', origin=origin, form=form)
def __call__(self, field: TreeField, **kwargs: Any) -> TreeSelect: from openatlas.models.node import NodeMapper selection = '' selected_ids = [] if field.data: field.data = field.data[0] if type( field.data) is list else field.data selection = g.nodes[int(field.data)].name selected_ids.append(g.nodes[int(field.data)].id) html = """ <input id="{name}-button" name="{name}-button" type="text" class="table-select {required}" onfocus="this.blur()" readonly="readonly" value="{selection}" placeholder="{change_label}"> <a id="{name}-clear" {clear_style} class="button" onclick="clearSelect('{name}');">{clear_label}</a> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container"> <input class="tree-filter" id="{name}-tree-search" placeholder="{filter}"> <div id="{name}-tree"></div> </div> </div> <script> $(document).ready(function () {{ createOverlay("{name}","{title}",false,); $("#{name}-tree").jstree({{ "core" : {{"check_callback" : true, 'data':[{tree_data}]}}, "search": {{"case_insensitive": true, "show_only_matches": true}}, "plugins" : ["search"], }}); $("#{name}-tree").on("select_node.jstree", function (e, data) {{ selectFromTree("{name}", data.node.id, data.node.text); }}); $("#{name}-tree-search").keyup(function() {{ if (this.value.length >= {min_chars}) {{ $("#{name}-tree").jstree("search", $(this).val()); }} else if (this.value.length == 0) {{ $("#{name}-tree").jstree("search", $(this).val()); $("#{name}-tree").jstree(true).show_all(); }} }}); }}); </script>""".format( filter=uc_first(_('type to search')), min_chars=session['settings']['minimum_jstree_search'], name=field.id, title=g.nodes[int(field.id)].name, change_label=uc_first(_('change')), clear_label=uc_first(_('clear')), selection=selection, tree_data=NodeMapper.get_tree_data(int(field.id), selected_ids), clear_style='' if selection else ' style="display: none;" ', required=' required' if field.flags.required else '') return super(TreeSelect, self).__call__(field, **kwargs) + html
def test_hierarchy(self): with app.app_context(): self.login() # Custom types data = { 'name': 'Geronimo', 'forms': [1, 2, 4, 5, 6], 'multiple': True, 'description': 'Very important!'} rv = self.app.post(url_for('hierarchy_insert', param='custom'), data=data) hierarchy_id = rv.location.split('/')[-1].replace('types#tab-', '') rv = self.app.get(url_for('hierarchy_update', id_=hierarchy_id)) assert b'Geronimo' in rv.data data['forms'] = [3] rv = self.app.post( url_for('hierarchy_update', id_=hierarchy_id), data=data, follow_redirects=True) assert b'Changes have been saved.' in rv.data rv = self.app.get(url_for('hierarchy_insert', param='custom')) assert b'+ Custom' in rv.data data = {'name': 'My secret node', 'description': 'Very important!'} rv = self.app.post(url_for('node_insert', root_id=hierarchy_id), data=data) node_id = rv.location.split('/')[-1].replace('types#tab-', '') rv = self.app.get(url_for('hierarchy_remove_form', id_=hierarchy_id, remove_id=2), follow_redirects=True) assert b'Changes have been saved.' in rv.data self.app.get(url_for('node_delete', id_=node_id)) data['name'] = 'Actor Actor Relation' rv = self.app.post( url_for('hierarchy_update', id_=hierarchy_id), data=data, follow_redirects=True) assert b'The name is already in use' in rv.data rv = self.app.post(url_for('hierarchy_delete', id_=hierarchy_id), follow_redirects=True) assert b'deleted' in rv.data # Value types rv = self.app.get(url_for('hierarchy_insert', param='value')) assert b'+ Value' in rv.data data2 = {'name': 'A valued value type', 'forms': [1], 'description': ''} rv = self.app.post(url_for('hierarchy_insert', param='value'), data=data2) custom_hierarchy_id = rv.location.split('/')[-1].replace('types#tab-', '') rv = self.app.get(url_for('hierarchy_update', id_=custom_hierarchy_id)) assert b'valued' in rv.data # Test checks actor_node = NodeMapper.get_hierarchy_by_name('Actor Actor Relation') rv = self.app.get(url_for('hierarchy_update', id_=actor_node.id), follow_redirects=True) assert b'Forbidden' in rv.data rv = self.app.get(url_for('hierarchy_delete', id_=actor_node.id), follow_redirects=True) assert b'Forbidden' in rv.data rv = self.app.post(url_for('hierarchy_insert', param='custom'), data=data) assert b'The name is already in use' in rv.data
def test_involvement(self): with app.app_context(): self.login() with app.test_request_context(): app.preprocess_request() actor_id = EntityMapper.insert('E21', 'Captain Miller').id event_id = EntityMapper.insert('E8', 'Event Horizon').id involvement_id = NodeMapper.get_hierarchy_by_name( 'Involvement').id # add involvement rv = self.app.get(url_for('involvement_insert', origin_id=actor_id)) assert b'Involvement' in rv.data data = { 'event': '[' + str(event_id) + ']', 'activity': 'P11', involvement_id: involvement_id } rv = self.app.post(url_for('involvement_insert', origin_id=actor_id), data=data, follow_redirects=True) assert b'Event Horizon' in rv.data data = { 'actor': '[' + str(actor_id) + ']', 'continue_': 'yes', 'activity': 'P22' } rv = self.app.post(url_for('involvement_insert', origin_id=event_id), data=data, follow_redirects=True) assert b'Event Horizon' in rv.data self.app.get(url_for('event_view', id_=event_id)) # update involvement with app.test_request_context(): app.preprocess_request() link_id = LinkMapper.get_links(event_id, 'P22')[0].id rv = self.app.get( url_for('involvement_update', id_=link_id, origin_id=event_id)) assert b'Captain' in rv.data rv = self.app.post(url_for('involvement_update', id_=link_id, origin_id=actor_id), data={ 'description': 'Infinite Space - Infinite Terror', 'activity': 'P23' }, follow_redirects=True) assert b'Infinite Space - Infinite Terror' in rv.data self.app.get(url_for('event_view', id_=event_id))
def test_node(self): with app.app_context(): self.login() with app.test_request_context(): app.preprocess_request() actor_node = NodeMapper.get_hierarchy_by_name('Actor Actor Relation') sex_node = NodeMapper.get_hierarchy_by_name('Sex') rv = self.app.get(url_for('node_index')) assert b'Actor Actor Relation' in rv.data rv = self.app.post( url_for('node_insert', root_id=actor_node.id), data={'name_search': 'new'}) assert b'Inverse' in rv.data data = { 'name': 'My secret node', 'name_inverse': 'Do I look inverse?', 'description': 'Very important!'} rv = self.app.post(url_for('node_insert', root_id=actor_node.id), data=data) node_id = rv.location.split('/')[-1].replace('node#tab-', '') rv = self.app.get(url_for('node_update', id_=node_id)) assert b'My secret node' in rv.data and b'Super' in rv.data self.app.post(url_for('node_insert', root_id=sex_node.id), data=data) rv = self.app.post( url_for('node_update', id_=node_id), data=data, follow_redirects=True) assert b'Changes have been saved.' in rv.data # Test forbidden system node rv = self.app.post( url_for('node_update', id_=actor_node.id), data=data, follow_redirects=True) assert b'Forbidden' in rv.data # Test update with self as root data[str(actor_node.id)] = node_id rv = self.app.post( url_for('node_update', id_=node_id), data=data, follow_redirects=True) assert b'super' in rv.data # Test delete system node rv = self.app.get(url_for('node_delete', id_=actor_node.id), follow_redirects=True) assert b'Forbidden' in rv.data rv = self.app.get(url_for('node_delete', id_=node_id), follow_redirects=True) assert b'The entry has been deleted.' in rv.data
def involvement_update(id_, origin_id): link_ = LinkMapper.get_by_id(id_) event = EntityMapper.get_by_id(link_.domain.id) actor = EntityMapper.get_by_id(link_.range.id) origin = event if origin_id == event.id else actor form = build_form(ActorForm, 'Involvement', link_, request) form.save.label.text = _('save') del form.actor, form.event, form.insert_and_continue form.activity.choices = [('P11', g.properties['P11'].name)] if event.class_.code in ['E7', 'E8', 'E12']: form.activity.choices.append(('P14', g.properties['P14'].name)) if event.class_.code == 'E8': form.activity.choices.append(('P22', g.properties['P22'].name)) form.activity.choices.append(('P23', g.properties['P23'].name)) if form.validate_on_submit(): g.cursor.execute('BEGIN') try: link_.delete() link_id = event.link(form.activity.data, actor, form.description.data) DateMapper.save_link_dates(link_id, form) NodeMapper.save_link_nodes(link_id, form) g.cursor.execute('COMMIT') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') view_name = get_view_name(origin) tab = 'actor' if view_name == 'event' else 'event' return redirect( url_for(view_name + '_view', id_=origin.id) + '#tab-' + tab) form.activity.data = link_.property.code form.description.data = link_.description link_.set_dates() form.populate_dates(link_) return render_template( 'involvement/update.html', origin=origin, form=form, linked_object=event if origin_id != event.id else actor)
def member_insert(origin_id): origin = EntityMapper.get_by_id(origin_id) form = build_form(MemberForm, 'Member') del form.group form.origin_id.data = origin.id if form.validate_on_submit(): g.cursor.execute('BEGIN') try: for actor in EntityMapper.get_by_ids(ast.literal_eval(form.actor.data)): link_id = origin.link('P107', actor, form.description.data) DateMapper.save_link_dates(link_id, form) NodeMapper.save_link_nodes(link_id, form) g.cursor.execute('COMMIT') flash(_('entity created'), 'info') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') if form.continue_.data == 'yes': return redirect(url_for('member_insert', origin_id=origin_id)) return redirect(url_for('actor_view', id_=origin.id) + '#tab-member') return render_template('member/insert.html', origin=origin, form=form)
def hierarchy_update(id_): node = g.nodes[id_] if node.system: abort(403) form = build_form(HierarchyForm, 'hierarchy', node) form.forms.choices = NodeMapper.get_form_choices() if node.value_type: del form.multiple elif node.multiple: form.multiple.render_kw = {'disabled': 'disabled'} if form.validate_on_submit(): if form.name.data != node.name and NodeMapper.get_nodes(form.name.data): flash(_('error name exists'), 'error') return redirect(url_for('node_index') + '#tab-' + str(node.id)) save(form, node) flash(_('info update'), 'info') return redirect(url_for('node_index') + '#tab-' + str(node.id)) form.multiple = node.multiple return render_template( 'hierarchy/update.html', node=node, form=form, forms=[form.id for form in form.forms])
def involvement_insert(origin_id): origin = EntityMapper.get_by_id(origin_id) form = build_form(ActorForm, 'Involvement') if origin.view_name == 'event': del form.event else: del form.actor form.activity.choices = [('P11', g.properties['P11'].name_inverse)] if origin.class_.code in ['E7', 'E8', 'E12']: form.activity.choices.append(('P14', g.properties['P14'].name_inverse)) if origin.class_.code == 'E8': form.activity.choices.append(('P22', g.properties['P22'].name_inverse)) form.activity.choices.append(('P23', g.properties['P23'].name_inverse)) if form.validate_on_submit(): g.cursor.execute('BEGIN') try: if origin.view_name == 'event': for actor in EntityMapper.get_by_ids(ast.literal_eval(form.actor.data)): link_id = origin.link(form.activity.data, actor, form.description.data) DateMapper.save_link_dates(link_id, form) NodeMapper.save_link_nodes(link_id, form) else: for event in EntityMapper.get_by_ids(ast.literal_eval(form.event.data)): link_id = event.link(form.activity.data, origin, form.description.data) DateMapper.save_link_dates(link_id, form) NodeMapper.save_link_nodes(link_id, form) g.cursor.execute('COMMIT') flash(_('entity created'), 'info') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') if form.continue_.data == 'yes': return redirect(url_for('involvement_insert', origin_id=origin_id)) tab = 'actor' if origin.view_name == 'event' else 'event' return redirect(url_for(origin.view_name + '_view', id_=origin.id) + '#tab-' + tab) return render_template('involvement/insert.html', origin=origin, form=form)
def save(form, node=None, root=None): g.cursor.execute('BEGIN') try: if node: log_action = 'update' root = g.nodes[node.root[-1]] if node.root else None super_ = g.nodes[node.root[0]] if node.root else None else: log_action = 'insert' node = NodeMapper.insert(root.class_.code, form.name.data) super_ = 'new' new_super_id = getattr(form, str(root.id)).data new_super = g.nodes[int(new_super_id)] if new_super_id else g.nodes[ root.id] if new_super.id == node.id: flash(_('error node self as super'), 'error') return if new_super.root and node.id in new_super.root: flash(_('error node sub as super'), 'error') return node.name = form.name.data if root.directional and form.name_inverse.data.strip(): node.name += ' (' + form.name_inverse.data.strip() + ')' if not root.directional: node.name = node.name.replace('(', '').replace(')', '') node.description = form.description.data if form.description else form.unit.data node.update() # Update super if changed and node is not a root node if super_ and (super_ == 'new' or super_.id != new_super_id): property_code = 'P127' if node.class_.code == 'E55' else 'P89' node.delete_links(property_code) node.link(property_code, new_super) g.cursor.execute('COMMIT') url = url_for('node_view', id_=node.id) if form.continue_.data == 'yes': url = url_for('node_insert', root_id=root.id, super_id=new_super_id if new_super_id else None) logger.log_user(node.id, log_action) flash( _('entity created') if log_action == 'insert' else _('info update'), 'info') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') url = url_for('node_index') return url
def admin_orphans() -> str: header = ['name', 'class', 'type', 'system type', 'created', 'updated', 'description'] tables = {'orphans': Table(header), 'unlinked': Table(header), 'missing_files': Table(header), 'circular': Table(['entity']), 'nodes': Table(['name', 'root']), 'orphaned_files': Table(['name', 'size', 'date', 'ext'])} tables['circular'].rows = [[link(entity)] for entity in EntityMapper.get_circular()] for entity in EntityMapper.get_orphans(): name = 'unlinked' if entity.class_.code in app.config['CODE_CLASS'].keys() else 'orphans' tables[name].rows.append([link(entity), link(entity.class_), entity.print_base_type(), entity.system_type, format_date(entity.created), format_date(entity.modified), truncate_string(entity.description)]) for node in NodeMapper.get_orphans(): tables['nodes'].rows.append([link(node), link(g.nodes[node.root[-1]])]) # Get orphaned file entities (no corresponding file) file_ids = [] for entity in EntityMapper.get_by_system_type('file', nodes=True): file_ids.append(str(entity.id)) if not get_file_path(entity): tables['missing_files'].rows.append([link(entity), link(entity.class_), entity.print_base_type(), entity.system_type, format_date(entity.created), format_date(entity.modified), truncate_string(entity.description)]) # Get orphaned files (no corresponding entity) for file in os.scandir(app.config['UPLOAD_FOLDER_PATH']): name = file.name if name != '.gitignore' and splitext(file.name)[0] not in file_ids: confirm = ' onclick="return confirm(\'' + _('Delete %(name)s?', name=name) + '\')"' tables['orphaned_files'].rows.append([ name, convert_size(file.stat().st_size), format_date(datetime.datetime.utcfromtimestamp(file.stat().st_ctime)), splitext(name)[1], '<a href="' + url_for('download_file', filename=name) + '">' + uc_first( _('download')) + '</a>', '<a href="' + url_for('admin_file_delete', filename=name) + '" ' + confirm + '>' + uc_first(_('delete')) + '</a>']) return render_template('admin/orphans.html', tables=tables)
def build_form(form, form_name, entity=None, request_origin=None, entity2=None) -> Form: """ The entity parameter can also be a link.""" # Add custom fields custom_list = [] def add_value_type_fields(subs) -> None: for sub_id in subs: sub = g.nodes[sub_id] setattr(form, str(sub.id), ValueFloatField(sub.name, [Optional()])) add_value_type_fields(sub.subs) for id_, node in NodeMapper.get_nodes_for_form(form_name).items(): custom_list.append(id_) setattr(form, str(id_), TreeMultiField(str(id_)) if node.multiple else TreeField(str(id_))) if node.value_type: add_value_type_fields(node.subs) form_instance = form(obj=entity) # 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) in (TreeField, TreeMultiField) and int(field.id) not in custom_list: delete_list.append(field.id) for item in delete_list: delattr(form_instance, item) # Set field data if available and only if it's a GET request if entity and request_origin and request_origin.method == 'GET': # Important to use isinstance instead type check, because can be a sub type (e.g. ActorForm) if isinstance(form_instance, DateForm): form_instance.populate_dates(entity) nodes = entity.nodes if entity2: nodes.update(entity2.nodes) if hasattr(form, 'opened'): form_instance.opened.data = time.time() node_data = {} # type: dict for node, node_value in nodes.items(): root = g.nodes[node.root[-1]] if node.root else node if root.id not in node_data: node_data[root.id] = [] node_data[root.id].append(node.id) if root.value_type: getattr(form_instance, str(node.id)).data = node_value for root_id, nodes in node_data.items(): if hasattr(form_instance, str(root_id)): getattr(form_instance, str(root_id)).data = nodes return form_instance
def __call__(self, field, **kwargs): from openatlas.models.node import NodeMapper selection = '' selected_ids = [] if field.data: field.data = field.data[0] if type(field.data) is list else field.data selection = g.nodes[int(field.data)].name selected_ids.append(g.nodes[int(field.data)].id) html = """ <input id="{name}-button" name="{name}-button" type="text" class="table-select {required}" onfocus="this.blur()" readonly="readonly" value="{selection}" placeholder="{change_label}" /> <a id="{name}-clear" {clear_style} class="button" onclick="clearSelect('{name}');">{clear_label}</a> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container"> <input class="tree-filter" id="{name}-tree-search" placeholder="{filter}" /> <div id="{name}-tree"></div> </div> </div> <script> $(document).ready(function () {{ createOverlay("{name}","{title}",false,); $("#{name}-tree").jstree({{ "core" : {{"check_callback" : true, 'data':[{tree_data}]}}, "search": {{"case_insensitive": true, "show_only_matches": true}}, "plugins" : ["search"], }}); $("#{name}-tree").on("select_node.jstree", function (e, data) {{ selectFromTree("{name}", data.node.id, data.node.text); }}); $("#{name}-tree-search").keyup(function() {{ if (this.value.length >= {min_chars}) {{ $("#{name}-tree").jstree("search", $(this).val()); }} }}); }}); </script>""".format( filter=uc_first(_('type to search')), min_chars=session['settings']['minimum_jstree_search'], name=field.id, title=g.nodes[int(field.id)].name, change_label=uc_first(_('change')), clear_label=uc_first(_('clear')), selection=selection, tree_data=NodeMapper.get_tree_data(int(field.id), selected_ids), clear_style='' if selection else ' style="display: none;" ', required=' required' if field.flags.required else '') return super(TreeSelect, self).__call__(field, **kwargs) + html
def __call__(self, field: TreeField, **kwargs: Any) -> TreeMultiSelect: selection = '' selected_ids = [] root = g.nodes[int(field.id)] if field.data: # Somehow field.data can be a string after a failed form validation, so fix that below field.data = ast.literal_eval( field.data) if type(field.data) is str else field.data for entity_id in field.data: selected_ids.append(entity_id) selection += g.nodes[entity_id].name + '<br>' html = """ <span id="{name}-button" class="button">{change_label}</span> <div id="{name}-selection" style="text-align:left;">{selection}</div> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container"> <input class="tree-filter" id="{name}-tree-search" placeholder="{filter}"> <div id="{name}-tree"></div> </div> </div> <script> createOverlay("{name}", "{title}", true, "tree"); $("#{name}-tree").jstree({{ "core" : {{ "check_callback" : true, 'data':[{tree_data}] }}, "search": {{"case_insensitive": true, "show_only_matches": true}}, "plugins": ["search", "checkbox"], "checkbox": {{"three_state": false}} }}); $("#{name}-tree-search").keyup(function(){{ if (this.value.length >= {min_chars}) {{ $("#{name}-tree").jstree("search", $(this).val()); }} else if (this.value.length == 0) {{ $("#{name}-tree").jstree("search", $(this).val()); $("#{name}-tree").jstree(true).show_all(); }} }}); </script>""".format( filter=uc_first(_('type to search')), min_chars=session['settings']['minimum_jstree_search'], name=field.id, title=root.name, selection=selection, change_label=uc_first(_('change')), tree_data=NodeMapper.get_tree_data(int(field.id), selected_ids)) return super(TreeMultiSelect, self).__call__(field, **kwargs) + html
def save(form, node=None, root=None): g.cursor.execute('BEGIN') try: if node: log_action = 'update' root = g.nodes[node.root[-1]] if node.root else None super_ = g.nodes[node.root[0]] if node.root else None else: log_action = 'insert' node = NodeMapper.insert(root.class_.code, form.name.data) super_ = 'new' new_super_id = getattr(form, str(root.id)).data new_super = g.nodes[int(new_super_id)] if new_super_id else g.nodes[root.id] if new_super.id == node.id: flash(_('error node self as super'), 'error') return if new_super.root and node.id in new_super.root: flash(_('error node sub as super'), 'error') return node.name = form.name.data if root.directional and form.name_inverse.data.strip(): node.name += ' (' + form.name_inverse.data.strip() + ')' if not root.directional: node.name = node.name.replace('(', '').replace(')', '') node.description = form.description.data node.update() # Update super if changed and node is not a root node if super_ and (super_ == 'new' or super_.id != new_super_id): property_code = 'P127' if node.class_.code == 'E55' else 'P89' node.delete_links(property_code) node.link(property_code, new_super) g.cursor.execute('COMMIT') url = url_for('node_view', id_=node.id) if form.continue_.data == 'yes': url = url_for('node_insert', root_id=root.id, super_id=new_super_id if new_super_id else None) logger.log_user(node.id, log_action) flash(_('entity created') if log_action == 'insert' else _('info update'), 'info') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') url = url_for('node_index') return url
def print_base_type(self): from openatlas.models.node import NodeMapper if not self.view_name or self.view_name == 'actor': # actors have no base type return '' root_name = self.view_name.title() if self.view_name == 'reference': root_name = self.system_type.title() elif self.view_name == 'file': root_name = 'License' elif self.view_name == 'place': root_name = uc_first(self.system_type) if self.system_type == 'stratigraphic unit': root_name = 'Stratigraphic Unit' root_id = NodeMapper.get_hierarchy_by_name(root_name).id for node in self.nodes: if node.root and node.root[-1] == root_id: return node.name return ''
def delete_orphans(parameter: str) -> int: from openatlas.models.node import NodeMapper class_codes = tuple(app.config['CODE_CLASS'].keys()) if parameter == 'orphans': class_codes = class_codes + ('E55', ) sql_where = EntityMapper.sql_orphan + " AND e.class_code NOT IN %(class_codes)s" elif parameter == 'unlinked': sql_where = EntityMapper.sql_orphan + " AND e.class_code IN %(class_codes)s" elif parameter == 'types': count = 0 for node in NodeMapper.get_node_orphans(): node.delete() count += 1 return count else: return 0 sql = 'DELETE FROM model.entity WHERE id IN (' + sql_where + ');' g.execute(sql, {'class_codes': class_codes}) return g.cursor.rowcount
def print_base_type(self): from openatlas.models.node import NodeMapper view_name = get_view_name(self) if not view_name or view_name == 'actor': # actors have no base type return '' root_name = view_name.title() if view_name == 'reference': root_name = self.system_type.title() elif view_name == 'file': root_name = 'License' elif view_name == 'place': root_name = uc_first(self.system_type) if self.system_type == 'stratigraphic unit': root_name = 'Stratigraphic Unit' root_id = NodeMapper.get_hierarchy_by_name(root_name).id for node in self.nodes: if node.root and node.root[-1] == root_id: return node.name return ''
def test_involvement(self): with app.app_context(): self.login() rv = self.app.post(url_for('event_insert', code='E8'), data={ 'name': 'Event Horizon', 'date_begin_year': '1949', 'date_begin_month': '10', 'date_begin_day': '8', 'date_end_year': '1951'}) event_id = int(rv.location.split('/')[-1]) with app.test_request_context(): app.preprocess_request() actor_id = EntityMapper.insert('E21', 'Captain Miller').id involvement_id = NodeMapper.get_hierarchy_by_name('Involvement').id # Add involvement rv = self.app.get(url_for('involvement_insert', origin_id=actor_id)) assert b'Involvement' in rv.data data = {'event': '[' + str(event_id) + ']', 'activity': 'P11', involvement_id: involvement_id} rv = self.app.post(url_for('involvement_insert', origin_id=actor_id), data=data, follow_redirects=True) assert b'Event Horizon' in rv.data data = {'actor': '[' + str(actor_id) + ']', 'continue_': 'yes', 'activity': 'P22'} rv = self.app.post(url_for('involvement_insert', origin_id=event_id), data=data, follow_redirects=True) assert b'Event Horizon' in rv.data self.app.get(url_for('event_view', id_=event_id)) # Update involvement with app.test_request_context(): app.preprocess_request() link_id = LinkMapper.get_links(event_id, 'P22')[0].id rv = self.app.get(url_for('involvement_update', id_=link_id, origin_id=event_id)) assert b'Captain' in rv.data rv = self.app.post( url_for('involvement_update', id_=link_id, origin_id=actor_id), data={'description': 'Infinite Space - Infinite Terror', 'activity': 'P23'}, follow_redirects=True) assert b'Infinite Space - Infinite Terror' in rv.data self.app.get(url_for('event_view', id_=event_id))
def tree_select(name): html = """ <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}] }} }}); $("#{name}-tree-search").keyup(function() {{ $("#{name}-tree").jstree("search", $(this).val()); }}); $("#{name}-tree").on("select_node.jstree", function (e, data) {{ document.location.href = data.node.original.href; }}); }}); </script>""".format(name=sanitize(name), tree=walk_tree(NodeMapper.get_nodes(name))) return html
def delete_orphans(parameter) -> int: from openatlas.models.node import NodeMapper class_codes = tuple(app.config['CODE_CLASS'].keys()) if parameter == 'orphans': class_codes = class_codes + ('E55',) sql_where = EntityMapper.sql_orphan + " AND e.class_code NOT IN %(class_codes)s" elif parameter == 'unlinked': sql_where = EntityMapper.sql_orphan + " AND e.class_code IN %(class_codes)s" elif parameter == 'types': count = 0 for node in NodeMapper.get_orphans(): EntityMapper.delete(node) count += 1 return count else: return 0 sql = 'DELETE FROM model.entity WHERE id IN (' + sql_where + ');' g.cursor.execute(sql, {'class_codes': class_codes}) debug_model['div sql'] += 1 return g.cursor.rowcount
def __call__(self, field, **kwargs): selection = '' selected_ids = [] root = g.nodes[int(field.id)] if field.data: # Somehow field.data can be a string after a failed form validation, so fix that below field.data = ast.literal_eval(field.data) if type(field.data) is str else field.data for entity_id in field.data: selected_ids.append(entity_id) selection += g.nodes[entity_id].name + '<br />' html = """ <span id="{name}-button" class="button">{change_label}</span> <div id="{name}-selection" style="text-align:left;">{selection}</div> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container"> <input class="tree-filter" id="{name}-tree-search" placeholder="{filter}" /> <div id="{name}-tree"></div> </div> </div> <script> createOverlay("{name}", "{title}", true, "tree"); $("#{name}-tree").jstree({{ "core" : {{ "check_callback" : true, 'data':[{tree_data}] }}, "search": {{"case_insensitive": true, "show_only_matches": true}}, "plugins": ["search", "checkbox"], "checkbox": {{"three_state": false}} }}); $("#{name}-tree-search").keyup(function(){{ if (this.value.length >= {min_chars}) {{ $("#{name}-tree").jstree("search", $(this).val()); }} }}); </script>""".format( filter=uc_first(_('type to search')), min_chars=session['settings']['minimum_jstree_search'], name=field.id, title=root.name, selection=selection, change_label=uc_first(_('change')), tree_data=NodeMapper.get_tree_data(int(field.id), selected_ids)) return super(TreeMultiSelect, self).__call__(field, **kwargs) + html
def tree_select(name): html = """ <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}] }} }}); $("#{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=walk_tree(NodeMapper.get_nodes(name))) return html
def print_base_type(self) -> str: from openatlas.models.node import NodeMapper if not self.view_name or self.view_name == 'actor': # actors have no base type return '' root_name = self.view_name.title() if self.view_name == 'reference': root_name = self.system_type.title() if root_name == 'External Reference Geonames': root_name = 'External Reference' elif self.view_name == 'file': root_name = 'License' elif self.view_name == 'place': root_name = uc_first(self.system_type) if self.system_type == 'stratigraphic unit': root_name = 'Stratigraphic Unit' elif self.class_.code == 'E84': root_name = 'Information Carrier' root_id = NodeMapper.get_hierarchy_by_name(root_name).id for node in self.nodes: if node.root and node.root[-1] == root_id: return node.name return ''
def __call__(self, field, **kwargs): selection = '' selected_ids = [] root = g.nodes[int(field.id)] if field.data: if isinstance(field.data, str): field.data = ast.literal_eval(field.data) for entity_id in field.data: entity = g.nodes[entity_id] selected_ids.append(entity.id) html = """ <span id="{name}-button" class="button">{change_label}</span> <div id="{name}-selection" style="text-align:left;">{selection}</div> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container"> <input class="tree-filter" id="{name}-tree-search" placeholder="Filter" /> <div id="{name}-tree"></div> </div> </div> <script> createOverlay("{name}", "{title}", true, "tree"); $("#{name}-tree").jstree({{ "core" : {{ "check_callback" : true, 'data':[{tree_data}] }}, "search": {{"case_insensitive": true, "show_only_matches": true}}, "plugins": ["search", "checkbox"], "checkbox": {{"three_state": false}} }}); $("#{name}-tree-search").keyup(function(){{ $("#{name}-tree").jstree("search", $(this).val()); }}); </script>""".format( name=field.id, title=root.name, selection=selection, change_label=uc_first(_('change')), tree_data=NodeMapper.get_tree_data(int(field.id), selected_ids)) return super(TreeMultiSelect, self).__call__(field, **kwargs) + html
def test_place(self): with app.app_context(): self.login() rv = self.app.get(url_for('place_insert')) assert b'+ Place' in rv.data with app.test_request_context(): app.preprocess_request() unit_node = NodeMapper.get_hierarchy_by_name('Administrative Unit') unit_sub1 = g.nodes[unit_node.subs[0]] unit_sub2 = g.nodes[unit_node.subs[1]] reference_id = EntityMapper.insert('E31', 'Ancient Books', 'edition').id place_node = NodeMapper.get_hierarchy_by_name('Place') source_id = EntityMapper.insert('E33', 'Tha source').id data = {'name': 'Asgard', 'alias-0': 'Valhöll', unit_node.id: '[' + str(unit_sub1.id) + ',' + str(unit_sub2.id) + ']'} rv = self.app.post(url_for('place_insert', origin_id=reference_id), data=data, follow_redirects=True) assert b'Asgard' in rv.data gis_points = """[{"type":"Feature", "geometry":{"type":"Point", "coordinates":[9,17]}, "properties":{"name":"Valhalla","description":"","shapeType":"centerpoint"}}]""" data['gis_points'] = gis_points data['gis_polygons'] = """[{"geometry":{ "coordinates":[[[9.75307425847859,17.8111792731339], [9.75315472474904,17.8110005175436],[9.75333711496205,17.8110873417098], [9.75307425847859,17.8111792731339]]],"type":"Polygon"}, "properties":{"count":4,"description":"","id":8,"name":"", "objectDescription":"","objectId":185,"shapeType":"Shape", "siteType":"Settlement","title":""},"type":"Feature"}]""" data[place_node.id] = place_node.subs data['continue_'] = 'yes' rv = self.app.post( url_for('place_insert', origin_id=source_id), data=data, follow_redirects=True) assert b'Tha source' in rv.data with app.test_request_context(): app.preprocess_request() places = EntityMapper.get_by_codes('place') place_id = places[0].id place2 = places[1] location = place2.get_linked_entity('P53') actor = EntityMapper.insert('E21', 'Milla Jovovich') actor.link('P74', location) assert b'Tha source' in rv.data rv = self.app.get(url_for('place_index')) assert b'Asgard' in rv.data rv = self.app.get(url_for('place_update', id_=place_id)) assert b'Valhalla' in rv.data data['continue_'] = '' data['alias-1'] = 'Val-hall' rv = self.app.post( url_for('place_update', id_=place_id), data=data, follow_redirects=True) assert b'Val-hall' in rv.data with app.test_request_context(): app.preprocess_request() event = EntityMapper.insert('E8', 'Valhalla rising') event.link('P7', location) event.link('P24', location) rv = self.app.get(url_for('place_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('place_insert', origin_id=source_id), data=data, follow_redirects=True) assert b'An invalid geometry was entered' 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), data={unit_node.id: unit_sub2.id, 'selection': location.id}, follow_redirects=True) assert b'Entities where updated' in rv.data # Test move entities of multiple node if link to new node doesn't exists rv = self.app.post(url_for('node_move_entities', id_=unit_sub2.id), data={unit_node.id: unit_sub1.id, 'selection': location.id}, follow_redirects=True) assert b'Entities where updated' in rv.data # Subunits with app.app_context(): self.app.get(url_for('place_insert', origin_id=place_id)) rv = self.app.post(url_for('place_insert', origin_id=place_id), data={'name': "It's not a bug, it's a feature!"}) feat_id = rv.location.split('/')[-1] self.app.get(url_for('place_insert', origin_id=feat_id)) self.app.get(url_for('place_update', id_=feat_id)) self.app.post(url_for('place_update', id_=feat_id), data={'name': "It's not a bug, it's a feature!"}) rv = self.app.post(url_for('place_insert', origin_id=feat_id), data={'name': "I'm a stratigraphic unit"}) stratigraphic_id = rv.location.split('/')[-1] self.app.get(url_for('place_insert', origin_id=stratigraphic_id)) self.app.get(url_for('place_update', id_=stratigraphic_id)) self.app.post(url_for('place_update', id_=stratigraphic_id), data={'name': "I'm a stratigraphic unit"}) dimension_node_id = NodeMapper.get_hierarchy_by_name('Dimensions').subs[0] data = {'name': 'You never find me', str(dimension_node_id): '50'} rv = self.app.post(url_for('place_insert', origin_id=stratigraphic_id), data=data) find_id = rv.location.split('/')[-1] self.app.get(url_for('place_update', id_=find_id)) self.app.post(url_for('place_update', id_=find_id), data=data) rv = self.app.get(url_for('place_view', id_=feat_id)) assert b'not a bug' in rv.data rv = self.app.get(url_for('place_view', id_=stratigraphic_id)) assert b'a stratigraphic unit' in rv.data rv = self.app.get(url_for('place_view', id_=find_id)) assert b'You never' in rv.data rv = self.app.get(url_for('place_delete', id_=place_id), follow_redirects=True) assert b'not possible if subunits' in rv.data rv = self.app.get(url_for('place_delete', id_=find_id), follow_redirects=True) assert b'The entry has been deleted.' in rv.data rv = self.app.get(url_for('place_delete', id_=place2.id), follow_redirects=True) assert b'The entry has been deleted.' in rv.data
def save_nodes(self, form: FlaskForm) -> None: from openatlas.models.node import NodeMapper NodeMapper.save_entity_nodes(self, form)
def test_relation(self): with app.app_context(): self.login() with app.test_request_context(): app.preprocess_request() actor_id = EntityMapper.insert('E21', 'Connor MacLeod').id related_id = EntityMapper.insert('E21', 'The Kurgan').id # Add relationship rv = self.app.get(url_for('relation_insert', origin_id=actor_id)) assert b'Actor Actor Relation' in rv.data relation_id = NodeMapper.get_hierarchy_by_name('Actor Actor Relation').id relation_sub_id = g.nodes[relation_id].subs[0] data = { 'actor': '[' + str(related_id) + ']', relation_id: relation_sub_id, 'inverse': None, 'date_begin_year': '-1949', 'date_begin_month': '10', 'date_begin_day': '8', 'date_begin_year2': '-1948', 'date_end_year': '2049', 'date_end_year2': '2050'} rv = self.app.post( url_for('relation_insert', origin_id=actor_id), data=data, follow_redirects=True) assert b'The Kurgan' in rv.data rv = self.app.get(url_for('node_view', id_=relation_sub_id)) assert b'Connor' in rv.data data['continue_'] = 'yes' data['inverse'] = True rv = self.app.post( url_for('relation_insert', origin_id=actor_id), data=data, follow_redirects=True) assert b'The Kurgan' in rv.data rv = self.app.get(url_for('actor_view', id_=actor_id)) assert b'The Kurgan' in rv.data rv = self.app.post( url_for('relation_insert', origin_id=related_id), data=data, follow_redirects=True) assert b"Can't link to itself." in rv.data # Relation types rv = self.app.get(url_for('node_move_entities', id_=relation_sub_id)) assert b'The Kurgan' in rv.data # Update relationship with app.test_request_context(): app.preprocess_request() link_id = LinkMapper.get_links(actor_id, 'OA7')[0].id link_id2 = LinkMapper.get_links(actor_id, 'OA7', True)[0].id rv = self.app.get(url_for('relation_update', id_=link_id, origin_id=related_id)) assert b'Connor' in rv.data rv = self.app.post( url_for('relation_update', id_=link_id, origin_id=actor_id), data={'description': 'There can be only one!', 'inverse': True}, follow_redirects=True) assert b'only one' in rv.data rv = self.app.post( url_for('relation_update', id_=link_id2, origin_id=actor_id), data={'description': 'There can be only one!', 'inverse': None}, follow_redirects=True) assert b'only one' in rv.data
def get_all(objects=None): all_ = {'point': [], 'linestring': [], 'polygon': []} selected = {'point': [], 'linestring': [], 'polygon': [], 'polygon_point': []} # Workaround to include GIS features of a subunit which would be otherwise omitted subunit_selected_id = 0 if objects: if type(objects) is not list: if objects.system_type in ['feature', 'finds', 'stratigraphic unit']: subunit_selected_id = objects.id objects = [objects] else: objects = [] object_ids = [x.id for x in objects] polygon_point_sql = \ 'public.ST_AsGeoJSON(public.ST_PointOnSurface(polygon.geom)) AS polygon_point, ' for shape in ['point', 'polygon', 'linestring']: sql = """ SELECT object.id AS object_id, {shape}.id, {shape}.name, {shape}.description, {shape}.type, public.ST_AsGeoJSON({shape}.geom) AS geojson, {polygon_point_sql} object.name AS object_name, object.description AS object_desc, string_agg(CAST(t.range_id AS text), ',') AS types FROM model.entity place JOIN model.link l ON place.id = l.range_id JOIN model.entity object ON l.domain_id = object.id JOIN gis.{shape} {shape} ON place.id = {shape}.entity_id LEFT JOIN model.link t ON object.id = t.domain_id AND t.property_code = 'P2' WHERE place.class_code = 'E53' AND l.property_code = 'P53' AND (object.system_type = 'place' OR object.id = {subunit_selected_id}) GROUP BY object.id, {shape}.id;""".format( shape=shape, subunit_selected_id=subunit_selected_id, polygon_point_sql=polygon_point_sql if shape == 'polygon' else '') g.cursor.execute(sql) debug_model['div sql'] += 1 place_type_root_id = NodeMapper.get_hierarchy_by_name('Place').id for row in g.cursor.fetchall(): description = row.description.replace('"', '\"') if row.description else '' object_desc = row.object_desc.replace('"', '\"') if row.object_desc else '' item = {'type': 'Feature', 'geometry': json.loads(row.geojson), 'properties': { 'objectId': row.object_id, 'objectName': row.object_name.replace('"', '\"'), 'objectDescription': object_desc, 'id': row.id, 'name': row.name.replace('"', '\"') if row.name else '', 'description': description, 'shapeType': row.type}} if hasattr(row, 'types') and row.types: nodes_list = ast.literal_eval('[' + row.types + ']') for node_id in list(set(nodes_list)): node = g.nodes[node_id] if node.root and node.root[-1] == place_type_root_id: item['properties']['objectType'] = node.name.replace('"', '\"') break if row.object_id in object_ids: selected[shape].append(item) else: all_[shape].append(item) if hasattr(row, 'polygon_point'): polygon_point_item = dict(item) # make a copy to prevent overriding geometry polygon_point_item['geometry'] = json.loads(row.polygon_point) if row.object_id in object_ids: selected['polygon_point'].append(polygon_point_item) else: all_['point'].append(polygon_point_item) return {'gisPointAll': json.dumps(all_['point']), 'gisPointSelected': json.dumps(selected['point']), 'gisLineAll': json.dumps(all_['linestring']), 'gisLineSelected': json.dumps(selected['linestring']), 'gisPolygonAll': json.dumps(all_['polygon']), 'gisPolygonSelected': json.dumps(selected['polygon']), 'gisPolygonPointSelected': json.dumps(selected['polygon_point'])}
def test_actor(self): with app.app_context(): self.login() rv = self.app.get(url_for('actor_index')) assert b'No entries' in rv.data # Create entities for actor rv = self.app.post(url_for('place_insert'), data={'name': 'Nostromos'}) residence_id = rv.location.split('/')[-1] with app.test_request_context(): app.preprocess_request() sex_node = NodeMapper.get_hierarchy_by_name('Sex') sex_node_sub_1 = g.nodes[sex_node.subs[0]] sex_node_sub_2 = g.nodes[sex_node.subs[1]] event_id = EntityMapper.insert('E8', 'Event Horizon').id source_id = EntityMapper.insert('E33', 'Tha source').id # Actor insert rv = self.app.get(url_for('actor_insert', code='E21')) assert b'+ Person' in rv.data self.app.get(url_for('actor_insert', code='E21', origin_id=residence_id)) data = { sex_node.id: sex_node_sub_1.id, 'name': 'Sigourney Weaver', 'alias-1': 'Ripley', 'residence': residence_id, 'appears_first': residence_id, 'appears_last': residence_id, 'description': 'Susan Alexandra Weaver is an American actress.', 'date_begin_year': '-1949', 'date_begin_month': '10', 'date_begin_day': '8', 'date_begin_year2': '-1948', 'date_end_year': '2049', 'date_end_year2': '2050', 'date_birth': True, 'date_death': True} rv = self.app.post( url_for('actor_insert', code='E21', origin_id=residence_id), data=data) actor_id = rv.location.split('/')[-1] # Test actor nodes rv = self.app.get(url_for('node_view', id_=sex_node_sub_1.id)) assert b'Susan' in rv.data rv = self.app.get(url_for('node_move_entities', id_=sex_node_sub_1.id)) assert b'Sigourney' in rv.data rv = self.app.post(url_for('node_move_entities', id_=sex_node_sub_1.id), data={sex_node.id: sex_node_sub_2.id, 'selection': [actor_id]}, follow_redirects=True) assert b'Entities where updated' in rv.data rv = self.app.post(url_for('node_move_entities', id_=sex_node_sub_2.id), data={sex_node.id: '', 'selection': [actor_id]}, follow_redirects=True) assert b'Entities where updated' in rv.data self.app.post(url_for('actor_insert', code='E21', origin_id=actor_id), data=data) self.app.post(url_for('actor_insert', code='E21', origin_id=event_id), data=data) self.app.post(url_for('actor_insert', code='E21', origin_id=source_id), data=data) rv = self.app.post(url_for('reference_insert', code='reference'), data={'name': 'Book'}) reference_id = rv.location.split('/')[-1] rv = self.app.post( url_for('actor_insert', code='E21', origin_id=reference_id), data=data, follow_redirects=True) assert b'An entry has been created' in rv.data data['continue_'] = 'yes' rv = self.app.post( url_for('actor_insert', code='E21'), data=data, follow_redirects=True) assert b'An entry has been created' in rv.data rv = self.app.get(url_for('actor_index')) assert b'Sigourney Weaver' in rv.data # Actor update rv = self.app.get(url_for('actor_update', id_=actor_id)) assert b'American actress' in rv.data data['name'] = 'Susan Alexandra Weaver' data['date_end_year'] = '' data['date_begin_year2'] = '1950' data['date_begin_day'] = '' rv = self.app.post( url_for('actor_update', id_=actor_id), data=data, follow_redirects=True) assert b'Susan Alexandra Weaver' in rv.data rv = self.app.post( url_for('ajax_bookmark'), data={'entity_id': actor_id}, follow_redirects=True) assert b'Remove bookmark' in rv.data rv = self.app.get('/') assert b'Weaver' in rv.data rv = self.app.post( url_for('ajax_bookmark'), data={'entity_id': actor_id}, follow_redirects=True) assert b'Bookmark' in rv.data rv = self.app.get(url_for('link_delete', origin_id=actor_id, id_=666), follow_redirects=True) assert b'removed'in rv.data # Actor delete rv = self.app.get(url_for('actor_delete', id_=actor_id), follow_redirects=True) assert b'The entry has been deleted.' in rv.data
def save_nodes(self, form): from openatlas.models.node import NodeMapper NodeMapper.save_entity_nodes(self, form)