summaryrefslogtreecommitdiff
path: root/archaeological_warehouse
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2017-04-07 12:16:37 +0200
committerÉtienne Loks <etienne.loks@iggdrasil.net>2017-04-07 12:16:37 +0200
commit6b87dae76d931ead9838db1f66a6ff1fe1cc1dd1 (patch)
tree0a2273d9db0499214e18a041987339eac9d20ac9 /archaeological_warehouse
parent75730c0ac55c935c03e58977405b4b8a2233595d (diff)
parent3d8b8d86f01ecb9b37e24e25fd15500b8f4fb2a0 (diff)
downloadIshtar-6b87dae76d931ead9838db1f66a6ff1fe1cc1dd1.tar.bz2
Ishtar-6b87dae76d931ead9838db1f66a6ff1fe1cc1dd1.zip
Merge branch 'master' into v0.9
Diffstat (limited to 'archaeological_warehouse')
-rw-r--r--archaeological_warehouse/forms.py2
-rw-r--r--archaeological_warehouse/locale/django.pot112
-rw-r--r--archaeological_warehouse/models.py114
-rw-r--r--archaeological_warehouse/templates/ishtar/sheet_warehouse.html44
-rw-r--r--archaeological_warehouse/templates/ishtar/wizard/wizard_containerlocalisation.html4
-rw-r--r--archaeological_warehouse/templates/ishtar/wizard/wizard_warehouse_divisions.html11
-rw-r--r--archaeological_warehouse/wizards.py14
7 files changed, 254 insertions, 47 deletions
diff --git a/archaeological_warehouse/forms.py b/archaeological_warehouse/forms.py
index 3dd38d747..94e31b759 100644
--- a/archaeological_warehouse/forms.py
+++ b/archaeological_warehouse/forms.py
@@ -100,7 +100,7 @@ class WarehouseForm(ManageOldType, forms.Form):
associated_models = {'warehouse_type': models.WarehouseType,
'person_in_charge': Person}
- name = forms.CharField(label=_(u"Name"), max_length=40,
+ name = forms.CharField(label=_(u"Name"), max_length=200,
validators=[name_validator])
warehouse_type = forms.ChoiceField(label=_(u"Warehouse type"),
choices=[])
diff --git a/archaeological_warehouse/locale/django.pot b/archaeological_warehouse/locale/django.pot
index d5d6e9b63..eb88b13af 100644
--- a/archaeological_warehouse/locale/django.pot
+++ b/archaeological_warehouse/locale/django.pot
@@ -4,19 +4,20 @@
# Étienne Loks <etienne.loks at peacefrogs net>, 2010-2011.
# Valérie-Emma Leroux <emma@iggdrasil.net>, 2016. #zanata
# Valérie-Emma Leroux <emma@iggdrasil.net>, 2017. #zanata
+# Étienne Loks <etienne.loks@iggdrasil.net>, 2017. #zanata
msgid ""
msgstr ""
-#: forms.py:36 forms.py:99 ishtar_menu.py:40 models.py:63 models.py:103
+#: forms.py:36 forms.py:99 ishtar_menu.py:40 models.py:65 models.py:204
#: templates/ishtar/sheet_warehouse.html:4
msgid "Warehouse"
msgstr ""
-#: forms.py:45 forms.py:50 models.py:281
+#: forms.py:45 forms.py:50 models.py:389
msgid "Division"
msgstr ""
-#: forms.py:52 models.py:127
+#: forms.py:52 models.py:228
msgid "Order"
msgstr ""
@@ -24,15 +25,15 @@ msgstr ""
msgid "There are identical divisions."
msgstr ""
-#: forms.py:70 models.py:53
+#: forms.py:70 models.py:55
msgid "Divisions"
msgstr ""
-#: forms.py:74 forms.py:103 models.py:45 models.py:100
+#: forms.py:74 forms.py:103 models.py:47 models.py:201
msgid "Name"
msgstr ""
-#: forms.py:75 forms.py:105 models.py:36 models.py:47
+#: forms.py:75 forms.py:105 models.py:38 models.py:49
msgid "Warehouse type"
msgstr ""
@@ -44,11 +45,11 @@ msgstr ""
msgid "Warehouse search"
msgstr ""
-#: forms.py:108 models.py:50
+#: forms.py:108 models.py:52
msgid "Person in charge"
msgstr ""
-#: forms.py:114 forms.py:184 models.py:51 models.py:181
+#: forms.py:114 forms.py:184 models.py:53 models.py:282
msgid "Comment"
msgstr ""
@@ -80,16 +81,16 @@ msgstr ""
msgid "Would you like to delete this warehouse?"
msgstr ""
-#: forms.py:158 models.py:192 models.py:279
+#: forms.py:158 models.py:293 models.py:386
#: templates/ishtar/sheet_container.html:4
msgid "Container"
msgstr ""
-#: forms.py:163 forms.py:237 models.py:142
+#: forms.py:163 forms.py:237 models.py:243
msgid "Ref."
msgstr ""
-#: forms.py:164 forms.py:236 models.py:145 models.py:179
+#: forms.py:164 forms.py:236 models.py:246 models.py:280
msgid "Container type"
msgstr ""
@@ -97,7 +98,7 @@ msgstr ""
msgid "Current location (warehouse)"
msgstr ""
-#: forms.py:172 models.py:176
+#: forms.py:172 models.py:277
msgid "Responsible warehouse"
msgstr ""
@@ -148,7 +149,7 @@ msgstr ""
msgid "Packaged finds"
msgstr ""
-#: forms.py:280 models.py:182
+#: forms.py:280 models.py:283
msgid "Localisation"
msgstr ""
@@ -176,115 +177,116 @@ msgstr ""
msgid "Deletion"
msgstr ""
-#: ishtar_menu.py:57 models.py:193 templates/ishtar/sheet_warehouse.html:20
+#: ishtar_menu.py:57 models.py:294 templates/ishtar/sheet_warehouse.html:20
+#: templates/ishtar/sheet_warehouse.html:53
msgid "Containers"
msgstr ""
-#: models.py:37
+#: models.py:39
msgid "Warehouse types"
msgstr ""
-#: models.py:56 models.py:187
+#: models.py:58 models.py:288
msgid "External ID"
msgstr ""
-#: models.py:58 models.py:189
+#: models.py:60 models.py:290
msgid "External ID is set automatically"
msgstr ""
-#: models.py:64
+#: models.py:66
msgid "Warehouses"
msgstr ""
-#: models.py:66
+#: models.py:68
msgid "Can view all Warehouses"
msgstr ""
-#: models.py:67
+#: models.py:69
msgid "Can view own Warehouse"
msgstr ""
-#: models.py:68
+#: models.py:70
msgid "Can add own Warehouse"
msgstr ""
-#: models.py:69
+#: models.py:71
msgid "Can change own Warehouse"
msgstr ""
-#: models.py:70
+#: models.py:72
msgid "Can delete own Warehouse"
msgstr ""
-#: models.py:102
+#: models.py:203
msgid "Description"
msgstr ""
-#: models.py:107 models.py:108
+#: models.py:208 models.py:209
msgid "Collection"
msgstr ""
-#: models.py:117
+#: models.py:218
msgid "Warehouse division type"
msgstr ""
-#: models.py:118
+#: models.py:219
msgid "Warehouse division types"
msgstr ""
-#: models.py:138
+#: models.py:239
msgid "Length (mm)"
msgstr ""
-#: models.py:139
+#: models.py:240
msgid "Width (mm)"
msgstr ""
-#: models.py:140
+#: models.py:241
msgid "Height (mm)"
msgstr ""
-#: models.py:141
+#: models.py:242
msgid "Volume (l)"
msgstr ""
-#: models.py:146
+#: models.py:247
msgid "Container types"
msgstr ""
-#: models.py:165
+#: models.py:266
msgid "Location - index"
msgstr ""
-#: models.py:166
+#: models.py:267
msgid "Precise localisation"
msgstr ""
-#: models.py:167
+#: models.py:268
msgid "Type"
msgstr ""
-#: models.py:173
+#: models.py:274
msgid "Location (warehouse)"
msgstr ""
-#: models.py:180
+#: models.py:281
msgid "Container ref."
msgstr ""
-#: models.py:184
+#: models.py:285
msgid "Cached location"
msgstr ""
-#: models.py:282
+#: models.py:390
msgid "Reference"
msgstr ""
-#: models.py:285
+#: models.py:393
msgid "Container localisation"
msgstr ""
-#: models.py:286
+#: models.py:394
msgid "Container localisations"
msgstr ""
@@ -320,8 +322,34 @@ msgstr ""
msgid "Attached containers"
msgstr ""
-#: templates/ishtar/wizard/wizard_containerlocalisation.html:6
+#: templates/ishtar/sheet_warehouse.html:29
+msgid "Statistics"
+msgstr ""
+
+#: templates/ishtar/sheet_warehouse.html:30
+msgid "Theses number are updated hourly"
+msgstr ""
+
+#: templates/ishtar/sheet_warehouse.html:32
+msgid "Finds"
+msgstr ""
+
+#: templates/ishtar/sheet_warehouse.html:39
+msgid "Finds by location in the warehouse"
+msgstr ""
+
+#: templates/ishtar/sheet_warehouse.html:59
+msgid "Containers by location in the warehouse"
+msgstr ""
+
+#: templates/ishtar/wizard/wizard_containerlocalisation.html:8
msgid ""
"No division set for this warehouse. Define at least one division to localise "
"containers in this warehouse."
msgstr ""
+
+#: templates/ishtar/wizard/wizard_warehouse_divisions.html:8
+msgid ""
+"Containers with localisation are associated to this warehouse. You cannot "
+"change divisions."
+msgstr ""
diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py
index d1918f46a..96814339c 100644
--- a/archaeological_warehouse/models.py
+++ b/archaeological_warehouse/models.py
@@ -21,6 +21,7 @@ import datetime
from django.conf import settings
from django.contrib.gis.db import models
+from django.db.models import Q, Count
from django.db.models.signals import post_save, post_delete
from django.template.defaultfilters import slugify
from django.utils.translation import ugettext_lazy as _, ugettext
@@ -28,7 +29,8 @@ from django.utils.translation import ugettext_lazy as _, ugettext
from ishtar_common.utils import cached_label_changed
from ishtar_common.models import GeneralType, get_external_id, \
- LightHistorizedItem, OwnPerms, Address, Person, post_save_cache, ImageModel
+ LightHistorizedItem, OwnPerms, Address, Person, post_save_cache, \
+ ImageModel, DashboardFormItem
class WarehouseType(GeneralType):
@@ -40,7 +42,7 @@ post_save.connect(post_save_cache, sender=WarehouseType)
post_delete.connect(post_save_cache, sender=WarehouseType)
-class Warehouse(Address, OwnPerms):
+class Warehouse(Address, DashboardFormItem, OwnPerms):
SHOW_URL = 'show-warehouse'
name = models.CharField(_(u"Name"), max_length=200)
warehouse_type = models.ForeignKey(WarehouseType,
@@ -78,6 +80,105 @@ class Warehouse(Address, OwnPerms):
return datetime.date.today().strftime('%Y-%m-%d') + '-' + \
slugify(unicode(self))
+ @classmethod
+ def get_query_owns(cls, user):
+ return Q(person_in_charge__ishtaruser=user.ishtaruser)
+
+ @property
+ def number_of_finds(self):
+ from archaeological_finds.models import Find
+ return Find.objects.filter(container__responsible=self).count()
+
+ @property
+ def number_of_finds_hosted(self):
+ from archaeological_finds.models import Find
+ return Find.objects.filter(container__location=self).count()
+
+ @property
+ def number_of_containers(self):
+ return Container.objects.filter(location=self).count()
+
+ def _get_divisions(self, current_path, remaining_division, depth=0):
+ if not remaining_division:
+ return [current_path]
+ current_division = remaining_division.pop(0)
+ q = ContainerLocalisation.objects.filter(
+ division=current_division,
+ )
+ for div, ref in current_path:
+ q = q.filter(
+ container__division__division=div,
+ container__division__reference=ref
+ )
+ res = []
+ old_ref = None
+ for ref in q.values('reference').order_by('reference').all():
+ if ref['reference'] == old_ref:
+ continue
+ old_ref = ref['reference']
+ cpath = current_path[:]
+ cpath.append((current_division, ref['reference']))
+ for r in self._get_divisions(cpath, remaining_division[:],
+ depth + 1):
+ res.append(r)
+ return res
+
+ @property
+ def available_division_tuples(self):
+ """
+ :return: ordered list of available paths. Each path is a list of
+ tuple with the WarehouseDivisionLink and the reference.
+ """
+ divisions = list(
+ WarehouseDivisionLink.objects.filter(warehouse=self
+ ).order_by('order').all())
+ return self._get_divisions([], divisions)
+
+ def _number_of_items_by_place(self, model, division_key='division'):
+ res = {}
+ paths = self.available_division_tuples[:]
+ for path in paths:
+ q = model.objects
+ cpath = []
+ for division, ref in path:
+ lbl = u"{} {}".format(division.division, ref)
+ cpath.append(lbl)
+ attrs = {
+ division_key + "__division": division,
+ division_key + "__reference": ref
+ }
+ q = q.filter(**attrs)
+ if tuple(cpath) not in res:
+ res[tuple(cpath)] = q.count()
+ res = [(k, res[k]) for k in res]
+ final_res, current_res, depth = [], [], 1
+ for path, nb in sorted(res, key=lambda x: (len(x[0]), x[0])):
+ if depth != len(path):
+ final_res.append(current_res[:])
+ current_res = []
+ depth = len(path)
+ current_res.append((u" | ".join(path), nb))
+ final_res.append(current_res[:])
+ return final_res
+
+ def _number_of_finds_by_place(self):
+ from archaeological_finds.models import Find
+ return self._number_of_items_by_place(
+ Find, division_key='container__division')
+
+ @property
+ def number_of_finds_by_place(self, update=False):
+ return self._get_or_set_stats('_number_of_finds_by_place', update,
+ settings.CACHE_SMALLTIMEOUT)
+
+ def _number_of_containers_by_place(self):
+ return self._number_of_items_by_place(Container)
+
+ @property
+ def number_of_containers_by_place(self, update=False):
+ return self._get_or_set_stats('_number_of_containers_by_place', update,
+ settings.CACHE_SMALLTIMEOUT)
+
def save(self, *args, **kwargs):
super(Warehouse, self).save(*args, **kwargs)
for container in self.containers.all():
@@ -208,6 +309,12 @@ class Container(LightHistorizedItem, ImageModel):
cached_label = u" - ".join(items)
return cached_label
+ @classmethod
+ def get_query_owns(cls, user):
+ return Q(history_creator=user) | \
+ Q(location__person_in_charge__ishtaruser=user.ishtaruser) | \
+ Q(responsible__person_in_charge__ishtaruser=user.ishtaruser)
+
@property
def associated_filename(self):
filename = datetime.date.today().strftime('%Y-%m-%d')
@@ -276,7 +383,8 @@ post_save.connect(cached_label_changed, sender=Container)
class ContainerLocalisation(models.Model):
- container = models.ForeignKey(Container, verbose_name=_(u"Container"))
+ container = models.ForeignKey(Container, verbose_name=_(u"Container"),
+ related_name='division')
division = models.ForeignKey(WarehouseDivisionLink,
verbose_name=_(u"Division"))
reference = models.CharField(_(u"Reference"), max_length=200, default='')
diff --git a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html
index c31fc93b4..eb31392e4 100644
--- a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html
+++ b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html
@@ -26,4 +26,48 @@
{% dynamic_table_document '' 'containers' 'responsible' item.pk 'TABLE_COLS' output %}
{% endif %}
+<h3>{% trans "Statistics" %}</h3>
+<small class="centered"><em>{% trans "These numbers are updated hourly" %}</em></small>
+
+<h4>{% trans "Finds" %}</h4>
+<ul class='form-flex'>
+ {% field_li "Number of attached finds" item.number_of_finds %}
+ {% field_li "Number of hosted finds" item.number_of_finds_hosted %}
+</ul>
+
+{% if item.number_of_finds_by_place %}
+<h4>{% trans "Finds by location in the warehouse" %}</h4>
+<ul class='form-flex'>
+ {% for items in item.number_of_finds_by_place %}
+ <li>
+ <table class='clean-table small'>
+ {% for item in items %}
+ <tr><th>{{item.0}}</th><td>{{item.1}}</td></tr>
+ {% endfor %}
+ </table>
+ </li>
+ {% endfor %}
+</ul>
+{% endif %}
+
+<h4>{% trans "Containers" %}</h4>
+<ul class='form-flex'>
+ {% field_li "Number of containers" item.number_of_containers %}
+</ul>
+
+{% if item.number_of_containers_by_place %}
+<h4>{% trans "Containers by location in the warehouse" %}</h4>
+<ul class='form-flex'>
+ {% for items in item.number_of_containers_by_place %}
+ <li>
+ <table class='clean-table small'>
+ {% for item in items %}
+ <tr><th>{{item.0}}</th><td>{{item.1}}</td></tr>
+ {% endfor %}
+ </table>
+ </li>
+ {% endfor %}
+</ul>
+{% endif %}
+
{% endblock %}
diff --git a/archaeological_warehouse/templates/ishtar/wizard/wizard_containerlocalisation.html b/archaeological_warehouse/templates/ishtar/wizard/wizard_containerlocalisation.html
index 2e817ff41..41be02748 100644
--- a/archaeological_warehouse/templates/ishtar/wizard/wizard_containerlocalisation.html
+++ b/archaeological_warehouse/templates/ishtar/wizard/wizard_containerlocalisation.html
@@ -3,7 +3,9 @@
{% load url from future %}
{% block form_head %}
{% if not wizard.form.fields %}
-<p class='warning'>{% trans "No division set for this warehouse. Define at least one division to localise containers in this warehouse." %}<br/>
+<p class="alert">
+ <i class="fa fa-exclamation-triangle"></i>
+ {% trans "No division set for this warehouse. Define at least one division to localise containers in this warehouse." %}<br/>
{{wizard.form.warehouse}}
<a href="{% url 'warehouse_modify' wizard.form.warehouse.pk %}">
<span class="fa-stack fa-lg">
diff --git a/archaeological_warehouse/templates/ishtar/wizard/wizard_warehouse_divisions.html b/archaeological_warehouse/templates/ishtar/wizard/wizard_warehouse_divisions.html
new file mode 100644
index 000000000..83dbfc0fe
--- /dev/null
+++ b/archaeological_warehouse/templates/ishtar/wizard/wizard_warehouse_divisions.html
@@ -0,0 +1,11 @@
+{% extends "ishtar/wizard/default_wizard.html" %}
+{% load i18n %}
+{% load url from future %}
+{% block form_head %}
+{% if wizard.form.readonly %}
+<p class="alert">
+ <i class="fa fa-exclamation-triangle"></i>
+ {% trans "Containers with localisation are associated to this warehouse. You cannot change divisions." %}<br/>
+</p>
+{% endif %}
+{% endblock %}
diff --git a/archaeological_warehouse/wizards.py b/archaeological_warehouse/wizards.py
index 9ecc16b3f..571e56b1b 100644
--- a/archaeological_warehouse/wizards.py
+++ b/archaeological_warehouse/wizards.py
@@ -64,6 +64,20 @@ class WarehouseModificationWizard(Wizard):
model = models.Warehouse
modification = True
wizard_done_window = reverse_lazy('show-warehouse')
+ wizard_templates = {
+ 'divisions-warehouse_modification':
+ 'ishtar/wizard/wizard_warehouse_divisions.html',
+ }
+
+ def get_form_kwargs(self, step=None):
+ kwargs = super(WarehouseModificationWizard, self).get_form_kwargs(step)
+ if step == "divisions-warehouse_modification":
+ current_warehouse = self.get_current_object()
+ q = models.ContainerLocalisation.objects.filter(
+ division__warehouse=current_warehouse)
+ if q.count():
+ kwargs['readonly'] = True
+ return kwargs
class WarehouseDeletionWizard(DeletionWizard):