diff options
Diffstat (limited to 'archaeological_context_records/models.py')
| -rw-r--r-- | archaeological_context_records/models.py | 114 | 
1 files changed, 113 insertions, 1 deletions
diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py index d62dde4be..96ba16935 100644 --- a/archaeological_context_records/models.py +++ b/archaeological_context_records/models.py @@ -35,6 +35,7 @@ from ishtar_common.utils import (      cached_label_changed,      m2m_historization_changed,      post_save_geo, +    task  )  from ishtar_common.models import ( @@ -61,6 +62,7 @@ from ishtar_common.models import (      DocumentItem,      MainItem,      QuickAction, +    RelationsViews  )  from ishtar_common.models_common import HistoricalRecords  from archaeological_operations.models import ( @@ -1282,7 +1284,7 @@ class RecordRelationView(models.Model):          return '{} "{}"'.format(self.relation_type, self.right_record) -class ContextRecordTree(models.Model): +class ContextRecordTree(RelationsViews):      CREATE_SQL = """      CREATE VIEW cr_parent_relation_id AS          SELECT id @@ -1354,6 +1356,19 @@ class ContextRecordTree(models.Model):      DROP VIEW IF EXISTS context_records_tree;      DROP VIEW IF EXISTS cr_parent_relation_id;      """ + +    CREATE_TABLE_SQL = """ +    CREATE TABLE {table} ( +        key                 varchar(100) PRIMARY KEY, +        cr_id               integer NOT NULL, +        cr_parent_id        integer NOT NULL, +        CONSTRAINT fk1_{table} FOREIGN KEY(cr_id) +            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") +      key = models.TextField(primary_key=True)      cr = models.ForeignKey(          "archaeological_context_records.ContextRecord", @@ -1369,3 +1384,100 @@ class ContextRecordTree(models.Model):      class Meta:          managed = False          db_table = "context_records_tree" + +    @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: +                    cls.objects.get_or_create( +                        key=f"{child_id}_{parent_id}", +                        cr_id=child_id, cr_parent_id=parent_id +                    ) +                    keys.append((child_id, parent_id)) +        return keys + +    @classmethod +    def _update_child(cls, parent_id, tree, rel_types): +        whole_tree = set() +        childs = 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[:]: +            if c in tree:  # cyclic +                childs.pop(c) +        #print("childs", parent_id, childs) +        if not childs:  # last leaf in the tree +            return cls._save_tree(tree) +        for c in childs: +            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): +        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: +            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) +        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) + +        # 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)) +        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() +  | 
