summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--archaeological_finds/migrations/0104_findinsidecontainer.py26
-rw-r--r--archaeological_finds/models.py4
-rw-r--r--archaeological_finds/models_finds.py168
-rw-r--r--archaeological_finds/templates/ishtar/sheet_find.html16
-rw-r--r--archaeological_finds/tests.py164
-rw-r--r--archaeological_finds/tests/import_loca_test.json305
-rw-r--r--archaeological_finds/urls.py5
-rw-r--r--archaeological_finds/views.py11
-rw-r--r--archaeological_warehouse/forms.py15
-rw-r--r--archaeological_warehouse/management/commands/migrate_to_new_container_management.py13
-rw-r--r--archaeological_warehouse/migrations/0103_auto_20200403_1638.py41
-rw-r--r--archaeological_warehouse/migrations/0104_auto_container_views.py26
-rw-r--r--archaeological_warehouse/models.py284
-rw-r--r--archaeological_warehouse/templates/ishtar/sheet_container.html63
-rw-r--r--ishtar_common/fixtures/initial_importtypes-fr.json7
-rw-r--r--ishtar_common/templatetags/link_to_window.py4
-rw-r--r--ishtar_common/templatetags/window_field.py7
-rw-r--r--ishtar_common/templatetags/window_tables.py5
-rw-r--r--ishtar_common/utils.py3
19 files changed, 1033 insertions, 134 deletions
diff --git a/archaeological_finds/migrations/0104_findinsidecontainer.py b/archaeological_finds/migrations/0104_findinsidecontainer.py
new file mode 100644
index 000000000..20a91c9cf
--- /dev/null
+++ b/archaeological_finds/migrations/0104_findinsidecontainer.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.27 on 2020-04-03 16:33
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_finds', '0103_auto_20200129_1944'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='FindInsideContainer',
+ fields=[
+ ('find', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='inside_container', serialize=False, to='archaeological_finds.Find', verbose_name='Find')),
+ ],
+ options={
+ 'db_table': 'find_inside_container',
+ 'managed': False,
+ },
+ ),
+ ]
diff --git a/archaeological_finds/models.py b/archaeological_finds/models.py
index ad349e315..53fa35c40 100644
--- a/archaeological_finds/models.py
+++ b/archaeological_finds/models.py
@@ -3,7 +3,7 @@ from archaeological_finds.models_finds import MaterialType, ConservatoryState, \
FindBasket, Find, Property, BatchType, BFBulkView, FBulkView, \
FirstBaseFindView, AlterationType, AlterationCauseType, \
TreatmentEmergencyType, TreatmentType, CommunicabilityType, \
- MaterialTypeQualityType, ObjectTypeQualityType
+ MaterialTypeQualityType, ObjectTypeQualityType, FindInsideContainer
from archaeological_finds.models_treatments import Treatment, \
AbsFindTreatments, FindUpstreamTreatments, FindDownstreamTreatments, \
FindTreatments, TreatmentFile, TreatmentFileType, \
@@ -18,4 +18,4 @@ __all__ = ['MaterialType', 'ConservatoryState', 'IntegrityType', 'CheckedType',
'FindNonModifTreatments', 'FindDownstreamTreatments',
'FindTreatments', 'TreatmentFile', 'TreatmentFileType',
'CommunicabilityType', 'MaterialTypeQualityType',
- 'ObjectTypeQualityType']
+ 'ObjectTypeQualityType', 'FindInsideContainer']
diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py
index 0260200e9..119a2022a 100644
--- a/archaeological_finds/models_finds.py
+++ b/archaeological_finds/models_finds.py
@@ -785,8 +785,7 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem,
'base_finds__context_record__label',
'cached_materials', 'cached_object_types',
'cached_periods',
- 'container__cached_label',
- 'container_ref__cached_label']
+ 'container__cached_label']
if settings.COUNTRY == 'fr':
TABLE_COLS.insert(
3, 'base_finds__context_record__operation__code_patriarche')
@@ -2294,7 +2293,8 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem,
def localisation_9(self):
return self.get_localisation(8)
- def set_localisation(self, place, context, value, is_ref=False):
+ def set_localisation(self, place, context, value, is_ref=False,
+ static=False):
"""
Get localisation reference in the warehouse
@@ -2302,6 +2302,7 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem,
:param context: context of the request - not used
:param value: localisation value
:param is_ref: if true - reference container else current container
+ :param static: if true: do not create new container
:return: None
"""
if is_ref:
@@ -2314,19 +2315,17 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem,
return
if is_ref:
raise ImporterError(
- _(u"No reference container have been set - the "
- u"localisation cannot be set."))
+ _("No reference container have been set - the "
+ "localisation cannot be set."))
else:
raise ImporterError(
- _(u"No container have been set - the localisation cannot "
- u"be set."))
+ _("No container have been set - the localisation cannot "
+ "be set."))
- localisation = container.set_localisation(place, value)
- if value and value != '-' and not localisation:
- raise ImporterError(
- str(_(u"The division number {} have not been set "
- u"for the warehouse {}.")).format(
- place + 1, container.location))
+ localisation, error = container.set_localisation(
+ place, value, static=static, return_errors=True)
+ if error:
+ raise ImporterError(error)
@post_importer_action
def set_reference_localisation_1(self, context, value):
@@ -2374,6 +2373,60 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem,
set_reference_localisation_9.post_save = True
@post_importer_action
+ def set_reference_static_localisation_1(self, context, value):
+ return self.set_localisation(0, context, value, is_ref=True,
+ static=True)
+ set_reference_static_localisation_1.post_save = True
+
+ @post_importer_action
+ def set_reference_static_localisation_2(self, context, value):
+ return self.set_localisation(1, context, value, is_ref=True,
+ static=True)
+ set_reference_static_localisation_2.post_save = True
+
+ @post_importer_action
+ def set_reference_static_localisation_3(self, context, value):
+ return self.set_localisation(2, context, value, is_ref=True,
+ static=True)
+ set_reference_static_localisation_3.post_save = True
+
+ @post_importer_action
+ def set_reference_static_localisation_4(self, context, value):
+ return self.set_localisation(3, context, value, is_ref=True,
+ static=True)
+ set_reference_static_localisation_4.post_save = True
+
+ @post_importer_action
+ def set_reference_static_localisation_5(self, context, value):
+ return self.set_localisation(4, context, value, is_ref=True,
+ static=True)
+ set_reference_static_localisation_5.post_save = True
+
+ @post_importer_action
+ def set_reference_static_localisation_6(self, context, value):
+ return self.set_localisation(5, context, value, is_ref=True,
+ static=True)
+ set_reference_static_localisation_6.post_save = True
+
+ @post_importer_action
+ def set_reference_static_localisation_7(self, context, value):
+ return self.set_localisation(6, context, value, is_ref=True,
+ static=True)
+ set_reference_static_localisation_7.post_save = True
+
+ @post_importer_action
+ def set_reference_static_localisation_8(self, context, value):
+ return self.set_localisation(7, context, value, is_ref=True,
+ static=True)
+ set_reference_static_localisation_8.post_save = True
+
+ @post_importer_action
+ def set_reference_static_localisation_9(self, context, value):
+ return self.set_localisation(8, context, value, is_ref=True,
+ static=True)
+ set_reference_static_localisation_9.post_save = True
+
+ @post_importer_action
def set_localisation_1(self, context, value):
return self.set_localisation(0, context, value)
set_localisation_1.post_save = True
@@ -2418,6 +2471,51 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem,
return self.set_localisation(8, context, value)
set_localisation_9.post_save = True
+ @post_importer_action
+ def set_static_localisation_1(self, context, value):
+ return self.set_localisation(0, context, value, static=True)
+ set_static_localisation_1.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_2(self, context, value):
+ return self.set_localisation(1, context, value, static=True)
+ set_static_localisation_2.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_3(self, context, value):
+ return self.set_localisation(2, context, value, static=True)
+ set_static_localisation_3.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_4(self, context, value):
+ return self.set_localisation(3, context, value, static=True)
+ set_static_localisation_4.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_5(self, context, value):
+ return self.set_localisation(4, context, value, static=True)
+ set_static_localisation_5.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_6(self, context, value):
+ return self.set_localisation(5, context, value, static=True)
+ set_static_localisation_6.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_7(self, context, value):
+ return self.set_localisation(6, context, value, static=True)
+ set_static_localisation_7.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_8(self, context, value):
+ return self.set_localisation(7, context, value, static=True)
+ set_static_localisation_8.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_9(self, context, value):
+ return self.set_localisation(8, context, value, static=True)
+ set_static_localisation_9.post_save = True
+
def generate_index(self):
"""
Generate index based on operation or context record (based on
@@ -2555,6 +2653,50 @@ m2m_changed.connect(base_find_find_changed, sender=Find.base_finds.through)
m2m_changed.connect(document_attached_changed,
sender=Find.documents.through)
+
+class FindInsideContainer(models.Model):
+ CREATE_SQL = """
+ CREATE VIEW find_inside_container AS
+ SELECT fb.id AS find_id, fb.container_id AS container_id
+ FROM archaeological_finds_find fb
+ WHERE fb.downstream_treatment_id IS NULL AND fb.container_id IS NOT NULL
+ UNION
+ SELECT f.id AS find_id, r.container_parent_id AS container_id
+ FROM archaeological_finds_find f
+ INNER JOIN container_tree r
+ ON r.container_id = f.container_id
+ WHERE f.downstream_treatment_id IS NULL;
+
+ -- deactivate deletion
+ CREATE RULE find_inside_container_del AS
+ ON DELETE TO find_inside_container
+ DO INSTEAD DELETE FROM archaeological_finds_find where id=NULL;
+ """
+ DELETE_SQL = """
+ DROP VIEW IF EXISTS find_inside_container;
+ """
+ TABLE_COLS = ["find__" + t for t in Find.TABLE_COLS]
+ COL_LABELS = {
+ "find__" + k: Find.COL_LABELS[k] for k in Find.COL_LABELS.keys()
+ }
+ EXTRA_REQUEST_KEYS = {
+ "find__" + k:
+ "find__" + Find.EXTRA_REQUEST_KEYS[k]
+ for k in Find.EXTRA_REQUEST_KEYS.keys()
+ }
+
+ find = models.OneToOneField(
+ Find, verbose_name=_("Find"), related_name="inside_container",
+ primary_key=True)
+ container = models.ForeignKey("archaeological_warehouse.Container",
+ verbose_name=_("Container"),
+ related_name="container_content")
+
+ class Meta:
+ managed = False
+ db_table = 'find_inside_container'
+
+
for attr in Find.HISTORICAL_M2M:
m2m_changed.connect(m2m_historization_changed,
sender=getattr(Find, attr).through)
diff --git a/archaeological_finds/templates/ishtar/sheet_find.html b/archaeological_finds/templates/ishtar/sheet_find.html
index a696bcf9f..2b2e868be 100644
--- a/archaeological_finds/templates/ishtar/sheet_find.html
+++ b/archaeological_finds/templates/ishtar/sheet_find.html
@@ -253,20 +253,10 @@
{% if display_warehouse_treatments %}
<div class="tab-pane fade" id="{{window_id}}-warehouse"
role="tabpanel" aria-labelledby="{{window_id}}-warehouse-tab">
- {% if item.container_ref %}
- <h3>{% trans "Warehouse - reference container"%}</h3>
+ {% if item.container %}
+ <h3>{% trans "Warehouse - container" %}</h3>
<div class='row'>
- {% field_flex_detail "Container" item.container_ref %}
- {% field_flex "Container ID" item.container_ref.cached_location %}
- {% field_flex_detail "Responsible warehouse" item.container_ref.responsible %}
- {% field_flex_detail "Location (warehouse)" item.container_ref.location %}
- {% field_flex "Precise localisation" item.container_ref.cached_division %}
- </div>
- {% endif %}
- {% if item.container and item.container_ref.pk != item.container.pk %}
- <h3>{% trans "Warehouse - current container"%}</h3>
- <div class='row'>
- {% field_flex_detail "Container" item.container %}
+ {% field_flex_detail "Container" item.container "large" %}
{% field_flex "Container ID" item.container.cached_location %}
{% field_flex_detail "Responsible warehouse" item.container.responsible %}
{% field_flex_detail "Location (warehouse)" item.container.location %}
diff --git a/archaeological_finds/tests.py b/archaeological_finds/tests.py
index 55006c4ca..80202f442 100644
--- a/archaeological_finds/tests.py
+++ b/archaeological_finds/tests.py
@@ -18,8 +18,11 @@
# See the file COPYING for details.
from copy import deepcopy
+import csv
import datetime
import json
+import os
+import tempfile
from rest_framework.test import APITestCase
from rest_framework.authtoken.models import Token
@@ -35,6 +38,7 @@ from django.test import tag
from django.test.client import Client
from ishtar_common.models import ImporterType, IshtarUser, ImporterColumn,\
FormaterType, ImportTarget, IshtarSiteProfile, ProfileType
+from django.utils.text import slugify
from django.utils.translation import pgettext_lazy, gettext_lazy as _
from ishtar_common.models import Person, get_current_profile, UserProfile, \
@@ -598,15 +602,22 @@ class TreatmentWizardCreationTest(WizardTest, FindInit, TestCase):
# treat)
-class ImportFindTest(ImportTest, TestCase):
- fixtures = FIND_TOWNS_FIXTURES
+class ImportFindTest(ImportTest, FindInit, TestCase):
+ fixtures = FIND_TOWNS_FIXTURES + [
+ settings.ROOT_PATH +
+ '../archaeological_finds/tests/import_loca_test.json',
+ ]
+
+ def setUp(self):
+ super(ImportFindTest, self).setUp()
+ self.tmpdir = tempfile.TemporaryDirectory()
def test_mcc_import_finds(self):
self.init_context_record()
old_nb = models.BaseFind.objects.count()
old_nb_find = models.Find.objects.count()
- MCC = ImporterType.objects.get(name=u"MCC - Mobilier")
+ MCC = ImporterType.objects.get(name="MCC - Mobilier")
col = ImporterColumn.objects.create(col_number=25,
importer_type_id=MCC.pk)
@@ -671,6 +682,153 @@ class ImportFindTest(ImportTest, TestCase):
f.index, expected_index
))
+ def test_import_locations(self):
+ self.create_finds()
+ self.create_finds()
+ self.create_finds()
+ self.create_finds()
+ external_ids = []
+ for idx, f in enumerate(self.finds):
+ f.label = "Find {}".format(idx)
+ f.save()
+ external_ids.append(f.external_id)
+
+ wt, __ = WarehouseType.objects.get_or_create(label="WT", txt_idx="WT")
+
+ warehouse, __ = Warehouse.objects.get_or_create(
+ external_id="warehouse-test",
+ defaults={"name": "Warehouse test",
+ "warehouse_type": wt}
+ )
+
+ container_types = []
+ levels = ["Building", "Area", "Shelf", "Box"]
+ for level in levels:
+ container_type, __ = ContainerType.objects.get_or_create(
+ label=level, txt_idx=slugify(level)
+ )
+ container_types.append(container_type)
+
+ for idx in range(len(levels[:-1])):
+ WarehouseDivisionLink.objects.get_or_create(
+ warehouse=warehouse,
+ container_type=container_types[idx],
+ order=(idx + 1) * 10
+ )
+
+ old_nb = models.BaseFind.objects.count()
+ old_nb_find = models.Find.objects.count()
+ old_nb_container = Container.objects.count()
+
+ importer_type = ImporterType.objects.get(slug="importeur-test")
+
+ imp_filename = os.path.join(self.tmpdir.name, "imp_locations.csv")
+
+ with open(imp_filename, "w") as impfile:
+ w = csv.writer(impfile)
+ w.writerow(['External ID', "Warehouse", "Ref.",
+ "Container type", "Localisation 1", "Localisation 2",
+ "Localisation 3"])
+ for idx, ext_id in enumerate(external_ids):
+ if idx < 2:
+ w.writerow([ext_id, "warehouse-test",
+ "Réf. {}".format((idx + 1) * 10),
+ container_types[-1].name,
+ "A", "42", idx + 1])
+ else:
+ w.writerow([ext_id, "warehouse-test",
+ "Réf. {}".format((idx + 1) * 10),
+ container_types[-1].name,
+ "A", 43])
+
+ imp_file = open(imp_filename, "rb")
+ file_dict = {'imported_file': SimpleUploadedFile(imp_file.name,
+ imp_file.read())}
+ post_dict = {'importer_type': importer_type.pk, 'skip_lines': 1,
+ "encoding": 'utf-8', "name": 'init_find_import',
+ "csv_sep": ","}
+ form = forms_common.NewImportForm(data=post_dict, files=file_dict,
+ user=self.user)
+ form.is_valid()
+ self.assertTrue(form.is_valid())
+ impt = form.save(self.ishtar_user)
+ impt.initialize()
+
+ impt.importation()
+ # no new finds has now been imported
+ current_nb = models.BaseFind.objects.count()
+ self.assertEqual(current_nb, old_nb)
+ current_nb = models.Find.objects.count()
+ self.assertEqual(current_nb, old_nb_find)
+
+ current_nb = Container.objects.count()
+ self.assertEqual(current_nb, old_nb_container + 4 + 2 + 2 + 1)
+ containers = list(Container.objects.all())
+ for container in containers:
+ self.assertEqual(container.location, warehouse)
+ q = Container.objects.filter(container_type=container_types[0])
+ self.assertEqual(q.count(), 1)
+ building = q.all()[0]
+ self.assertIsNone(building.parent)
+
+ q = Container.objects.filter(container_type=container_types[1])
+ self.assertEqual(q.count(), 2)
+ areas = list(q.all())
+ area = q.all()[0]
+ self.assertEqual(area.parent, building)
+
+ q = Container.objects.filter(container_type=container_types[2])
+ self.assertEqual(q.count(), 2)
+ shelves = list(q.all())
+ for shelf in shelves:
+ self.assertEqual(shelf.parent, area)
+
+ q = Container.objects.filter(container_type=container_types[3])
+ self.assertEqual(q.count(), 4)
+ boxes = list(q.all().order_by("id"))
+ previous_shelf = None
+ for box in boxes[:2]:
+ if not previous_shelf:
+ previous_shelf = box.parent
+ else:
+ # on a different shelf
+ self.assertNotEqual(previous_shelf, box.parent)
+ self.assertIn(box.parent_id, [s.pk for s in shelves])
+ previous_area = None
+ for box in boxes[2:]:
+ if not previous_area:
+ previous_area = box.parent
+ else:
+ self.assertEqual(previous_area, box.parent) # on the same area
+ self.assertIn(box.parent_id, [s.pk for s in areas])
+
+ importer_type = ImporterType.objects.get(slug="importeur-test")
+ cols = list(ImporterColumn.objects.filter(
+ importer_type=importer_type,
+ col_number__gte=5).values_list("id", flat=True))
+ for target in ImportTarget.objects.filter(column_id__in=cols):
+ target.target = target.target.replace("set_localisation",
+ "set_static_localisation")
+ target.save()
+
+ # delete area 43 and all boxes
+ Container.objects.filter(reference="43").delete()
+ Container.objects.filter(container_type=container_types[-1]).delete()
+
+ # reimport
+ impt.initialize()
+ impt.importation()
+ # check errors
+ self.assertEqual(len(impt.errors), 2)
+ for error in impt.errors:
+ self.assertEqual(
+ error['error'],
+ "The division Area 43 do not exist for the location Warehouse "
+ "test.")
+
+ def tearDown(self):
+ self.tmpdir.cleanup()
+
class FindTest(FindInit, TestCase):
fixtures = FIND_FIXTURES
diff --git a/archaeological_finds/tests/import_loca_test.json b/archaeological_finds/tests/import_loca_test.json
new file mode 100644
index 000000000..1f0a9054a
--- /dev/null
+++ b/archaeological_finds/tests/import_loca_test.json
@@ -0,0 +1,305 @@
+[
+{
+ "model": "ishtar_common.importertype",
+ "fields": {
+ "name": "Importeur test",
+ "slug": "importeur-test",
+ "description": null,
+ "associated_models": [
+ "archaeological_finds.models_finds.Find"
+ ],
+ "is_template": false,
+ "unicity_keys": "external_id",
+ "available": true,
+ "users": [],
+ "created_models": [
+ [
+ "archaeological_warehouse.models.Container"
+ ],
+ [
+ "archaeological_finds.models_finds.Find"
+ ],
+ [
+ "archaeological_finds.models.BaseFind"
+ ]
+ ]
+ }
+},
+{
+ "model": "ishtar_common.importercolumn",
+ "fields": {
+ "label": "ID externe",
+ "importer_type": [
+ "importeur-test"
+ ],
+ "col_number": 1,
+ "description": "",
+ "regexp_pre_filter": null,
+ "value_format": null,
+ "required": true,
+ "export_field_name": null
+ }
+},
+{
+ "model": "ishtar_common.importercolumn",
+ "fields": {
+ "label": "Localisation 1",
+ "importer_type": [
+ "importeur-test"
+ ],
+ "col_number": 5,
+ "description": "",
+ "regexp_pre_filter": null,
+ "value_format": null,
+ "required": false,
+ "export_field_name": null
+ }
+},
+{
+ "model": "ishtar_common.importercolumn",
+ "fields": {
+ "label": "Localisation 2",
+ "importer_type": [
+ "importeur-test"
+ ],
+ "col_number": 6,
+ "description": "",
+ "regexp_pre_filter": null,
+ "value_format": null,
+ "required": false,
+ "export_field_name": null
+ }
+},
+{
+ "model": "ishtar_common.importercolumn",
+ "fields": {
+ "label": "Localisation 3",
+ "importer_type": [
+ "importeur-test"
+ ],
+ "col_number": 7,
+ "description": "",
+ "regexp_pre_filter": null,
+ "value_format": null,
+ "required": false,
+ "export_field_name": null
+ }
+},
+{
+ "model": "ishtar_common.importercolumn",
+ "fields": {
+ "label": "D\u00e9p\u00f4t",
+ "importer_type": [
+ "importeur-test"
+ ],
+ "col_number": 2,
+ "description": "",
+ "regexp_pre_filter": null,
+ "value_format": null,
+ "required": false,
+ "export_field_name": null
+ }
+},
+{
+ "model": "ishtar_common.importercolumn",
+ "fields": {
+ "label": "Ref. container",
+ "importer_type": [
+ "importeur-test"
+ ],
+ "col_number": 3,
+ "description": "",
+ "regexp_pre_filter": null,
+ "value_format": null,
+ "required": false,
+ "export_field_name": null
+ }
+},
+{
+ "model": "ishtar_common.importercolumn",
+ "fields": {
+ "label": "Type de contenant",
+ "importer_type": [
+ "importeur-test"
+ ],
+ "col_number": 4,
+ "description": "",
+ "regexp_pre_filter": null,
+ "value_format": null,
+ "required": false,
+ "export_field_name": null
+ }
+},
+{
+ "model": "ishtar_common.importtarget",
+ "fields": {
+ "column": [
+ "importeur-test",
+ 1
+ ],
+ "target": "external_id",
+ "formater_type": [
+ "UnicodeFormater",
+ "100",
+ ""
+ ],
+ "force_new": false,
+ "concat": false,
+ "concat_str": null,
+ "comment": ""
+ }
+},
+{
+ "model": "ishtar_common.importtarget",
+ "fields": {
+ "column": [
+ "importeur-test",
+ 5
+ ],
+ "target": "set_localisation_1",
+ "formater_type": [
+ "UnicodeFormater",
+ "10",
+ ""
+ ],
+ "force_new": false,
+ "concat": false,
+ "concat_str": null,
+ "comment": ""
+ }
+},
+{
+ "model": "ishtar_common.importtarget",
+ "fields": {
+ "column": [
+ "importeur-test",
+ 6
+ ],
+ "target": "set_localisation_2",
+ "formater_type": [
+ "UnicodeFormater",
+ "10",
+ ""
+ ],
+ "force_new": false,
+ "concat": false,
+ "concat_str": null,
+ "comment": ""
+ }
+},
+{
+ "model": "ishtar_common.importtarget",
+ "fields": {
+ "column": [
+ "importeur-test",
+ 7
+ ],
+ "target": "set_localisation_3",
+ "formater_type": [
+ "UnicodeFormater",
+ "10",
+ ""
+ ],
+ "force_new": false,
+ "concat": false,
+ "concat_str": null,
+ "comment": ""
+ }
+},
+{
+ "model": "ishtar_common.importtarget",
+ "fields": {
+ "column": [
+ "importeur-test",
+ 2
+ ],
+ "target": "container__location__external_id",
+ "formater_type": [
+ "UnicodeFormater",
+ "100",
+ ""
+ ],
+ "force_new": false,
+ "concat": false,
+ "concat_str": null,
+ "comment": ""
+ }
+},
+{
+ "model": "ishtar_common.importtarget",
+ "fields": {
+ "column": [
+ "importeur-test",
+ 3
+ ],
+ "target": "container__reference",
+ "formater_type": [
+ "UnicodeFormater",
+ "30",
+ ""
+ ],
+ "force_new": false,
+ "concat": false,
+ "concat_str": null,
+ "comment": ""
+ }
+},
+{
+ "model": "ishtar_common.importtarget",
+ "fields": {
+ "column": [
+ "importeur-test",
+ 4
+ ],
+ "target": "container__container_type",
+ "formater_type": [
+ "TypeFormater",
+ "archaeological_warehouse.models.ContainerType",
+ ""
+ ],
+ "force_new": false,
+ "concat": false,
+ "concat_str": null,
+ "comment": ""
+ }
+},
+{
+ "model": "ishtar_common.importerduplicatefield",
+ "fields": {
+ "column": [
+ "importeur-test",
+ 2
+ ],
+ "field_name": "container__responsible__external_id",
+ "force_new": false,
+ "concat": false,
+ "concat_str": null
+ }
+},
+{
+ "model": "ishtar_common.importerduplicatefield",
+ "fields": {
+ "column": [
+ "importeur-test",
+ 3
+ ],
+ "field_name": "container__external_id",
+ "force_new": false,
+ "concat": false,
+ "concat_str": "-"
+ }
+},
+{
+ "model": "ishtar_common.importerduplicatefield",
+ "fields": {
+ "column": [
+ "importeur-test",
+ 2
+ ],
+ "field_name": "container__external_id",
+ "force_new": false,
+ "concat": false,
+ "concat_str": "-"
+ }
+}
+]
diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py
index 588777552..bab3fc6b4 100644
--- a/archaeological_finds/urls.py
+++ b/archaeological_finds/urls.py
@@ -270,6 +270,11 @@ urlpatterns = [
name='get-own-find-for-treatment', kwargs={'force_own': True}),
url(r'get-find-for-treatment/(?P<type>.+)?$', views.get_find_for_treatment,
name='get-find-for-treatment'),
+ url(r'get-find-inside-container/own/(?P<type>.+)?$',
+ views.get_find_inside_container,
+ name='get-find-inside-container', kwargs={'force_own': True}),
+ url(r'get-find-inside-container/(?P<type>.+)?$',
+ views.get_find_inside_container, name='get-find-inside-container'),
url(r'get-find-full/own/(?P<type>.+)?$', views.get_find,
name='get-own-find-full', kwargs={'full': True, 'force_own': True}),
url(r'get-find-full/(?P<type>.+)?$', views.get_find,
diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py
index 1fc2b3722..cf82cceb8 100644
--- a/archaeological_finds/views.py
+++ b/archaeological_finds/views.py
@@ -66,6 +66,11 @@ def get_table_cols_for_ope():
return settings.TABLE_COLS[tb_key]
+def get_table_cols_for_container():
+ table_col = get_table_cols_for_ope()
+ return ["find__" + tc for tc in table_col]
+
+
get_find_for_ope = get_item(models.Find, 'get_find', 'find',
own_table_cols=get_table_cols_for_ope())
@@ -73,6 +78,12 @@ get_find_for_treatment = get_item(
models.Find, 'get_find', 'find',
own_table_cols=get_table_cols_for_ope(), base_request={})
+get_find_inside_container = get_item(
+ models.FindInsideContainer, 'get_find_inside_container',
+ 'find',
+ extra_request_keys=models.FindInsideContainer.EXTRA_REQUEST_KEYS,
+ own_table_cols=get_table_cols_for_container())
+
autocomplete_find = get_autocomplete_item(model=models.Find)
diff --git a/archaeological_warehouse/forms.py b/archaeological_warehouse/forms.py
index e03918965..a7390a890 100644
--- a/archaeological_warehouse/forms.py
+++ b/archaeological_warehouse/forms.py
@@ -372,15 +372,14 @@ class ContainerSelect(DocumentItemSelect):
label=_(u"Full text search"), widget=widgets.SearchWidget(
'archaeological-warehouse', 'container'
))
- location_name = get_warehouse_field(
- label=_(u"Current location (warehouse)"))
- responsible_name = get_warehouse_field(label=_(u"Responsible warehouse"))
- container_type = forms.ChoiceField(label=_(u"Container type"), choices=[])
- reference = forms.CharField(label=_(u"Ref."))
- old_reference = forms.CharField(label=_(u"Old reference"))
+ location_name = get_warehouse_field(label=_("Warehouse"))
+ container_type = forms.ChoiceField(label=_("Container type"), choices=[])
+ reference = forms.CharField(label=_("Ref."))
+ old_reference = forms.CharField(label=_("Old reference"))
comment = forms.CharField(label=_(u"Comment"))
- no_finds = forms.NullBooleanField(label=_(u"No associated finds"))
- empty = forms.NullBooleanField(label=_(u"Currently empty"))
+ contain_containers = forms.NullBooleanField(label=_("Contain containers"))
+ empty = forms.NullBooleanField(label=_("Currently empty"))
+ is_stationary = forms.NullBooleanField(label=_("Is stationary"))
archaeological_sites = forms.IntegerField(
label=_("Archaeological site (attached to the operation)"),
diff --git a/archaeological_warehouse/management/commands/migrate_to_new_container_management.py b/archaeological_warehouse/management/commands/migrate_to_new_container_management.py
index b5885cbf0..d8f701793 100644
--- a/archaeological_warehouse/management/commands/migrate_to_new_container_management.py
+++ b/archaeological_warehouse/management/commands/migrate_to_new_container_management.py
@@ -30,18 +30,23 @@ class Command(BaseCommand):
def handle(self, *args, **options):
to_update = models.Container.objects.filter(
- division__pk__isnull=False)
+ division__pk__isnull=False, parent__isnull=True)
container_types = {}
created_nb = 0
for div_type in models.WarehouseDivision.objects.all():
container_type, c = models.ContainerType.objects.get_or_create(
txt_idx=slugify(div_type.label),
- defaults={"label": div_type.label})
+ defaults={"label": div_type.label,
+ "stationary": True})
if c:
created_nb += 1
sys.stdout.write("-> {} created\n".format(
div_type.label))
container_types[div_type.pk] = container_type
+ for wdl in models.WarehouseDivisionLink.objects.all():
+ wdl.container_type = container_types[wdl.division_id]
+ wdl.save()
+
if created_nb:
sys.stdout.write("* {} container types created\n".format(
created_nb))
@@ -50,10 +55,10 @@ class Command(BaseCommand):
potential_duplicate = {}
data = [("id", "warehouse", "reference",
"old cached division", "new cached division")]
- for idx, container in enumerate(models.Container.objects.filter(
- division__pk__isnull=False).all()):
+ for idx, container in enumerate(to_update.values("id").all()):
sys.stdout.write("* Updating: {}/{}\r".format(idx + 1, to_be_done))
sys.stdout.flush()
+ container = models.Container.objects.get(pk=container["id"])
if container.responsible_id not in potential_duplicate:
potential_duplicate[container.responsible_id] = {}
parent = None
diff --git a/archaeological_warehouse/migrations/0103_auto_20200403_1638.py b/archaeological_warehouse/migrations/0103_auto_20200403_1638.py
new file mode 100644
index 000000000..d7bd5320b
--- /dev/null
+++ b/archaeological_warehouse/migrations/0103_auto_20200403_1638.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.27 on 2020-04-03 16:38
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_warehouse', '0102_auto_20200324_1142'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='containertype',
+ name='stationary',
+ field=models.BooleanField(default=False, help_text='Container that usually will not be moved. Ex: building, room.', verbose_name='Stationary'),
+ ),
+ migrations.AddField(
+ model_name='warehousedivisionlink',
+ name='container_type',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='archaeological_warehouse.ContainerType'),
+ ),
+ migrations.AlterField(
+ model_name='container',
+ name='parent',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='archaeological_warehouse.Container', verbose_name='Parent container'),
+ ),
+ migrations.AlterField(
+ model_name='container',
+ name='responsible',
+ field=models.ForeignKey(blank=True, help_text='Deprecated - do not use', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='owned_containers', to='archaeological_warehouse.Warehouse', verbose_name='Responsible warehouse'),
+ ),
+ migrations.AlterField(
+ model_name='warehousedivisionlink',
+ name='division',
+ field=models.ForeignKey(blank=True, help_text='Deprecated - do not use', null=True, on_delete=django.db.models.deletion.CASCADE, to='archaeological_warehouse.WarehouseDivision'),
+ ),
+ ]
diff --git a/archaeological_warehouse/migrations/0104_auto_container_views.py b/archaeological_warehouse/migrations/0104_auto_container_views.py
new file mode 100644
index 000000000..4f707c8d7
--- /dev/null
+++ b/archaeological_warehouse/migrations/0104_auto_container_views.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.27 on 2020-04-03 16:47
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+import archaeological_warehouse.models
+import archaeological_finds.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_warehouse', '0103_auto_20200403_1638'),
+ ]
+
+ operations = [
+ migrations.RunSQL(
+ archaeological_warehouse.models.ContainerTree.DELETE_SQL),
+ migrations.RunSQL(
+ archaeological_warehouse.models.ContainerTree.CREATE_SQL),
+ migrations.RunSQL(
+ archaeological_finds.models.FindInsideContainer.DELETE_SQL),
+ migrations.RunSQL(
+ archaeological_finds.models.FindInsideContainer.CREATE_SQL),
+ ]
diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py
index 839ca1a6e..c4b26e3d1 100644
--- a/archaeological_warehouse/models.py
+++ b/archaeological_warehouse/models.py
@@ -393,12 +393,37 @@ class WarehouseDivisionLinkManager(models.Manager):
division__txt_idx=division)
+class ContainerType(GeneralType):
+ stationary = models.BooleanField(
+ _("Stationary"), default=False,
+ help_text=_("Container that usually will not be moved. Ex: building, "
+ "room."))
+ length = models.IntegerField(_(u"Length (mm)"), blank=True, null=True)
+ width = models.IntegerField(_(u"Width (mm)"), blank=True, null=True)
+ height = models.IntegerField(_(u"Height (mm)"), blank=True, null=True)
+ volume = models.FloatField(_(u"Volume (l)"), blank=True, null=True)
+ reference = models.CharField(_(u"Ref."), max_length=300, blank=True,
+ null=True)
+
+ class Meta:
+ verbose_name = _(u"Container type")
+ verbose_name_plural = _(u"Container types")
+ ordering = ('label',)
+
+
+post_save.connect(post_save_cache, sender=ContainerType)
+post_delete.connect(post_save_cache, sender=ContainerType)
+
+
class WarehouseDivisionLink(models.Model):
RELATED_SET_NAME = "divisions"
RELATED_ATTRS = ["order"]
RELATIVE_MODELS = {Warehouse: 'warehouse'}
warehouse = models.ForeignKey(Warehouse, related_name='divisions')
- division = models.ForeignKey(WarehouseDivision)
+ container_type = models.ForeignKey(ContainerType, blank=True, null=True)
+ division = models.ForeignKey(
+ WarehouseDivision, help_text=_("Deprecated - do not use"),
+ blank=True, null=True)
order = models.IntegerField(_("Order"), default=10)
objects = WarehouseDivisionLinkManager()
@@ -413,22 +438,43 @@ class WarehouseDivisionLink(models.Model):
return self.warehouse.uuid, self.division.txt_idx
-class ContainerType(GeneralType):
- length = models.IntegerField(_(u"Length (mm)"), blank=True, null=True)
- width = models.IntegerField(_(u"Width (mm)"), blank=True, null=True)
- height = models.IntegerField(_(u"Height (mm)"), blank=True, null=True)
- volume = models.FloatField(_(u"Volume (l)"), blank=True, null=True)
- reference = models.CharField(_(u"Ref."), max_length=300, blank=True,
- null=True)
-
- class Meta:
- verbose_name = _(u"Container type")
- verbose_name_plural = _(u"Container types")
- ordering = ('label',)
-
-
-post_save.connect(post_save_cache, sender=ContainerType)
-post_delete.connect(post_save_cache, sender=ContainerType)
+class ContainerTree:
+ CREATE_SQL = """
+ CREATE VIEW containers_tree AS
+ WITH RECURSIVE rel_tree AS (
+ SELECT c.id AS container_id, c.parent_id AS container_parent_id,
+ 1 AS level
+ FROM archaeological_warehouse_container c
+ WHERE c.parent_id is NOT NULL
+ UNION ALL
+ SELECT p.container_id AS container_id,
+ c.parent_id as container_parent_id,
+ p.level + 1
+ FROM archaeological_warehouse_container c, rel_tree p
+ WHERE c.id = p.container_parent_id
+ AND c.parent_id is NOT NULL
+ AND p.level < 10 -- prevent recursive...
+ )
+ SELECT DISTINCT container_id, container_parent_id, level
+ FROM rel_tree;
+
+ CREATE VIEW container_tree AS
+ SELECT DISTINCT y.container_id, y.container_parent_id
+ FROM (SELECT * FROM containers_tree) y
+ ORDER BY y.container_id, y.container_parent_id;
+
+ -- deactivate deletion
+ CREATE RULE container_tree_del AS
+ ON DELETE TO container_tree
+ DO INSTEAD DELETE FROM archaeological_warehouse_container where id=NULL;
+ CREATE RULE containers_tree_del AS
+ ON DELETE TO container_tree
+ DO INSTEAD DELETE FROM archaeological_warehouse_container where id=NULL;
+ """
+ DELETE_SQL = """
+ DROP VIEW IF EXISTS container_tree;
+ DROP VIEW IF EXISTS containers_tree;
+ """
class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
@@ -439,7 +485,8 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
SHOW_URL = 'show-container'
DELETE_URL = 'delete-container'
NEW_QUERY_ENGINE = True
- TABLE_COLS = ['reference', 'container_type__label', 'cached_location',
+ TABLE_COLS = ['container_type__label', 'reference',
+ 'location__name',
'cached_division', 'old_reference']
IMAGE_PREFIX = 'containers/'
BASE_SEARCH_VECTORS = [
@@ -457,6 +504,7 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
# search parameters
EXTRA_REQUEST_KEYS = {
'location': 'location__pk',
+ 'location__name': "location__name",
'location_id': 'location__pk',
'responsible_id': 'responsible__pk',
'container_type': 'container_type__pk',
@@ -470,9 +518,10 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
'container_type__label': 'container_type__label',
}
COL_LABELS = {
- 'cached_location': _(u"Location - index"),
- 'cached_division': _(u"Precise localisation"),
- 'container_type__label': _(u"Type")
+ 'cached_location': _("Location - index"),
+ 'cached_division': _("Precise localisation"),
+ 'container_type__label': _("Type"),
+ "location__name": _("Warehouse")
}
GEO_LABEL = "cached_label"
CACHED_LABELS = ['cached_division', 'cached_label', 'cached_location', ]
@@ -483,10 +532,6 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
pgettext_lazy("key for text search", "location"),
'location__name__iexact'
),
- 'responsible_name': SearchAltName(
- pgettext_lazy("key for text search", "responsible-warehouse"),
- 'responsible__name__iexact'
- ),
'container_type': SearchAltName(
pgettext_lazy("key for text search", "type"),
'container_type__label__iexact'
@@ -595,20 +640,25 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
pgettext_lazy("key for text search", "find-description"),
'finds__description__iexact'),
'empty': SearchAltName(
- pgettext_lazy("key for text search", u"empty"),
+ pgettext_lazy("key for text search", "empty"),
'finds'
),
- 'no_finds': SearchAltName(
- pgettext_lazy("key for text search", u"no-associated-finds"),
- 'finds_ref'
+ 'contain_containers': SearchAltName(
+ pgettext_lazy("key for text search", "contain-containers"),
+ 'children__isnull'
),
-
+ 'is_stationary': SearchAltName(
+ pgettext_lazy("key for text search", "is-stationary"),
+ 'container_type__stationary'
+ )
}
REVERSED_BOOL_FIELDS = [
+ 'children__isnull',
'documents__image__isnull',
'documents__associated_file__isnull',
'documents__associated_url__isnull',
]
+ BOOL_FIELDS = ['container_type__stationary']
REVERSED_MANY_COUNTED_FIELDS = ['finds', 'finds_ref']
ALT_NAMES.update(LightHistorizedItem.ALT_NAMES)
@@ -638,11 +688,13 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
# fields
uuid = models.UUIDField(default=uuid.uuid4)
location = models.ForeignKey(
- Warehouse, verbose_name=_(u"Location (warehouse)"),
+ Warehouse, verbose_name=_("Location (warehouse)"),
related_name='containers')
responsible = models.ForeignKey(
- Warehouse, verbose_name=_(u"Responsible warehouse"),
- related_name='owned_containers')
+ Warehouse, verbose_name=_("Responsible warehouse"),
+ related_name='owned_containers', blank=True, null=True,
+ help_text=_("Deprecated - do not use")
+ )
container_type = models.ForeignKey(ContainerType,
verbose_name=_("Container type"))
reference = models.TextField(_(u"Container ref."))
@@ -655,19 +707,21 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
null=True, blank=True, db_index=True)
parent = models.ForeignKey("Container", verbose_name=_("Parent container"),
on_delete=models.SET_NULL,
- blank=True, null=True)
- index = models.IntegerField(u"Container ID", default=0)
- old_reference = models.TextField(_(u"Old reference"), blank=True, null=True)
- external_id = models.TextField(_(u"External ID"), blank=True, null=True)
+ related_name="children", blank=True, null=True)
+ index = models.IntegerField(_("Container ID"), default=0)
+ old_reference = models.TextField(_("Old reference"), blank=True, null=True)
+ external_id = models.TextField(_("External ID"), blank=True, null=True)
auto_external_id = models.BooleanField(
- _(u"External ID is set automatically"), default=False)
+ _("External ID is set automatically"), default=False)
documents = models.ManyToManyField(
- Document, related_name='containers', verbose_name=_(u"Documents"),
+ Document, related_name='containers', verbose_name=_("Documents"),
blank=True)
main_image = models.ForeignKey(
Document, related_name='main_image_containers',
on_delete=models.SET_NULL,
- verbose_name=_(u"Main image"), blank=True, null=True)
+ verbose_name=_("Main image"), blank=True, null=True)
+
+ DISABLE_POLYGONS = False
class Meta:
verbose_name = _(u"Container")
@@ -688,13 +742,15 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
def __str__(self):
return self.cached_label or ""
+ @property
+ def short_label(self):
+ return "{} {}".format(self.container_type.label, self.reference)
+
def natural_key(self):
return (self.uuid, )
def _generate_cached_label(self):
- items = [self.reference, self.precise_location]
- cached_label = u" | ".join(items)
- return cached_label
+ return self.precise_location
def _generate_cached_location(self):
items = [self.location.name, str(self.index)]
@@ -715,10 +771,11 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
"{} {}".format(loca.container_type.name, loca.reference)
for loca in reversed(parents)
]
+ locas.append("{} {}".format(self.container_type.name, self.reference))
return " | ".join(locas)
def _get_base_image_path(self):
- return self.responsible._get_base_image_path() + u"/" + self.external_id
+ return self.location._get_base_image_path() + "/" + self.external_id
def merge(self, item, keep_old=False):
locas = [
@@ -754,21 +811,21 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
@property
def associated_filename(self):
filename = datetime.date.today().strftime('%Y-%m-%d')
- filename += u'-' + self.reference
- filename += u"-" + self.location.name
- filename += u"-" + str(self.index)
+ filename += "-" + self.reference
+ filename += "-" + self.location.name
+ filename += "-" + str(self.index)
if self.cached_division is None:
self.skip_history_when_saving = True
self.save()
if self.cached_division:
- filename += u"-" + self.cached_division
+ filename += "-" + self.cached_division
return slugify(filename)
@property
def precise_location(self):
location = self.location.name
if self.cached_division:
- location += u" " + self.cached_division
+ location += " " + self.cached_division
return location
def get_localisations(self):
@@ -777,11 +834,12 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
:return: tuple of strings with localisations
"""
- return tuple((
- loca.reference
- for loca in ContainerLocalisation.objects.filter(
- container=self).order_by('division__order')
- ))
+ localisations = []
+ parent = self.parent
+ while parent:
+ localisations.append(parent)
+ parent = parent.parent
+ return reversed(localisations)
def get_localisation(self, place):
locas = self.get_localisations()
@@ -825,30 +883,83 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
def localisation_9(self):
return self.get_localisation(8)
- def set_localisation(self, place, value):
+ def set_localisation(self, place, value, static=False, return_errors=False):
"""
Set the reference for the localisation number "place" (starting from 0)
:param place: the number of the localisation
:param value: the reference to be set
+ :param static: do not create new containers
+ :param return_errors: return error message
:return: the container location object or None if the place does not
- exist
+ exist - return also error message if return_errors set to True
"""
+ value = value.strip()
+ if not value or value == "-":
+ if return_errors:
+ return None, _("No value")
+ return
q = WarehouseDivisionLink.objects.filter(
warehouse=self.location).order_by('order')
+
+ current_container_type = None
+ error_msg = str(
+ _("The division number {} have not been set for the warehouse {}.")
+ ).format(place + 1, self.location)
+ previous_container_types = []
for idx, division_link in enumerate(q.all()):
if idx == place:
+ current_container_type = division_link.container_type
break
+ previous_container_types.append(division_link.container_type_id)
else:
+ if return_errors:
+ return None, error_msg
return
- dct = {'container': self, 'division': division_link}
- if not value:
- if ContainerLocalisation.objects.filter(**dct).count():
- c = ContainerLocalisation.objects.filter(**dct).all()[0]
- c.delete()
+ if not current_container_type:
+ # no division link set at this place
+ if return_errors:
+ return None, error_msg
return
- dct['defaults'] = {'reference': value}
- obj, created = ContainerLocalisation.objects.update_or_create(**dct)
- return obj
+
+ # modify existing
+ current_localisations = self.get_localisations()
+ current_localisation, current_parent = None, None
+ for loca in current_localisations:
+ if loca.container_type == current_container_type:
+ if loca.reference == value:
+ current_localisation = loca
+ break
+ elif loca.container_type_id in previous_container_types:
+ current_parent = loca
+
+ if not current_localisation:
+ dct = {
+ "reference": value,
+ "container_type": current_container_type,
+ "parent": current_parent,
+ "location": self.location
+ }
+ q = Container.objects.filter(**dct)
+ if q.count():
+ current_localisation = q.all()[0]
+ else:
+ if static:
+ if return_errors:
+ error_msg = str(
+ _("The division {} {} do not exist for the "
+ "location {}.")
+ ).format(current_container_type, value, self.location)
+ return None, error_msg
+ return
+ current_localisation = Container.objects.create(**dct)
+ self.parent = current_localisation
+ self.save()
+ if return_errors:
+ return current_localisation, None
+ return current_localisation
+
+ def set_static_localisation(self, place, value):
+ return self.set_localisation(place, value, static=True)
@post_importer_action
def set_localisation_1(self, context, value):
@@ -895,6 +1006,51 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem,
return self.set_localisation(8, value)
set_localisation_9.post_save = True
+ @post_importer_action
+ def set_static_localisation_1(self, context, value):
+ return self.set_static_localisation(0, value)
+ set_static_localisation_1.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_2(self, context, value):
+ return self.set_static_localisation(1, value)
+ set_static_localisation_2.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_3(self, context, value):
+ return self.set_static_localisation(2, value)
+ set_static_localisation_3.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_4(self, context, value):
+ return self.set_static_localisation(3, value)
+ set_static_localisation_4.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_5(self, context, value):
+ return self.set_static_localisation(4, value)
+ set_static_localisation_5.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_6(self, context, value):
+ return self.set_static_localisation(5, value)
+ set_static_localisation_6.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_7(self, context, value):
+ return self.set_static_localisation(6, value)
+ set_static_localisation_7.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_8(self, context, value):
+ return self.set_static_localisation(7, value)
+ set_static_localisation_8.post_save = True
+
+ @post_importer_action
+ def set_static_localisation_9(self, context, value):
+ return self.set_static_localisation(8, value)
+ set_static_localisation_9.post_save = True
+
def get_extra_actions(self, request):
"""
extra actions for the sheet template
diff --git a/archaeological_warehouse/templates/ishtar/sheet_container.html b/archaeological_warehouse/templates/ishtar/sheet_container.html
index ffc533513..1bb004071 100644
--- a/archaeological_warehouse/templates/ishtar/sheet_container.html
+++ b/archaeological_warehouse/templates/ishtar/sheet_container.html
@@ -1,7 +1,10 @@
{% extends "ishtar/sheet.html" %}
-{% load i18n window_header window_field window_tables %}
+{% load i18n window_header window_field window_tables link_to_window %}
-{% block head_title %}<strong>{% trans "Container" %}</strong> - {{ item.reference|default:"" }} ({{ item.container_type|default:"" }}){% endblock %}
+{% block head_title %}<strong>{% trans "Container" %}</strong> -
+{{ item.container_type|default:"" }} {{ item.reference|default:"" }} -
+{{ item.location.name|default:"" }}
+{% endblock %}
{% block toolbar %}
{% window_nav item window_id 'show-container' 'container_modify' '' '' previous next 1 %}
@@ -19,9 +22,8 @@
{% else %}
<div class="float-left col-6 col-md-3 text-center">
{% endif %}
- <p class="window-refs">{{ item.reference|default:"" }}</p>
- <p class="window-refs">{{ item.container_type|default:"" }}</p>
- <p class="window-refs">{{ item.responsible.name }} - {{ item.index }}</p>
+ <p class="window-refs">{{ item.container_type|default:"" }} {{ item.reference|default:"" }}</p>
+ <p class="window-refs">{{ item.location.name }} - {{ item.index }}</p>
<p class="window-refs">{{ item.old_reference|default:"" }}</p>
{% include "ishtar/blocks/sheet_external_id.html" %}
</div>
@@ -30,36 +32,57 @@
{% else %}
<div class="float-left row col-6 col-md-8">
{% endif %}
- {% field_flex_detail "Responsible warehouse" item.responsible %}
- {% field_flex_detail "Location (warehouse)" item.location %}
+ {% field_flex_detail "Warehouse" item.location %}
+ {% if item.parent %}
+ <dl class="col-12 col-md-6 flex-wrap">
+ <dt>{% trans "Location" %}</dt>
+ <dd>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ {% for loca in item.get_localisations %}
+ <li class="breadcrumb-item">
+ {{loca.short_label}}&nbsp;{{loca|simple_link_to_window}}
+ </li>
+ {% endfor %}
+ </ol>
+ </nav>
+ </dd>
+ </dl>
+ {% endif %}
{% include "ishtar/blocks/sheet_creation_section.html" %}
- {% field_flex "Location" item.precise_location %}
{% field_flex_full "Comment" item.comment "<pre>" "</pre>" %}
{% include "ishtar/blocks/sheet_json.html" %}
</div>
</div>
+{% if item.container_content.count or item.children.count %}
+<h4>{% trans "Content" %}</h4>
+
+{% if item.children.count %}
+{% trans "Containers" as container_lbl %}
+{% dynamic_table_document container_lbl 'containers' 'parent' item.pk 'TABLE_COLS' output 'large' %}
+{% endif %}
+
+{% if item.container_content.count %}
+{% trans "Finds" as finds_lbl %}
+{% dynamic_table_document finds_lbl 'finds_inside_container' 'container' item.pk 'TABLE_COLS' output 'large' %}
+{% endif %}
+
+{% endif %}
+
+{% if PROFILE.locate_warehouses %}
{% if item.point_2d or item.multi_polygon %}
<h3>{% trans "Localisation"%}</h3>
<div class='row'>
+ {{item.point_2d}}
{% with geo_item=item %}
- {% if PROFILE.locate_warehouses %}{% include "ishtar/blocks/sheet_simple_map.html"%}{% endif %}
+ {% include "ishtar/blocks/sheet_simple_map.html" %}
<div class="col-12 col-lg-6 flex-wrap">
- {% if PROFILE.locate_warehouses %}{% include "ishtar/blocks/sheet_coordinates.html"%}{% endif %}
+ {% include "ishtar/blocks/sheet_coordinates.html" %}
</div>
{% endwith %}
</div>
{% endif %}
-
-
-{% if item.finds.count %}
-<h4>{% trans "Content" %}</h4>
-{% dynamic_table_document finds 'finds' 'container' item.pk 'TABLE_COLS' output 'large' %}
-{% endif %}
-
-{% if item.finds_ref.count %}
-<h4>{% trans "Reference content" %}</h4>
-{% dynamic_table_document finds 'finds' 'container_ref' item.pk 'TABLE_COLS' output 'large' %}
{% endif %}
{% endblock %}
diff --git a/ishtar_common/fixtures/initial_importtypes-fr.json b/ishtar_common/fixtures/initial_importtypes-fr.json
index 5ae394040..374b5ccbc 100644
--- a/ishtar_common/fixtures/initial_importtypes-fr.json
+++ b/ishtar_common/fixtures/initial_importtypes-fr.json
@@ -133,6 +133,13 @@
}
},
{
+ "model": "ishtar_common.importermodel",
+ "fields": {
+ "name": "Contenant",
+ "klass": "archaeological_warehouse.models.Container"
+ }
+},
+{
"model": "ishtar_common.documenttemplate",
"fields": {
"name": "Document de r\u00e9f\u00e9rence",
diff --git a/ishtar_common/templatetags/link_to_window.py b/ishtar_common/templatetags/link_to_window.py
index 6f0db9dc1..77d743ea0 100644
--- a/ishtar_common/templatetags/link_to_window.py
+++ b/ishtar_common/templatetags/link_to_window.py
@@ -33,9 +33,9 @@ def link_to_window(item, context):
elif "request" in context: # RequestContext
request = context['request']
else:
- return u""
+ return ""
if not item.can_view(request):
- return u""
+ return ""
return simple_link_to_window(item)
diff --git a/ishtar_common/templatetags/window_field.py b/ishtar_common/templatetags/window_field.py
index 7aaf62397..cd4122b58 100644
--- a/ishtar_common/templatetags/window_field.py
+++ b/ishtar_common/templatetags/window_field.py
@@ -161,9 +161,10 @@ def field_li_detail(context, caption, item):
@register.inclusion_tag('ishtar/blocks/window_field_flex_detail.html',
takes_context=True)
-def field_flex_detail(context, caption, item, small=False):
- size = None
- if small:
+def field_flex_detail(context, caption, item, size=None):
+ if size == "large":
+ size = "full"
+ elif size:
size = 2
return field_detail(context, caption, item, size=size)
diff --git a/ishtar_common/templatetags/window_tables.py b/ishtar_common/templatetags/window_tables.py
index ab60f7eeb..078afca62 100644
--- a/ishtar_common/templatetags/window_tables.py
+++ b/ishtar_common/templatetags/window_tables.py
@@ -19,7 +19,8 @@ from archaeological_operations.models import Operation, ArchaeologicalSite, \
from archaeological_context_records.models import ContextRecord, \
RecordRelationView, RecordRelations as CRRecordRelations
from archaeological_finds.models import Find, FindUpstreamTreatments, \
- FindDownstreamTreatments, FindTreatments, TreatmentFile, Treatment
+ FindDownstreamTreatments, FindTreatments, TreatmentFile, Treatment, \
+ FindInsideContainer
from archaeological_warehouse.models import Container, Warehouse
register = template.Library()
@@ -55,6 +56,8 @@ ASSOCIATED_MODELS['finds_upstreamtreatments'] = (
FindUpstreamTreatments, 'get-upstreamtreatment', '')
ASSOCIATED_MODELS['finds_downstreamtreatments'] = (
FindDownstreamTreatments, 'get-downstreamtreatment', '')
+ASSOCIATED_MODELS['finds_inside_container'] = (
+ FindInsideContainer, 'get-find-inside-container', '')
ASSOCIATED_MODELS['treatments'] = (
FindTreatments, 'get-treatment', '')
ASSOCIATED_MODELS['base_treatments'] = (
diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py
index 2eacce013..f18544ea9 100644
--- a/ishtar_common/utils.py
+++ b/ishtar_common/utils.py
@@ -740,7 +740,8 @@ def _post_save_geo(sender, **kwargs):
current_source = str(instance.__class__._meta.verbose_name)
modified = False
- if hasattr(instance, 'multi_polygon'):
+ if hasattr(instance, 'multi_polygon') and not getattr(
+ instance, "DISABLE_POLYGONS", False):
if instance.multi_polygon_source_item and \
instance.multi_polygon_source_item != current_source: # refetch
instance.multi_polygon = None