summaryrefslogtreecommitdiff
path: root/archaeological_context_records/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'archaeological_context_records/models.py')
-rw-r--r--archaeological_context_records/models.py186
1 files changed, 132 insertions, 54 deletions
diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py
index 96ba16935..65145be98 100644
--- a/archaeological_context_records/models.py
+++ b/archaeological_context_records/models.py
@@ -1366,8 +1366,11 @@ class ContextRecordTree(RelationsViews):
REFERENCES {fk_table}(id),
CONSTRAINT fk2_{table} FOREIGN KEY(cr_parent_id)
REFERENCES {fk_table}(id)
- );""".format(table="context_records_tree",
- fk_table="archaeological_context_records_contextrecord")
+ );
+ CREATE INDEX {table}_id ON {table} (cr_id);
+ CREATE INDEX {table}_parent_id ON {table} (cr_parent_id);
+ """.format(table="context_records_tree",
+ fk_table="archaeological_context_records_contextrecord")
key = models.TextField(primary_key=True)
cr = models.ForeignKey(
@@ -1388,7 +1391,6 @@ class ContextRecordTree(RelationsViews):
@classmethod
def _save_tree(cls, tree):
keys = []
- print("tree", tree)
for idx, parent_id in enumerate(tree[:-1]):
for child_id in tree[idx:]:
if child_id != parent_id:
@@ -1400,84 +1402,160 @@ class ContextRecordTree(RelationsViews):
return keys
@classmethod
+ def _get_base_relations(cls):
+ return RelationType.objects.filter(
+ logical_relation__in=('included', 'equal')).values_list("id", flat=True)
+
+ @classmethod
+ def _get_base_equal_relations(cls):
+ return RelationType.objects.filter(
+ logical_relation='equal').values_list("id", flat=True)
+
+ @classmethod
+ def _get_base_included_relations(cls):
+ return RelationType.objects.filter(
+ logical_relation='included').values_list("id", flat=True)
+
+ @classmethod
+ def _get_base_children(cls):
+ return ContextRecord.objects.values_list("id", flat=True)
+
+ @classmethod
def _update_child(cls, parent_id, tree, rel_types):
whole_tree = set()
- childs = RecordRelations.objects.values_list(
+ children = list(RecordRelations.objects.values_list(
"left_record_id", flat=True).filter(
- right_record_id=parent_id, relation_type_id__in=rel_types)
- for c in childs[:]:
+ right_record_id=parent_id, relation_type_id__in=rel_types))
+ to_be_pop = []
+ for idx, c in enumerate(children[:]):
if c in tree: # cyclic
- childs.pop(c)
- #print("childs", parent_id, childs)
- if not childs: # last leaf in the tree
+ to_be_pop.append(idx)
+ for idx in reversed(to_be_pop):
+ children.pop(idx)
+ if not children: # last leaf in the tree
return cls._save_tree(tree)
- for c in childs:
+ for c in children:
whole_tree.update(cls._update_child(c, tree[:] + [c], rel_types))
return whole_tree
@classmethod
- def _get_parent_trees(cls, child_id, trees, rel_types):
+ def _get_parent_trees(cls, child_id, trees, rel_types, deep=0):
parents = RecordRelations.objects.values_list(
"right_record_id", flat=True).filter(
left_record_id=child_id, relation_type_id__in=rel_types)
if not parents:
return trees
new_trees = []
- for p in parents:
+ for p in set(parents):
if p == child_id or any(1 for tree in trees if p in tree): # cyclic
continue
c_trees = list(map(lambda x: x + [p], trees))
- new_trees += cls._get_parent_trees(p, c_trees, rel_types)
+ new_trees += cls._get_parent_trees(p, c_trees, rel_types, deep + 1)
return new_trees
@classmethod
- def _update(cls, item_id, cascade=True):
- # update the whole tree
- rel_types = RelationType.objects.filter(
- logical_relation__in=('included', 'equal')).values_list("id", flat=True)
+ def _get_equals(cls, item_id, equal_rel_types):
+ equals = list(RecordRelations.objects.values_list(
+ "right_record_id", flat=True).filter(
+ left_record_id=item_id, relation_type_id__in=equal_rel_types))
+ equals += list(RecordRelations.objects.values_list(
+ "left_record_id", flat=True).filter(
+ right_record_id=item_id, relation_type_id__in=equal_rel_types))
+ return set(equals)
+
+ @classmethod
+ def _update_equals(cls, item_id, equals):
+ keys = []
+ for equal_id in equals:
+ if item_id != equal_id:
+ cls.objects.get_or_create(
+ key=f"{item_id}_{equal_id}",
+ cr_id=item_id, cr_parent_id=equal_id
+ )
+ keys.append((item_id, equal_id))
+ cls.objects.get_or_create(
+ key=f"{equal_id}_{item_id}",
+ cr_id=equal_id, cr_parent_id=item_id
+ )
+ keys.append((equal_id, item_id))
+ return keys
+
+ @classmethod
+ def _update_relations_equals(cls, relations):
+ equal_rel_types = cls._get_base_equal_relations()
+ keys = []
+ for child_id, parent_id in relations:
+ equals = set(cls._get_equals(child_id, equal_rel_types))
+ keys += cls._update_equals(child_id, equals)
+ for alt_child in equals:
+ if alt_child != child_id:
+ cls.objects.get_or_create(
+ key=f"{alt_child}_{parent_id}",
+ cr_id=alt_child, cr_parent_id=parent_id
+ )
+ keys.append((alt_child, parent_id))
+ equals = set(cls._get_equals(parent_id, equal_rel_types))
+ keys += cls._update_equals(parent_id, equals)
+ for alt_parent in equals:
+ if alt_parent != parent_id:
+ cls.objects.get_or_create(
+ key=f"{child_id}_{alt_parent}",
+ cr_id=child_id, cr_parent_id=alt_parent
+ )
+ keys.append((child_id, alt_parent))
+ return set(keys)
+
+ @classmethod
+ def _update(cls, item_id, already_updated=None):
+ all_relations = set()
+ # add self relation
+ cls.objects.get_or_create(
+ key=f"{item_id}_{item_id}",
+ cr_id=item_id, cr_parent_id=item_id
+ )
+ all_relations.add((item_id, item_id))
+ current_relations_as_child = list(
+ cls.objects.filter(cr_id=item_id).values_list("cr_parent_id", flat=True)
+ )
+ current_relations_as_parent = list(
+ cls.objects.filter(cr_parent_id=item_id).values_list("cr_id", flat=True)
+ )
+
+ ## update the whole tree
+ inc_rel_types = cls._get_base_included_relations()
# get first parents
parent_ids = [
- tree[-1] for tree in cls._get_parent_trees(item_id, [[item_id]], rel_types)]
- """
- parent_ids = []
- current_ids = [item_id]
- while current_ids:
- new_ids = []
- for current_id in current_ids:
- parents = RecordRelations.objects.values_list(
- "right_record_id", flat=True).filter(
- left_record_id=current_id, relation_type_id__in=rel_types)
- if not parents:
- continue
- for p in parents[:]:
- if p == current_id or p in parent_ids: # cyclic
- parents.pop(p)
- parent_ids += parents
- new_ids += parents
- current_ids = new_ids
- """
- def get_cr(idx):
- return ContextRecord.objects.get(pk=idx)
- print(get_cr(item_id))
+ tree[-1] for tree in cls._get_parent_trees(item_id, [[item_id]],
+ inc_rel_types)]
if not parent_ids:
parent_ids = [item_id]
- print("parents", [get_cr(p) for p in parent_ids])
# get all child for parents and save trees
- all_relations = set()
for parent_id in parent_ids:
tree = [parent_id]
- all_relations.update(cls._update_child(parent_id, tree, rel_types))
- #print(all_relations)
-
- # delete old relations
- for item_id in set([c for c, __ in all_relations] +
- [p for p, __ in all_relations]):
- for rel in cls.objects.filter(cr_id=item_id).all():
- if (rel.cr_id, rel.cr_parent_id) not in all_relations:
- rel.delete()
- for rel in cls.objects.filter(cr_parent_id=item_id).all():
- if (rel.cr_id, rel.cr_parent_id) not in all_relations:
- rel.delete()
-
+ all_relations.update(cls._update_child(parent_id, tree, inc_rel_types))
+ all_relations.update(cls._update_relations_equals(all_relations))
+ if not all_relations:
+ equal_rel_types = cls._get_base_equal_relations()
+ equals = set(cls._get_equals(item_id, equal_rel_types))
+ all_relations.update(cls._update_equals(item_id, equals))
+
+
+ ## delete old relations
+ if not already_updated:
+ already_updated = [item_id]
+ for parent_id in current_relations_as_child:
+ if (item_id, parent_id) not in all_relations and \
+ parent_id not in already_updated:
+ # disappeared - must regenerate
+ already_updated.append(parent_id)
+ cls.objects.filter(key=f"{item_id}_{parent_id}").delete()
+ cls._update(parent_id, already_updated)
+ for child_id in current_relations_as_parent:
+ if (child_id, item_id) not in all_relations and \
+ child_id not in already_updated:
+ # disappeared - must regenerate
+ already_updated.append(child_id)
+ cls.objects.filter(key=f"{child_id}_{item_id}").delete()
+ cls._update(child_id, already_updated)