diff options
-rw-r--r-- | archaeological_warehouse/migrations/0104_auto_20200925_1024.py | 31 | ||||
-rw-r--r-- | archaeological_warehouse/models.py | 102 | ||||
-rw-r--r-- | archaeological_warehouse/templates/ishtar/sheet_warehouse.html | 4 | ||||
-rw-r--r-- | ishtar_common/models.py | 9 |
4 files changed, 106 insertions, 40 deletions
diff --git a/archaeological_warehouse/migrations/0104_auto_20200925_1024.py b/archaeological_warehouse/migrations/0104_auto_20200925_1024.py new file mode 100644 index 000000000..f82b25b19 --- /dev/null +++ b/archaeological_warehouse/migrations/0104_auto_20200925_1024.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.27 on 2020-09-25 10:24 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_warehouse', '0103_auto_container_views'), + ] + + operations = [ + migrations.AddField( + model_name='historicalwarehouse', + name='max_division_number', + field=models.IntegerField(default=0, help_text='Automatically generated', verbose_name='Number maximum of division'), + ), + migrations.AddField( + model_name='warehouse', + name='max_division_number', + field=models.IntegerField(default=0, help_text='Automatically generated', verbose_name='Number maximum of division'), + ), + migrations.AlterField( + model_name='container', + name='container_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='containers', to='archaeological_warehouse.ContainerType', verbose_name='Container type'), + ), + ] diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py index 096ff0b6c..870f10d95 100644 --- a/archaeological_warehouse/models.py +++ b/archaeological_warehouse/models.py @@ -25,7 +25,7 @@ from django.conf import settings from django.contrib.gis.db import models from django.contrib.postgres.indexes import GinIndex from django.core.urlresolvers import reverse -from django.db.models import Q, Max +from django.db.models import Q, Max, Count from django.db.models.signals import post_save, post_delete, m2m_changed from django.template.defaultfilters import slugify from ishtar_common.utils import ugettext_lazy as _, pgettext_lazy @@ -131,6 +131,9 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, external_id = models.TextField(_("External ID"), blank=True, null=True) auto_external_id = models.BooleanField( _("External ID is set automatically"), default=False) + max_division_number = models.IntegerField( + _("Number maximum of division"), default=0, + help_text=_("Automatically generated")) SUB_ADDRESSES = ["organization", "person_in_charge"] class Meta: @@ -190,10 +193,11 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, @property def location_types(self): + if not self.max_division_number: + return [] return [ - wd.container_type.label - for wd in WarehouseDivisionLink.objects.filter( - warehouse=self).order_by('order').all() if wd.container_type + "{} {}".format(_("Division"), idx + 1) + for idx in range(self.max_division_number) ] @property @@ -227,33 +231,53 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, def _get_divisions(self, current_path, remaining_division, depth=0): if not remaining_division: return [current_path] - current_division = remaining_division.pop(0) + remaining_division.pop(0) - base_q = Container.objects.filter( - container_type=current_division, - location=self - ) - q = base_q - for div, ref in current_path: - q = base_q.filter( - parent__container_type=div, - parent__reference=ref) + query_location = "location" + for __ in range(depth): + query_location = "parent__" + query_location + base_q = Container.objects.filter(**{query_location: self}) + + q = base_q.annotate(nb_children=Count("children__id")).exclude( + nb_children=0) + + if not current_path: + q = q.annotate(nb_parent=Count("parent__id")).filter( + nb_parent=0) + + for idx, p in enumerate(reversed(current_path)): + parent_id, __ = p + key = "parent__" * (idx + 1) + "id" + q = q.filter(**{key: parent_id}) res = [] - old_ref = None + old_ref, ct = None, None if not q.count(): return [current_path] - for ref in q.values('reference').order_by('reference').all(): - if ref['reference'] == old_ref: + q = q.values( + 'id', 'reference', 'container_type__label', 'container_type_id' + ).order_by('container_type__label', 'reference') + + DIVISION_TEMPLATE = """<a class="display_details" + href="#" onclick="load_window('/show-container/{id}/')"> + <i class="fa fa-info-circle" aria-hidden="true"></i></a> + {container} {ref}""" + for ref in q.all(): + if ref['reference'] == old_ref and \ + ref["container_type__label"] == ct: continue old_ref = ref['reference'] + ct = ref["container_type__label"] cpath = current_path[:] - cpath.append((current_division, ref['reference'])) + lbl = DIVISION_TEMPLATE.format( + id=ref["id"], container=ref["container_type__label"], + ref=ref['reference']) + cpath.append((ref["id"], lbl)) remaining_division = list( ContainerType.objects.filter( containers__parent__reference=ref['reference'], - containers__parent__container_type=current_division, - containers__location=self, - stationary=True).distinct()) + containers__parent__container_type_id=ref[ + "container_type_id"], + containers__location=self).distinct()) for r in self._get_divisions(cpath, remaining_division[:], depth + 1): res.append(r) @@ -263,7 +287,7 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, def available_division_tuples(self): """ :return: ordered list of available paths. Each path is a list of - tuple with the container type and the reference. + tuple with the container type and the full reference. """ top_divisions = list( ContainerType.objects.filter( @@ -278,20 +302,16 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, paths = self.available_division_tuples[:] for path in paths: cpath = [] - cdiv_path = [] - for division, ref in path: - cpath.append(ref) - cdiv_path.append(division) + for container_id, lbl in path: + cpath.append((container_id, lbl)) if tuple(cpath) in res: continue q = model.objects - reversed_cdiv = list(reversed(cdiv_path)) - for idx, new_ref in enumerate(reversed(cpath)): - division = reversed_cdiv[idx] + for idx, p in enumerate(reversed(cpath)): + container_id, __ = p div_key = division_key + "parent__" * idx attrs = { - div_key + "container_type": division, - div_key + "reference": new_ref + div_key + "id": container_id } q = q.filter(**attrs) if count_filter: @@ -300,16 +320,18 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, res = [(k, res[k]) for k in res] final_res, current_res, depth = [], [], 1 - len_divisions = WarehouseDivisionLink.objects.filter( - warehouse=self).count() + len_divisions = self.max_division_number for path, nb in sorted(res, key=lambda x: (len(x[0]), x[0])): + if len(path) > len_divisions: + continue if depth != len(path): final_res.append(current_res[:]) current_res = [] depth = len(path) if path[-1] == '-': continue - path = list(path) + ['' for __ in range(len_divisions - len(path))] + path = [k[1] for k in path] + path = path + ['' for __ in range(len_divisions - len(path))] current_res.append((path, nb)) final_res.append(current_res[:]) return final_res @@ -1173,10 +1195,22 @@ class Container(DocumentItem, Merge, LightHistorizedItem, QRCodeItem, GeoItem, else: self.index = 1 + def _update_warehouse_max_division(self): + number = 0 + parent = self.parent_id + while parent: + number += 1 + parent = Container.objects.filter(pk=parent).values_list( + "parent_id")[0][0] + if number > self.location.max_division_number: + self.location.max_division_number = number + self.location.save() + def save(self, *args, **kwargs): self.pre_save() super(Container, self).save(*args, **kwargs) self._change_child_location(self) + self._update_warehouse_max_division() updated = False if not self.index: diff --git a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html index dcaa12215..b6f93cc57 100644 --- a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html +++ b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html @@ -144,7 +144,7 @@ <tbody> {% for item in items %} <tr> - {% for local in item.0 %}<td>{{local}}</td>{% endfor %} + {% for local in item.0 %}<td>{{local|safe}}</td>{% endfor %} <td class="text-right">{{item.1}}</td> </tr> {% endfor %} @@ -175,7 +175,7 @@ <tbody> {% for item in items %} <tr> - {% for local in item.0 %}<td class="text-center">{{local}}</td>{% endfor %} + {% for local in item.0 %}<td>{{local|safe}}</td>{% endfor %} <td class="text-center">{{item.1}}</td> </tr> {% endfor %} diff --git a/ishtar_common/models.py b/ishtar_common/models.py index d26a64e38..2d3320e2e 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -3406,15 +3406,16 @@ class DashboardFormItem(object): def _get_or_set_stats(self, funcname, update, timeout=settings.CACHE_TIMEOUT, expected_type=None): + values = {} + from_cache = False model_name = self._meta.app_label + "." + self._meta.model_name sc, __ = StatsCache.objects.get_or_create( model=model_name, model_pk=self.pk ) now = datetime.datetime.now() - values = {} - from_cache = False - if not update and sc.values and funcname in sc.values and ( - sc.updated + datetime.timedelta(seconds=timeout)) > now: + if not settings.DEBUG and ( + not update and sc.values and funcname in sc.values and ( + sc.updated + datetime.timedelta(seconds=timeout)) > now): values = sc.values from_cache = True if funcname not in values: |