diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2020-12-01 13:33:21 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-02-28 12:15:21 +0100 |
commit | 5015e03c0ceabe8ca8a7884e7178bfd246d77788 (patch) | |
tree | f828418edadbf957db52121c13575ed7cc3e068c | |
parent | 3c25d1f4ab24a1bea9b4be0757f9b8f243564954 (diff) | |
download | Ishtar-5015e03c0ceabe8ca8a7884e7178bfd246d77788.tar.bz2 Ishtar-5015e03c0ceabe8ca8a7884e7178bfd246d77788.zip |
Context records: show find inside related context records
-rw-r--r-- | archaeological_context_records/migrations/0106_views_related_cr.py | 38 | ||||
-rw-r--r-- | archaeological_context_records/models.py | 113 | ||||
-rw-r--r-- | archaeological_context_records/templates/ishtar/sheet_contextrecord.html | 8 | ||||
-rw-r--r-- | archaeological_finds/models_finds.py | 26 | ||||
-rw-r--r-- | archaeological_finds/urls.py | 4 | ||||
-rw-r--r-- | archaeological_finds/views.py | 11 | ||||
-rw-r--r-- | ishtar_common/models.py | 4 | ||||
-rw-r--r-- | ishtar_common/templatetags/window_tables.py | 2 |
8 files changed, 190 insertions, 16 deletions
diff --git a/archaeological_context_records/migrations/0106_views_related_cr.py b/archaeological_context_records/migrations/0106_views_related_cr.py new file mode 100644 index 000000000..189bc3013 --- /dev/null +++ b/archaeological_context_records/migrations/0106_views_related_cr.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.27 on 2020-12-01 13:29 +from __future__ import unicode_literals + +from django.db import migrations, models + +import archaeological_context_records.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_context_records', '0105_auto_20201117_0759'), + ] + + operations = [ + migrations.RunSQL( + archaeological_context_records.models.ContextRecordTree.DELETE_SQL + ), + migrations.RunSQL( + archaeological_context_records.models.ContextRecordTree.CREATE_SQL + ), + migrations.CreateModel( + name='ContextRecordTree', + fields=[ + ('key', models.TextField(primary_key=True, serialize=False)), + ], + options={ + 'db_table': 'context_records_tree', + 'managed': False, + }, + ), + migrations.AlterField( + model_name='relationtype', + name='logical_relation', + field=models.CharField(blank=True, choices=[('above', 'Above'), ('below', 'Below'), ('equal', 'Equal'), ('include', 'Include'), ('included', 'Is included')], max_length=10, null=True, verbose_name='Logical relation'), + ), + ] diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py index 0a7cd3d88..8a1011084 100644 --- a/archaeological_context_records/models.py +++ b/archaeological_context_records/models.py @@ -20,6 +20,7 @@ from collections import OrderedDict import uuid +from django.apps import apps from django.conf import settings from django.contrib.gis.db import models from django.contrib.postgres.indexes import GinIndex @@ -406,7 +407,7 @@ class ContextRecord(BulkUpdatedItem, DocumentItem, BaseHistorizedItem, 'cached_label': 'cached_label__icontains', 'datings__period__label': 'datings__period__label', 'operation_id': 'operation_id', - 'unit__label': "unit__label" + 'unit__label': "unit__label", } MANY_COUNTED_FIELDS = ['base_finds'] REVERSED_BOOL_FIELDS = [ @@ -786,6 +787,13 @@ class ContextRecord(BulkUpdatedItem, DocumentItem, BaseHistorizedItem, def relation_label(self): return self.label + def all_base_finds(self): + BaseFind = apps.get_model("archaeological_finds", "BaseFind") + ids = [self.id] + [ + cr.cr_id for cr in ContextRecordTree.objects.filter( + cr_parent_id=self.id)] + return BaseFind.objects.filter(context_record_id__in=ids) + @property def show_url(self): return reverse('show-contextrecord', args=[self.pk, '']) @@ -805,7 +813,7 @@ class ContextRecord(BulkUpdatedItem, DocumentItem, BaseHistorizedItem, @classmethod def get_query_owns(cls, ishtaruser): - q = cls._construct_query_own( + return cls._construct_query_own( 'operation__', Operation._get_query_owns_dicts(ishtaruser) ) | cls._construct_query_own( 'base_finds__find__basket__', @@ -814,7 +822,6 @@ class ContextRecord(BulkUpdatedItem, DocumentItem, BaseHistorizedItem, {'history_creator': ishtaruser.user_ptr}, {'operation__end_date__isnull': True} ]) - return q @classmethod def get_owns(cls, user, menu_filtr=None, limit=None, @@ -855,7 +862,8 @@ class ContextRecord(BulkUpdatedItem, DocumentItem, BaseHistorizedItem, return self.detailed_related_context_records() def _get_associated_cached_labels(self): - from archaeological_finds.models import Find, BaseFind + BaseFind = apps.get_model("archaeological_finds", "BaseFind") + Find = apps.get_model("archaeological_finds", "Find") return list(Find.objects.filter(base_finds__context_record=self).all())\ + list(BaseFind.objects.filter(context_record=self).all()) @@ -932,10 +940,11 @@ class ContextRecord(BulkUpdatedItem, DocumentItem, BaseHistorizedItem, return q.count() def detailed_related_context_records(self): - crs = [] - for cr in self.right_relations.all(): - crs.append("{} ({})".format(cr.right_record, - cr.relation_type.get_tiny_label())) + crs = [ + "{} ({})".format(cr.right_record, cr.relation_type.get_tiny_label()) + for cr in self.right_relations.all() + ] + return " & ".join(crs) def find_docs_q(self): @@ -1087,3 +1096,91 @@ class RecordRelationView(models.Model): def __str__(self): return "{} \"{}\"".format(self.relation_type, self.right_record) + + +class ContextRecordTree(models.Model): + CREATE_SQL = """ + CREATE VIEW cr_parent_relation_id AS + SELECT id + FROM archaeological_context_records_relationtype + WHERE logical_relation in ('included', 'equal'); + + CREATE VIEW context_records_tree AS + WITH RECURSIVE rel_tree AS ( + SELECT cr.id AS cr_id, + cr.id AS cr_parent_id, 1 AS level, + cr.id || '_' || cr.id || '_1' AS key + FROM archaeological_context_records_contextrecord cr + UNION ALL + SELECT rel.left_record_id AS cr_id, + rel.right_record_id AS cr_parent_id, 1 AS level, + rel.left_record_id || '_' || rel.right_record_id || '_1' AS key + FROM archaeological_context_records_recordrelations rel + WHERE rel.relation_type_id in ( + SELECT id FROM cr_parent_relation_id + ) + UNION ALL + SELECT p.cr_id AS cr_id, + rel.right_record_id AS cr_parent_id, + p.level + 1, + p.cr_id || '_' || rel.right_record_id || '_' || p.level + 1 AS key + FROM archaeological_context_records_recordrelations rel, rel_tree p + WHERE rel.left_record_id = p.cr_parent_id + AND rel.relation_type_id in ( + SELECT id FROM cr_parent_relation_id + ) + AND p.level < 10 -- prevent recursive... + ) + SELECT DISTINCT key, cr_id, cr_parent_id, level + FROM rel_tree; + + CREATE VIEW context_record_tree AS + SELECT DISTINCT y.key, y.cr_id, y.cr_parent_id + FROM (SELECT * FROM context_records_tree) y + ORDER BY y.cr_id, y.cr_parent_id; + + -- deactivate deletion, update + CREATE RULE context_records_tree_del AS + ON DELETE TO context_records_tree + DO INSTEAD DELETE FROM archaeological_context_records_contextrecord + WHERE id=NULL; + CREATE RULE context_record_tree_del AS + ON DELETE TO context_record_tree + DO INSTEAD DELETE FROM archaeological_context_records_contextrecord + WHERE id=NULL; + CREATE RULE context_records_tree_update AS + ON UPDATE TO context_records_tree + DO INSTEAD UPDATE archaeological_context_records_contextrecord + SET id=id WHERE id=NULL; + CREATE RULE context_record_tree_update AS + ON UPDATE TO context_record_tree + DO INSTEAD UPDATE archaeological_context_records_contextrecord + SET id=id WHERE id=NULL; + CREATE RULE context_records_tree_insert AS + ON INSERT TO context_records_tree + DO INSTEAD UPDATE archaeological_context_records_contextrecord + SET id=id WHERE id=NULL; + CREATE RULE context_record_tree_insert AS + ON INSERT TO context_record_tree + DO INSTEAD UPDATE archaeological_context_records_contextrecord + SET id=id WHERE id=NULL; + """ + DELETE_SQL = """ + DROP VIEW IF EXISTS context_record_tree; + DROP VIEW IF EXISTS context_records_tree; + DROP VIEW IF EXISTS cr_parent_relation_id; + """ + key = models.TextField(primary_key=True) + cr = models.ForeignKey( + "archaeological_context_records.ContextRecord", + verbose_name=_("Context record"), + related_name="context_record_tree_parent") + cr_parent = models.ForeignKey( + "archaeological_context_records.ContextRecord", + verbose_name=_("Context record parent"), + related_name="context_record_tree_child") + + class Meta: + managed = False + db_table = 'context_records_tree' + diff --git a/archaeological_context_records/templates/ishtar/sheet_contextrecord.html b/archaeological_context_records/templates/ishtar/sheet_contextrecord.html index 95f4205f8..e15a07f70 100644 --- a/archaeological_context_records/templates/ishtar/sheet_contextrecord.html +++ b/archaeological_context_records/templates/ishtar/sheet_contextrecord.html @@ -238,8 +238,12 @@ role="tabpanel" aria-labelledby="{{window_id}}-finds-tab"> {% trans "Finds" as finds %} - {% if item.base_finds.count %} - {% dynamic_table_document finds 'finds_for_ope' 'base_finds__context_record' item.pk 'TABLE_COLS_FOR_OPE' output %} + {% if item.all_base_finds.count %} + {% dynamic_table_document finds 'finds_for_cr' 'all_base_finds__context_record' item.pk 'TABLE_COLS_FOR_CR' output %} + {% else %} + <div class="alert alert-info"> + {% trans "No associated finds." %} + </div> {% endif %} {% trans "Documents from associated finds" as finds_docs %} diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index 9b1f43cb6..6bb8d042e 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -810,17 +810,31 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem, 'description', 'base_finds__context_record__town__name', 'base_finds__context_record__parcel', ] + TABLE_COLS_FOR_CR = [ + 'base_finds__cache_short_id', + 'base_finds__cache_complete_id', + 'previous_id', 'label', + 'base_finds__context_record__label', + 'cached_materials', + 'cached_periods', + 'find_number', + 'cached_object_types', + 'container__cached_label', + 'container_ref__cached_label', + 'description', + 'base_finds__context_record__town__name', + 'base_finds__context_record__parcel', ] NEW_QUERY_ENGINE = True COL_LABELS = { - 'base_finds__context_record__label': _(u"Context record"), - 'base_finds__cache_short_id': _(u"Base find - Short ID"), - 'base_finds__cache_complete_id': _(u"Base find - Complete ID"), + 'base_finds__context_record__label': _("Context record"), + 'base_finds__cache_short_id': _("Base find - Short ID"), + 'base_finds__cache_complete_id': _("Base find - Complete ID"), 'base_finds__context_record__operation__code_patriarche': _( - u"Operation (code)" + "Operation (code)" ), 'base_finds__context_record__town__name': _(u"Town"), 'base_finds__context_record__operation__common_name': _( - u"Operation (name)" + "Operation (name)" ), 'base_finds__context_record__archaeological_site__name': IshtarSiteProfile.get_default_site_label, @@ -917,6 +931,8 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem, ] BASE_REQUEST = {'downstream_treatment__isnull': True} EXTRA_REQUEST_KEYS = { + 'all_base_finds__context_record': + 'base_finds__context_record__context_record_tree_parent__cr_parent_id', 'base_finds__context_record': 'base_finds__context_record__pk', 'base_finds__context_record__archaeological_site': diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py index bab3fc6b4..7bf85d9a4 100644 --- a/archaeological_finds/urls.py +++ b/archaeological_finds/urls.py @@ -265,6 +265,10 @@ urlpatterns = [ name='get-own-find-for-ope', kwargs={'force_own': True}), url(r'get-find-for-ope/(?P<type>.+)?$', views.get_find_for_ope, name='get-find-for-ope'), + url(r'get-find-for-cr/cr/(?P<type>.+)?$', views.get_find_for_cr, + name='get-own-find-for-cr', kwargs={'force_own': True}), + url(r'get-find-for-cr/(?P<type>.+)?$', views.get_find_for_cr, + name='get-find-for-cr'), url(r'get-find-for-treatment/own/(?P<type>.+)?$', views.get_find_for_treatment, name='get-own-find-for-treatment', kwargs={'force_own': True}), diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py index c843f02d2..4e4103224 100644 --- a/archaeological_finds/views.py +++ b/archaeological_finds/views.py @@ -66,6 +66,13 @@ def get_table_cols_for_ope(): return settings.TABLE_COLS[tb_key] +def get_table_cols_for_cr(): + tb_key = (models.Find.SLUG, "TABLE_COLS_FOR_CR") + if tb_key not in settings.TABLE_COLS: + return models.Find.TABLE_COLS_FOR_CR + return settings.TABLE_COLS[tb_key] + + def get_table_cols_for_container(): table_cols = models.Find.TABLE_COLS tb_key = ("find", 'TABLE_COLS') @@ -77,6 +84,10 @@ def get_table_cols_for_container(): get_find_for_ope = get_item(models.Find, 'get_find', 'find', own_table_cols=get_table_cols_for_ope()) + +get_find_for_cr = get_item(models.Find, 'get_find', 'find', + own_table_cols=get_table_cols_for_cr()) + get_find_for_treatment = get_item( models.Find, 'get_find', 'find', own_table_cols=get_table_cols_for_ope(), base_request={}) diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 722e134ac..3a8182d2f 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -579,7 +579,9 @@ class JsonDataField(models.Model): LOGICAL_TYPES = ( ('above', _("Above")), ('below', _("Below")), - ('equal', _("Equal")) + ('equal', _("Equal")), + ('include', _("Include")), + ('included', _("Is included")), ) diff --git a/ishtar_common/templatetags/window_tables.py b/ishtar_common/templatetags/window_tables.py index a96460869..f91339cb2 100644 --- a/ishtar_common/templatetags/window_tables.py +++ b/ishtar_common/templatetags/window_tables.py @@ -51,6 +51,8 @@ ASSOCIATED_MODELS['finds'] = (Find, 'get-find', 'get-find-full') ASSOCIATED_MODELS['sites'] = (ArchaeologicalSite, 'get-site', '') ASSOCIATED_MODELS['finds_for_ope'] = ( Find, 'get-find-for-ope', 'get-find-full') +ASSOCIATED_MODELS['finds_for_cr'] = ( + Find, 'get-find-for-cr', 'get-find-full') ASSOCIATED_MODELS['finds_for_treatment'] = ( Find, 'get-find-for-treatment', 'get-find-full') ASSOCIATED_MODELS['finds_upstreamtreatments'] = ( |