diff options
| -rw-r--r-- | archaeological_finds/migrations/0151_data_migration.json | 30 | ||||
| -rw-r--r-- | archaeological_finds/migrations/0151_data_migration_find_relation_type.py | 24 | ||||
| -rw-r--r-- | archaeological_finds/migrations/0152_find_cached_hierarchy_material_types.py | 24 | ||||
| -rw-r--r-- | archaeological_finds/models_finds.py | 8 | ||||
| -rw-r--r-- | archaeological_finds/templates/ishtar/sheet_find.html | 21 | ||||
| -rw-r--r-- | archaeological_finds/templates/ishtar/sheet_museum_find.html | 21 | ||||
| -rw-r--r-- | archaeological_operations/migrations/0129_data_migration.json | 176 | ||||
| -rw-r--r-- | archaeological_operations/templates/ishtar/sheet_operation.html | 25 | ||||
| -rw-r--r-- | archaeological_operations/templates/ishtar/sheet_site.html | 22 | ||||
| -rw-r--r-- | ishtar_common/models.py | 3 | ||||
| -rw-r--r-- | ishtar_common/models_common.py | 30 | ||||
| -rw-r--r-- | ishtar_common/templates/ishtar/blocks/sheet_relations.html | 22 | ||||
| -rw-r--r-- | ishtar_common/utils.py | 16 | ||||
| -rw-r--r-- | ishtar_common/views_item.py | 23 |
14 files changed, 354 insertions, 91 deletions
diff --git a/archaeological_finds/migrations/0151_data_migration.json b/archaeological_finds/migrations/0151_data_migration.json new file mode 100644 index 000000000..58dd88c85 --- /dev/null +++ b/archaeological_finds/migrations/0151_data_migration.json @@ -0,0 +1,30 @@ +[ + { + "model": "archaeological_finds.findrelationtype", + "fields": { + "label": "Recolle avec", + "txt_idx": "recolle-avec", + "comment": "", + "available": true, + "order": 10, + "symmetrical": true, + "tiny_label": null, + "inverse_relation": null, + "logical_relation": null + } + }, + { + "model": "archaeological_finds.findrelationtype", + "fields": { + "label": "Voisin de", + "txt_idx": "voisin-de", + "comment": "", + "available": true, + "order": 10, + "symmetrical": true, + "tiny_label": null, + "inverse_relation": null, + "logical_relation": null + } + } +] diff --git a/archaeological_finds/migrations/0151_data_migration_find_relation_type.py b/archaeological_finds/migrations/0151_data_migration_find_relation_type.py new file mode 100644 index 000000000..f3a1c1b4e --- /dev/null +++ b/archaeological_finds/migrations/0151_data_migration_find_relation_type.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.24 on 2024-02-10 12:07 + +import os + +from django.db import migrations +from django.core.management import call_command + + +def load_data(apps, __): + FindRelationtypeType = apps.get_model("archaeological_finds", "findrelationtype") + if not FindRelationtypeType.objects.count(): + json_path = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-1] + ["0151_data_migration.json"]) + call_command("loaddata", json_path) + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_finds', '0150_findrelationtype_findrecordrelations'), + ] + + operations = [ + migrations.RunPython(load_data) + ] diff --git a/archaeological_finds/migrations/0152_find_cached_hierarchy_material_types.py b/archaeological_finds/migrations/0152_find_cached_hierarchy_material_types.py new file mode 100644 index 000000000..b0d959ff8 --- /dev/null +++ b/archaeological_finds/migrations/0152_find_cached_hierarchy_material_types.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.21 on 2026-04-07 18:45 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_finds', '0151_data_migration_find_relation_type'), + ] + + operations = [ + migrations.AddField( + model_name='find', + name='cached_hierarchy_material_types', + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), blank=True, default=list, size=None), + ), + migrations.AddField( + model_name='historicalfind', + name='cached_hierarchy_material_types', + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), blank=True, default=list, size=None), + ), + ] diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index 57f7a4f79..4a8a4ee93 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -24,6 +24,7 @@ import uuid from django.apps import apps from django.conf import settings from django.contrib.gis.db import models +from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.indexes import GinIndex from django.db.models import Max, Q, F from django.db.models.signals import m2m_changed, post_save, post_delete, pre_delete @@ -1440,6 +1441,7 @@ class Find( "documents__image__isnull": "documents__image__isnull", "container__location": "container__location__pk", "container_ref__location": "container_ref__location__pk", + "base_finds__excavation_id": "base_finds__excavation_id" } for table in (TABLE_COLS, TABLE_COLS_FOR_OPE): for key in table: @@ -2134,6 +2136,9 @@ class Find( "cached_object_types", "cached_materials", ] + CACHED_HIERARCHY = [ + "material_types", + ] SERIALIZE_CALL = { "base_finds_list": "base_finds_list", "documents_list": "documents_list", @@ -2504,6 +2509,9 @@ class Find( db_index=True, help_text=_("Cached value - do not edit"), ) + cached_hierarchy_material_types = ArrayField( + models.TextField(), blank=True, default=list + ) history = HistoricalRecords(bases=[HistoryModel]) BASKET_MODEL = FindBasket diff --git a/archaeological_finds/templates/ishtar/sheet_find.html b/archaeological_finds/templates/ishtar/sheet_find.html index 0ae238d66..ac1ae4f05 100644 --- a/archaeological_finds/templates/ishtar/sheet_find.html +++ b/archaeological_finds/templates/ishtar/sheet_find.html @@ -26,6 +26,7 @@ {% with non_modif_treatments_count=item.non_modif_treatments_count %} {% with associated_treatment_files_count=item.associated_treatment_files_count %} +{% with display_relations=item|safe_or:"right_relations.count|left_relations.count"|safe_and_not:"right_relations_not_available"|safe_and_not:"left_relations_not_available" %} {% with can_view_container=permission_view_own_container|or_:permission_view_container %} {% with display_warehouse_treatments=item.container|or_:item.container_ref|or_:item.upstream_treatment|or_:item.downstream_treatment|or_:non_modif_treatments_count|or_:associated_treatment_files_count %} {% with dating_list=item|m2m_listing:"datings" %} @@ -51,6 +52,15 @@ {% trans "Archaeological context" %} </a> </li> + {% if display_relations %} + <li class="nav-item"> + <a class="nav-link" id="{{window_id}}-relations-tab" + data-toggle="tab" href="#{{window_id}}-relations" role="tab" + aria-controls="{{window_id}}-relations" aria-selected="false"> + {% trans "Relations" %} + </a> + </li> + {% endif %} <li class="nav-item"> <a class="nav-link" id="{{window_id}}-datations-tab" data-toggle="tab" href="#{{window_id}}-datations" role="tab" @@ -270,6 +280,15 @@ </div> </div> + {% if display_relations %} + <div class="tab-pane fade" id="{{window_id}}-relations" + role="tabpanel" aria-labelledby="{{window_id}}-relations-tab"> + {% with relation_url="/show-find/" %} + {% include "ishtar/blocks/sheet_relations.html" %} + {% endwith %} + </div> + {% endif %} + <div class="tab-pane fade" id="{{window_id}}-datations" role="tabpanel" aria-labelledby="{{window_id}}-datations-tab"> <h3>{% trans "Periods / Datings" %}</h3> @@ -326,7 +345,7 @@ {% endif %} </div> -{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %} +{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %} <script type='text/javascript'> $( "#{{window_id}}-tabs" ).on( "tabsactivate", function( event, ui ) { diff --git a/archaeological_finds/templates/ishtar/sheet_museum_find.html b/archaeological_finds/templates/ishtar/sheet_museum_find.html index 3acec990e..add8ce512 100644 --- a/archaeological_finds/templates/ishtar/sheet_museum_find.html +++ b/archaeological_finds/templates/ishtar/sheet_museum_find.html @@ -26,6 +26,7 @@ {% with non_modif_treatments_count=item.non_modif_treatments_count %} {% with associated_treatment_files_count=item.associated_treatment_files_count %} +{% with display_relations=item|safe_or:"right_relations.count|left_relations.count"|safe_and_not:"right_relations_not_available"|safe_and_not:"left_relations_not_available" %} {% with can_view_container=permission_view_own_container|or_:permission_view_container %} {% with display_warehouse_treatments=item.container|or_:item.container_ref|or_:item.upstream_treatment|or_:item.downstream_treatment|or_:non_modif_treatments_count|or_:associated_treatment_files_count %} {% with dating_list=item|m2m_listing:"datings" %} @@ -51,6 +52,15 @@ {% trans "Archaeological context" %} </a> </li> + {% if display_relations %} + <li class="nav-item"> + <a class="nav-link" id="{{window_id}}-relations-tab" + data-toggle="tab" href="#{{window_id}}-relations" role="tab" + aria-controls="{{window_id}}-relations" aria-selected="false"> + {% trans "Relations" %} + </a> + </li> + {% endif %} <li class="nav-item"> <a class="nav-link" id="{{window_id}}-datations-tab" data-toggle="tab" href="#{{window_id}}-datations" role="tab" @@ -270,6 +280,15 @@ </div> </div> + {% if display_relations %} + <div class="tab-pane fade" id="{{window_id}}-relations" + role="tabpanel" aria-labelledby="{{window_id}}-relations-tab"> + {% with relation_url="/show-find/" %} + {% include "ishtar/blocks/sheet_relations.html" %} + {% endwith %} + </div> + {% endif %} + <div class="tab-pane fade" id="{{window_id}}-datations" role="tabpanel" aria-labelledby="{{window_id}}-datations-tab"> <h3>{% trans "Periods / Datings" %}</h3> @@ -326,7 +345,7 @@ {% endif %} </div> -{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %} +{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %} <script type='text/javascript'> $( "#{{window_id}}-tabs" ).on( "tabsactivate", function( event, ui ) { diff --git a/archaeological_operations/migrations/0129_data_migration.json b/archaeological_operations/migrations/0129_data_migration.json index e60f33f27..edf090689 100644 --- a/archaeological_operations/migrations/0129_data_migration.json +++ b/archaeological_operations/migrations/0129_data_migration.json @@ -82,50 +82,152 @@ "order": 10 } }, -{ + { "model": "archaeological_operations.siterelationtype", "fields": { - "label": "Comprend", - "txt_idx": "comprend", - "comment": "", - "available": true, - "order": 1, - "symmetrical": false, - "tiny_label": null, - "inverse_relation": [ - "compris-dans" - ], - "logical_relation": null + "label": "Voisin de", + "txt_idx": "voisin-de", + "comment": "", + "available": true, + "order": 30, + "symmetrical": true, + "tiny_label": null, + "inverse_relation": null, + "logical_relation": null } -}, -{ + }, + { "model": "archaeological_operations.siterelationtype", "fields": { - "label": "Compris dans", - "txt_idx": "compris-dans", - "comment": "", - "available": true, - "order": 1, - "symmetrical": false, - "tiny_label": null, - "inverse_relation": [ - "comprend" - ], - "logical_relation": null + "label": "Relation indirecte", + "txt_idx": "relation-indirecte", + "comment": "", + "available": true, + "order": 40, + "symmetrical": true, + "tiny_label": null, + "inverse_relation": null, + "logical_relation": null } -}, -{ + }, + { "model": "archaeological_operations.siterelationtype", "fields": { - "label": "Lié à", - "txt_idx": "lie-a", - "comment": "", - "available": true, - "order": 1, - "symmetrical": true, - "tiny_label": null, - "inverse_relation": null, - "logical_relation": null + "label": "Parent de", + "txt_idx": "parent-de", + "comment": "", + "available": true, + "order": 10, + "symmetrical": false, + "tiny_label": null, + "inverse_relation": null, + "logical_relation": null + } + }, + { + "model": "archaeological_operations.siterelationtype", + "fields": { + "label": "Enfant de", + "txt_idx": "enfant-de", + "comment": "", + "available": true, + "order": 20, + "symmetrical": false, + "tiny_label": null, + "inverse_relation": null, + "logical_relation": null + } + }, + { + "model": "archaeological_operations.siterelationtype", + "fields": { + "label": "Ant\u00e9rieure \u00e0", + "txt_idx": "anterieure-a", + "comment": "", + "available": true, + "order": 50, + "symmetrical": false, + "tiny_label": null, + "inverse_relation": null, + "logical_relation": null + } + }, + { + "model": "archaeological_operations.siterelationtype", + "fields": { + "label": "Post\u00e9rieure \u00e0", + "txt_idx": "posterieure-a", + "comment": "", + "available": true, + "order": 60, + "symmetrical": false, + "tiny_label": null, + "inverse_relation": null, + "logical_relation": null + } + }, + { + "model": "archaeological_operations.siterelationtype", + "fields": { + "label": "Parent de", + "txt_idx": "parent-de", + "comment": "", + "available": true, + "order": 10, + "symmetrical": false, + "tiny_label": null, + "inverse_relation": [ + "enfant-de" + ], + "logical_relation": null + } + }, + { + "model": "archaeological_operations.siterelationtype", + "fields": { + "label": "Enfant de", + "txt_idx": "enfant-de", + "comment": "", + "available": true, + "order": 20, + "symmetrical": false, + "tiny_label": null, + "inverse_relation": [ + "parent-de" + ], + "logical_relation": null + } + }, + { + "model": "archaeological_operations.siterelationtype", + "fields": { + "label": "Ant\u00e9rieure \u00e0", + "txt_idx": "anterieure-a", + "comment": "", + "available": true, + "order": 50, + "symmetrical": false, + "tiny_label": null, + "inverse_relation": [ + "posterieure-a" + ], + "logical_relation": null + } + }, + { + "model": "archaeological_operations.siterelationtype", + "fields": { + "label": "Post\u00e9rieure \u00e0", + "txt_idx": "posterieure-a", + "comment": "", + "available": true, + "order": 60, + "symmetrical": false, + "tiny_label": null, + "inverse_relation": [ + "anterieure-a" + ], + "logical_relation": null } -} + } ] diff --git a/archaeological_operations/templates/ishtar/sheet_operation.html b/archaeological_operations/templates/ishtar/sheet_operation.html index e290ce319..fced5457a 100644 --- a/archaeological_operations/templates/ishtar/sheet_operation.html +++ b/archaeological_operations/templates/ishtar/sheet_operation.html @@ -410,28 +410,9 @@ {% if display_relations %} <div class="tab-pane fade" id="{{window_id}}-relations" role="tabpanel" aria-labelledby="{{window_id}}-relations-tab"> - - {% if item.right_relations.count and not item.right_relations_not_available %} - <h3>{% trans "Relations"%}</h3> - {% for rel in item.right_relations.all %} - {% ifchanged rel.relation_type %} - {% if forloop.counter0 %}</div>{% endif %} - <h4>{{rel.relation_type}}</h4> - <div class="row">{% endifchanged %} - <div class="col-12"> - <a href="#" onclick="load_window('/show-operation/{{rel.right_record.pk|unlocalize}}/');" class="display_details"> - <i class="fa fa-info-circle" aria-hidden="true"></i> - </a> {{rel.right_record}} - </div> - {% if forloop.last %} - </div>{% endif %} - {% endfor %} - {% else %} - <div class="alert alert-info" role="alert"> - <i class="fa fa-info-circle" aria-hidden="true"></i> - {% trans "No relations" %} - </div> - {% endif %} + {% with relation_url="/show-operation/" %} + {% include "ishtar/blocks/sheet_relations.html" %} + {% endwith %} </div> {% endif %} diff --git a/archaeological_operations/templates/ishtar/sheet_site.html b/archaeological_operations/templates/ishtar/sheet_site.html index 4116ee890..2ce9e7eaa 100644 --- a/archaeological_operations/templates/ishtar/sheet_site.html +++ b/archaeological_operations/templates/ishtar/sheet_site.html @@ -152,25 +152,9 @@ {% endif %} {% if display_relations %} -<h3>{% trans "Relations"%}</h3> -{% for rel in item.right_relations.all %} -{% ifchanged rel.relation_type %} -{% if forloop.counter0 %}</div>{% endif %} -<h4>{{rel.relation_type}}</h4> -<div class="row">{% endifchanged %} - <div class="col-12"> - <a href="#" onclick="load_window('/show-site/{{rel.right_record.pk|unlocalize}}/');" class="display_details"> - <i class="fa fa-info-circle" aria-hidden="true"></i> - </a> {{rel.right_record}} - </div> -{% if forloop.last %} -</div>{% endif %} -{% endfor %} -{% else %} -<div class="alert alert-info" role="alert"> - <i class="fa fa-info-circle" aria-hidden="true"></i> - {% trans "No relations" %} -</div> + {% with relation_url="/show-site/" %} + {% include "ishtar/blocks/sheet_relations.html" %} + {% endwith %} {% endif %} {% if not is_external and SHOW_GEO %} diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 5b06baba2..2d00f3455 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -887,6 +887,9 @@ class GeneralRecordRelations(Imported): class Meta: abstract = True + def __str__(self): + return f"{self.left_record} - {self.relation_type} - {self.right_record}" + @classmethod def general_types(cls): return ["relation_type"] diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index 0161aadad..5b8afc923 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -803,14 +803,26 @@ class HierarchicalType(GeneralType): has_full_label = True def full_label(self): - lbls = [self.label] - item = self - parents = [self.pk] # prevent loop - while item.parent and item.parent_id not in parents: - parents.append(item.parent_id) - item = item.parent - lbls.append(item.label) - return " > ".join(reversed(lbls)) + return " > ".join(reversed(self.get_label_hierarchy())) + + def _get_label_hierarchy(self, labels, pk, parents): + if not pk or pk in parents: + return labels + parents.add(pk) + q = self.__class__.objects.filter(pk=pk) + label, parent_id = q.values_list("label", "parent_id").all()[0] + labels.append(label) + self._get_label_hierarchy(labels, parent_id, parents) + return labels + + def get_label_hierarchy(self): + """ + Get every upward labels of the hierarchy for full label or + cached hierarchical search. + Returned in reversed order. + """ + parents = set((self.pk,)) # prevent loop + return self._get_label_hierarchy([self.label], self.parent_id, parents) @property def first_parent(self): @@ -1681,12 +1693,14 @@ class BaseHistorizedItem( All historized items are searchable and have a data json field. Historized items can be "locked" for edition. Historized items can have associated ishtar user for permission management + Cached hierarchy improve performance for hierarchal types """ IS_BASKET = False EXTERNAL_ID_KEY = "" EXTERNAL_ID_DEPENDENCIES = [] HISTORICAL_M2M = [] + CACHED_HIERARCHY = [] history_modifier = models.ForeignKey( User, diff --git a/ishtar_common/templates/ishtar/blocks/sheet_relations.html b/ishtar_common/templates/ishtar/blocks/sheet_relations.html new file mode 100644 index 000000000..e18cb5532 --- /dev/null +++ b/ishtar_common/templates/ishtar/blocks/sheet_relations.html @@ -0,0 +1,22 @@ +{% load i18n l10n %} + {% if display_relations %} + <h3>{% trans "Relations"%}</h3> + {% for rel in item.right_relations.all %} + {% ifchanged rel.relation_type %} + {% if forloop.counter0 %}</div>{% endif %} + <h4>{{rel.relation_type}}</h4> + <div class="row">{% endifchanged %} + <div class="col-12"> + <a href="#" onclick="load_window('{{relation_url}}{{rel.right_record.pk|unlocalize}}/');" class="display_details"> + <i class="fa fa-info-circle" aria-hidden="true"></i> + </a> {{rel.right_record}} + </div> + {% if forloop.last %} + </div>{% endif %} + {% endfor %} + {% else %} + <div class="alert alert-info" role="alert"> + <i class="fa fa-info-circle" aria-hidden="true"></i> + {% trans "No relations" %} + </div> + {% endif %} diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py index cb6511d9d..2135af3a6 100644 --- a/ishtar_common/utils.py +++ b/ishtar_common/utils.py @@ -2395,6 +2395,22 @@ def get_m2m_values(obj): return hist_values +def update_cached_hierarchy(obj): + updates = {} + for attr in obj.CACHED_HIERARCHY: + # for each type get all hierarchy then eliminate duplicate + cached_attr = f"cached_hierarchy_{attr}" + values = list(sorted( + set(chain(*[ + v.get_label_hierarchy() for v in getattr(obj, attr).all() + ])) + )) + if values != getattr(obj, cached_attr): + updates[cached_attr] = values + if updates: + obj.__class__.objects.filter(pk=obj.pk).update(**updates) + + def manage_m2m(obj, kwargs): obj._queue = kwargs.get("queue", settings.CELERY_DEFAULT_QUEUE) hist_values = get_m2m_values(obj) diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 878d5361e..cac99f53f 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -95,6 +95,10 @@ ENCODING = settings.ENCODING or "utf-8" HIERARCHIC_LEVELS = 5 +HIERARCHIC_CACHED_FIELDS = ( + "material_types", +) + LIST_FIELDS = { # key: hierarchic depth "conservatory_states": HIERARCHIC_LEVELS, "identifications": HIERARCHIC_LEVELS, @@ -1568,6 +1572,14 @@ def _manage_hierarchic_fields(model, dct, and_reqs): req = req[: -(len(base_suffix))] + suffix else: current_values = [val] + if k_hr in HIERARCHIC_CACHED_FIELDS: + cached_key = f"cached_hierarchy_{k_hr}__contains" + for idx, nval in enumerate(current_values): + if not reqs: + reqs = Q(**{cached_key: [nval.upper()]}) + else: + reqs |= Q(**{cached_key: [nval.upper()]}) + continue new_req = None for idx, nval in enumerate(current_values): @@ -2915,8 +2927,11 @@ def get_item( search_vector = request_items.get("search_vector", "").strip() + print("OKOK1") + n = datetime.datetime.now() # cache only for GUI search - cache_search = search_vector or "submited" in request_items + cache_search = search_vector or any( + 1 for k in request_items if k.startswith("columns[")) q_cached_count = None if cache_search: q_cached_count_attrs = { @@ -2944,7 +2959,10 @@ def get_item( if count: return items_nb - # print(str(items.values("id").query)) + print(2, datetime.datetime.now() - n) + n = datetime.datetime.now() + + print(str(items.values("id").query)) if data_type == "json-stats": stats_sum_variable = request_items.get("stats_sum_variable", None) @@ -2962,7 +2980,6 @@ def get_item( stats_modality_2, multiply=multiply, ) - table_cols = [col if col != [] else '' for col in table_cols] query_table_cols = [] for idx, cols in enumerate(table_cols): |
