summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2019-02-19 17:36:25 +0100
committerÉtienne Loks <etienne.loks@iggdrasil.net>2019-04-24 19:38:56 +0200
commit4f63ed814f96bbc68c22c98b799c3911d62aa737 (patch)
tree1ac2de99841b7a28fee128a68700f59458594f14
parentb6d3f2dc4a24bb055b73b7c80815685572a83086 (diff)
downloadIshtar-4f63ed814f96bbc68c22c98b799c3911d62aa737.tar.bz2
Ishtar-4f63ed814f96bbc68c22c98b799c3911d62aa737.zip
Geo: manage container and warehouse - manage geo save dependencies
-rw-r--r--archaeological_context_records/migrations/0043_auto_20190218_1808.py83
-rw-r--r--archaeological_context_records/models.py51
-rw-r--r--archaeological_finds/migrations/0064_auto_20190218_1808.py182
-rw-r--r--archaeological_finds/models_finds.py36
-rw-r--r--archaeological_operations/migrations/0053_auto_20190218_1808.py147
-rw-r--r--archaeological_operations/models.py71
-rw-r--r--archaeological_warehouse/forms.py31
-rw-r--r--archaeological_warehouse/migrations/0034_auto_20190218_1808.py168
-rw-r--r--archaeological_warehouse/models.py30
-rw-r--r--archaeological_warehouse/templates/ishtar/sheet_container.html12
-rw-r--r--archaeological_warehouse/templates/ishtar/sheet_warehouse.html15
-rw-r--r--ishtar_common/forms.py3
-rw-r--r--ishtar_common/migrations/0088_auto_20190218_1808.py165
-rw-r--r--ishtar_common/models.py528
-rw-r--r--ishtar_common/templates/ishtar/blocks/sheet_address_section.html14
-rw-r--r--ishtar_common/templates/ishtar/blocks/sheet_coordinates.html6
-rw-r--r--ishtar_common/tests.py1
-rw-r--r--ishtar_common/utils.py104
18 files changed, 1255 insertions, 392 deletions
diff --git a/archaeological_context_records/migrations/0043_auto_20190218_1808.py b/archaeological_context_records/migrations/0043_auto_20190218_1808.py
new file mode 100644
index 000000000..7cc1a89b6
--- /dev/null
+++ b/archaeological_context_records/migrations/0043_auto_20190218_1808.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.10 on 2019-02-18 18:08
+from __future__ import unicode_literals
+
+import django.contrib.gis.db.models.fields
+from django.db import migrations, models
+import django.db.models.deletion
+import ishtar_common.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_context_records', '0042_auto_20190206_1423'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='contextrecord',
+ name='multi_polygon_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Multi polygon source item'),
+ ),
+ migrations.AddField(
+ model_name='contextrecord',
+ name='point_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Point source item'),
+ ),
+ migrations.AddField(
+ model_name='historicalcontextrecord',
+ name='multi_polygon_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Multi polygon source item'),
+ ),
+ migrations.AddField(
+ model_name='historicalcontextrecord',
+ name='point_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Point source item'),
+ ),
+ migrations.AlterField(
+ model_name='contextrecord',
+ name='main_image',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='main_image_context_records', to='ishtar_common.Document', verbose_name='Image principale'),
+ ),
+ migrations.AlterField(
+ model_name='contextrecord',
+ name='point',
+ field=django.contrib.gis.db.models.fields.PointField(blank=True, dim=3, null=True, srid=4326, verbose_name='Point'),
+ ),
+ migrations.AlterField(
+ model_name='contextrecord',
+ name='relation_image',
+ field=models.FileField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Image des relations (SVG g\xe9n\xe9r\xe9)'),
+ ),
+ migrations.AlterField(
+ model_name='contextrecord',
+ name='x',
+ field=models.FloatField(blank=True, null=True, verbose_name='X/Long'),
+ ),
+ migrations.AlterField(
+ model_name='contextrecord',
+ name='y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Y/Lat'),
+ ),
+ migrations.AlterField(
+ model_name='historicalcontextrecord',
+ name='point',
+ field=django.contrib.gis.db.models.fields.PointField(blank=True, dim=3, null=True, srid=4326, verbose_name='Point'),
+ ),
+ migrations.AlterField(
+ model_name='historicalcontextrecord',
+ name='relation_image',
+ field=models.TextField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Image des relations (SVG g\xe9n\xe9r\xe9)'),
+ ),
+ migrations.AlterField(
+ model_name='historicalcontextrecord',
+ name='x',
+ field=models.FloatField(blank=True, null=True, verbose_name='X/Long'),
+ ),
+ migrations.AlterField(
+ model_name='historicalcontextrecord',
+ name='y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Y/Lat'),
+ ),
+ ]
diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py
index 71551a49d..a2793ac67 100644
--- a/archaeological_context_records/models.py
+++ b/archaeological_context_records/models.py
@@ -35,7 +35,7 @@ from ishtar_common.models import Document, GeneralType, \
GeneralRelationType, GeneralRecordRelations, post_delete_record_relation,\
post_save_cache, ValueGetter, BulkUpdatedItem, ExternalIdManager, \
RelationItem, Town, get_current_profile, document_attached_changed, \
- HistoryModel, SearchAltName, SpatialReferenceSystem
+ HistoryModel, SearchAltName, GeoItem
from archaeological_operations.models import Operation, Period, Parcel, \
ArchaeologicalSite
@@ -269,7 +269,7 @@ class CRBulkView(object):
"""
-class ContextRecord(BulkUpdatedItem, BaseHistorizedItem,
+class ContextRecord(BulkUpdatedItem, BaseHistorizedItem, GeoItem,
OwnPerms, ValueGetter, ShortMenuItem, RelationItem):
SHOW_URL = 'show-contextrecord'
SLUG = 'contextrecord'
@@ -471,30 +471,7 @@ class ContextRecord(BulkUpdatedItem, BaseHistorizedItem,
verbose_name=_(u"Excavation technique"))
related_context_records = models.ManyToManyField(
'ContextRecord', through='RecordRelations', blank=True)
- x = models.FloatField(_(u'X'), blank=True, null=True)
- y = models.FloatField(_(u'Y'), blank=True, null=True)
- z = models.FloatField(_(u'Z'), blank=True, null=True)
- estimated_error_x = models.FloatField(_(u'Estimated error for X'),
- blank=True, null=True)
- estimated_error_y = models.FloatField(_(u'Estimated error for Y'),
- blank=True, null=True)
- estimated_error_z = models.FloatField(_(u'Estimated error for Z'),
- blank=True, null=True)
- spatial_reference_system = models.ForeignKey(
- SpatialReferenceSystem, verbose_name=_(u"Spatial Reference System"),
- blank=True, null=True)
- point_2d = models.PointField(_(u"Point (2D)"), blank=True, null=True)
- point = models.PointField(_(u"Point (3D)"), blank=True, null=True, dim=3)
- point_source = models.CharField(
- _(u"Point source"),
- choices=(('T', _(u"Town")), ('P', _(u"Precise"))), max_length=1,
- blank=True, null=True)
- multi_polygon = models.MultiPolygonField(_(u"Multi polygon"), blank=True,
- null=True)
- multi_polygon_source = models.CharField(
- _(u"Multi-polygon source"),
- choices=(('T', _(u"Town")), ('P', _(u"Precise"))), max_length=1,
- blank=True, null=True)
+
documents = models.ManyToManyField(
Document, related_name='context_records', verbose_name=_(u"Documents"),
blank=True)
@@ -533,14 +510,32 @@ class ContextRecord(BulkUpdatedItem, BaseHistorizedItem,
def get_town_centroid(self):
if self.town:
- return self.town.center
+ return self.town.center, self._meta.verbose_name
+ if self.archaeological_site:
+ centroid = self.archaeological_site.get_town_centroid()
+ if centroid:
+ return centroid
return self.operation.get_town_centroid()
def get_town_polygons(self):
if self.town:
- return self.town.limit
+ return self.town.limit, self._meta.verbose_name
+ if self.archaeological_site:
+ polys = self.archaeological_site.get_town_centroid()
+ if polys:
+ return polys
return self.operation.get_town_polygons()
+ def get_precise_points(self):
+ precise_points = super(ContextRecord, self).get_precise_points()
+ if precise_points:
+ return precise_points
+ if self.archaeological_site:
+ precise_points = self.archaeological_site.get_precise_points()
+ if precise_points:
+ return precise_points
+ return self.operation.get_precise_points()
+
@classmethod
def cached_label_bulk_update(cls, operation_id=None, parcel_id=None,
transaction_id=None):
diff --git a/archaeological_finds/migrations/0064_auto_20190218_1808.py b/archaeological_finds/migrations/0064_auto_20190218_1808.py
new file mode 100644
index 000000000..2b13c0eee
--- /dev/null
+++ b/archaeological_finds/migrations/0064_auto_20190218_1808.py
@@ -0,0 +1,182 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.10 on 2019-02-18 18:08
+from __future__ import unicode_literals
+
+import django.contrib.gis.db.models.fields
+from django.db import migrations, models
+import django.db.models.deletion
+import ishtar_common.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_finds', '0063_auto_20190206_1423'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='conservatorystate',
+ options={'ordering': ('order', 'label'), 'verbose_name': "Type d'\xe9tat de conservation", 'verbose_name_plural': "Types d'\xe9tat de conservation"},
+ ),
+ migrations.AddField(
+ model_name='basefind',
+ name='multi_polygon_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Multi polygon source item'),
+ ),
+ migrations.AddField(
+ model_name='basefind',
+ name='point_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Point source item'),
+ ),
+ migrations.AddField(
+ model_name='historicalbasefind',
+ name='multi_polygon_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Multi polygon source item'),
+ ),
+ migrations.AddField(
+ model_name='historicalbasefind',
+ name='point_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Point source item'),
+ ),
+ migrations.AlterField(
+ model_name='basefind',
+ name='point',
+ field=django.contrib.gis.db.models.fields.PointField(blank=True, dim=3, null=True, srid=4326, verbose_name='Point'),
+ ),
+ migrations.AlterField(
+ model_name='basefind',
+ name='spatial_reference_system',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.SpatialReferenceSystem', verbose_name='Syst\xe8me de r\xe9f\xe9rence spatiale'),
+ ),
+ migrations.AlterField(
+ model_name='basefind',
+ name='x',
+ field=models.FloatField(blank=True, null=True, verbose_name='X/Long'),
+ ),
+ migrations.AlterField(
+ model_name='basefind',
+ name='y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Y/Lat'),
+ ),
+ migrations.AlterField(
+ model_name='find',
+ name='circumference',
+ field=models.FloatField(blank=True, null=True, verbose_name='Circonf\xe9rence (cm)'),
+ ),
+ migrations.AlterField(
+ model_name='find',
+ name='container_ref',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='finds_ref', to='archaeological_warehouse.Container', verbose_name='Contenant de r\xe9f\xe9rence'),
+ ),
+ migrations.AlterField(
+ model_name='find',
+ name='main_image',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='main_image_finds', to='ishtar_common.Document', verbose_name='Image principale'),
+ ),
+ migrations.AlterField(
+ model_name='find',
+ name='treatments',
+ field=models.ManyToManyField(blank=True, help_text="Traitements associ\xe9s quand il n'y a pas de cr\xe9ation de nouveau mobilier", related_name='finds', to='archaeological_finds.Treatment', verbose_name='Traitements'),
+ ),
+ migrations.AlterField(
+ model_name='findbasket',
+ name='shared_with',
+ field=models.ManyToManyField(blank=True, related_name='shared_findbaskets', to='ishtar_common.IshtarUser', verbose_name='Partag\xe9 (lecture) avec'),
+ ),
+ migrations.AlterField(
+ model_name='findbasket',
+ name='shared_write_with',
+ field=models.ManyToManyField(blank=True, related_name='shared_write_findbaskets', to='ishtar_common.IshtarUser', verbose_name='Partag\xe9 (lecture/\xe9dition) avec'),
+ ),
+ migrations.AlterField(
+ model_name='historicalbasefind',
+ name='point',
+ field=django.contrib.gis.db.models.fields.PointField(blank=True, dim=3, null=True, srid=4326, verbose_name='Point'),
+ ),
+ migrations.AlterField(
+ model_name='historicalbasefind',
+ name='x',
+ field=models.FloatField(blank=True, null=True, verbose_name='X/Long'),
+ ),
+ migrations.AlterField(
+ model_name='historicalbasefind',
+ name='y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Y/Lat'),
+ ),
+ migrations.AlterField(
+ model_name='historicalfind',
+ name='circumference',
+ field=models.FloatField(blank=True, null=True, verbose_name='Circonf\xe9rence (cm)'),
+ ),
+ migrations.AlterField(
+ model_name='historicaltreatment',
+ name='executed',
+ field=models.BooleanField(default=False, verbose_name='Le traitement a \xe9t\xe9 r\xe9alis\xe9'),
+ ),
+ migrations.AlterField(
+ model_name='historicaltreatment',
+ name='image',
+ field=models.TextField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=255, null=True),
+ ),
+ migrations.AlterField(
+ model_name='historicaltreatment',
+ name='thumbnail',
+ field=models.TextField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=255, null=True),
+ ),
+ migrations.AlterField(
+ model_name='treatment',
+ name='executed',
+ field=models.BooleanField(default=False, verbose_name='Le traitement a \xe9t\xe9 r\xe9alis\xe9'),
+ ),
+ migrations.AlterField(
+ model_name='treatment',
+ name='image',
+ field=models.ImageField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=255, null=True, upload_to=ishtar_common.models.get_image_path),
+ ),
+ migrations.AlterField(
+ model_name='treatment',
+ name='main_image',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='main_image_treatments', to='ishtar_common.Document', verbose_name='Image principale'),
+ ),
+ migrations.AlterField(
+ model_name='treatment',
+ name='thumbnail',
+ field=models.ImageField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=255, null=True, upload_to=ishtar_common.models.get_image_path),
+ ),
+ migrations.AlterField(
+ model_name='treatmentfile',
+ name='main_image',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='main_image_treatment_files', to='ishtar_common.Document', verbose_name='Image principale'),
+ ),
+ migrations.AlterField(
+ model_name='treatmentstate',
+ name='executed',
+ field=models.BooleanField(default=False, verbose_name='Le traitement est r\xe9alis\xe9'),
+ ),
+ migrations.AlterField(
+ model_name='treatmenttype',
+ name='change_current_location',
+ field=models.BooleanField(default=False, help_text='Le traitement change la localisation actuelle.', verbose_name='Change la localisation actuelle'),
+ ),
+ migrations.AlterField(
+ model_name='treatmenttype',
+ name='change_reference_location',
+ field=models.BooleanField(default=False, help_text='Le traitement change la localisation de r\xe9f\xe9rence.', verbose_name='Change la localisation de r\xe9f\xe9rence'),
+ ),
+ migrations.AlterField(
+ model_name='treatmenttype',
+ name='create_new_find',
+ field=models.BooleanField(default=False, help_text="Si mis \xe0 Vrai quand le traitement est appliqu\xe9 une nouvelle version de l'objet sera cr\xe9\xe9e.", verbose_name='Cr\xe9er un nouvel \xe9l\xe9ment'),
+ ),
+ migrations.AlterField(
+ model_name='treatmenttype',
+ name='destructive',
+ field=models.BooleanField(default=False, verbose_name='Destructif'),
+ ),
+ migrations.AlterField(
+ model_name='treatmenttype',
+ name='restore_reference_location',
+ field=models.BooleanField(default=False, help_text='Le traitement restaure la localisation de r\xe9f\xe9rence sur la localisation actuelle.', verbose_name='Restaure la localisation de r\xe9f\xe9rence'),
+ ),
+ ]
diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py
index 9d16cb66b..01f4b719f 100644
--- a/archaeological_finds/models_finds.py
+++ b/archaeological_finds/models_finds.py
@@ -39,7 +39,7 @@ from ishtar_common.models import Document, GeneralType, \
HierarchicalType, BaseHistorizedItem, LightHistorizedItem, \
HistoricalRecords, OwnPerms, Person, Basket, post_save_cache, \
ValueGetter, get_current_profile, IshtarSiteProfile, PRIVATE_FIELDS, \
- SpatialReferenceSystem, BulkUpdatedItem, ExternalIdManager, QuickAction, \
+ GeoItem, BulkUpdatedItem, ExternalIdManager, QuickAction, \
MainItem, document_attached_changed, HistoryModel, DynamicRequest, \
SearchAltName
@@ -264,7 +264,7 @@ class BFBulkView(object):
"""
-class BaseFind(BulkUpdatedItem, BaseHistorizedItem, OwnPerms):
+class BaseFind(BulkUpdatedItem, BaseHistorizedItem, GeoItem, OwnPerms):
EXTERNAL_ID_KEY = 'base_find_external_id'
EXTERNAL_ID_DEPENDENCIES = ['find']
SLUG = 'basefind'
@@ -292,32 +292,8 @@ class BaseFind(BulkUpdatedItem, BaseHistorizedItem, OwnPerms):
topographic_localisation = models.CharField(
_(u"Point of topographic reference"), blank=True, null=True,
max_length=120)
- x = models.FloatField(_(u'X'), blank=True, null=True)
- y = models.FloatField(_(u'Y'), blank=True, null=True)
- z = models.FloatField(_(u'Z'), blank=True, null=True)
- estimated_error_x = models.FloatField(_(u'Estimated error for X'),
- blank=True, null=True)
- estimated_error_y = models.FloatField(_(u'Estimated error for Y'),
- blank=True, null=True)
- estimated_error_z = models.FloatField(_(u'Estimated error for Z'),
- blank=True, null=True)
- spatial_reference_system = models.ForeignKey(
- SpatialReferenceSystem, verbose_name=_(u"Spatial Reference System"),
- on_delete=models.SET_NULL,
- blank=True, null=True)
- point_2d = models.PointField(_(u"Point (2D)"), blank=True, null=True)
- point = models.PointField(_(u"Point (3D)"), blank=True, null=True, dim=3)
- point_source = models.CharField(
- _(u"Point source"),
- choices=(('T', _(u"Town")), ('P', _(u"Precise"))), max_length=1,
- blank=True, null=True)
+ # gis
line = models.LineStringField(_(u"Line"), blank=True, null=True)
- multi_polygon = models.MultiPolygonField(_(u"Multi polygon"), blank=True,
- null=True)
- multi_polygon_source = models.CharField(
- _(u"Multi-polygon source"),
- choices=(('T', _(u"Town")), ('P', _(u"Precise"))), max_length=1,
- blank=True, null=True)
cache_short_id = models.TextField(
_(u"Short ID"), blank=True, null=True, db_index=True,
help_text=_(u"Cached value - do not edit"))
@@ -369,6 +345,12 @@ class BaseFind(BulkUpdatedItem, BaseHistorizedItem, OwnPerms):
def get_town_polygons(self):
return self.context_record.get_town_polygons()
+ def get_precise_points(self):
+ precise_points = super(BaseFind, self).get_precise_points()
+ if precise_points:
+ return precise_points
+ return self.context_record.get_precise_points()
+
def generate_index(self):
"""
Generate index based on operation or context record (based on
diff --git a/archaeological_operations/migrations/0053_auto_20190218_1808.py b/archaeological_operations/migrations/0053_auto_20190218_1808.py
new file mode 100644
index 000000000..0f553f131
--- /dev/null
+++ b/archaeological_operations/migrations/0053_auto_20190218_1808.py
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.10 on 2019-02-18 18:08
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import ishtar_common.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_operations', '0052_auto_20190206_1423'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='multi_polygon_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Multi polygon source item'),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='point_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Point source item'),
+ ),
+ migrations.AddField(
+ model_name='historicalarchaeologicalsite',
+ name='multi_polygon_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Multi polygon source item'),
+ ),
+ migrations.AddField(
+ model_name='historicalarchaeologicalsite',
+ name='point_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Point source item'),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='multi_polygon_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Multi polygon source item'),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='point_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Point source item'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='multi_polygon_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Multi polygon source item'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='point_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Point source item'),
+ ),
+ migrations.AlterField(
+ model_name='archaeologicalsite',
+ name='affmar_number',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Num\xe9ro AffMar'),
+ ),
+ migrations.AlterField(
+ model_name='archaeologicalsite',
+ name='drassm_number',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Num\xe9ro DRASSM'),
+ ),
+ migrations.AlterField(
+ model_name='archaeologicalsite',
+ name='main_image',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='main_image_sites', to='ishtar_common.Document', verbose_name='Image principale'),
+ ),
+ migrations.AlterField(
+ model_name='archaeologicalsite',
+ name='x',
+ field=models.FloatField(blank=True, null=True, verbose_name='X/Long'),
+ ),
+ migrations.AlterField(
+ model_name='archaeologicalsite',
+ name='y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Y/Lat'),
+ ),
+ migrations.AlterField(
+ model_name='historicalarchaeologicalsite',
+ name='affmar_number',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Num\xe9ro AffMar'),
+ ),
+ migrations.AlterField(
+ model_name='historicalarchaeologicalsite',
+ name='drassm_number',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Num\xe9ro DRASSM'),
+ ),
+ migrations.AlterField(
+ model_name='historicalarchaeologicalsite',
+ name='x',
+ field=models.FloatField(blank=True, null=True, verbose_name='X/Long'),
+ ),
+ migrations.AlterField(
+ model_name='historicalarchaeologicalsite',
+ name='y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Y/Lat'),
+ ),
+ migrations.AlterField(
+ model_name='historicaloperation',
+ name='drassm_code',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Code DRASSM'),
+ ),
+ migrations.AlterField(
+ model_name='historicaloperation',
+ name='relation_image',
+ field=models.TextField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Image des relations (SVG g\xe9n\xe9r\xe9)'),
+ ),
+ migrations.AlterField(
+ model_name='historicaloperation',
+ name='x',
+ field=models.FloatField(blank=True, null=True, verbose_name='X/Long'),
+ ),
+ migrations.AlterField(
+ model_name='historicaloperation',
+ name='y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Y/Lat'),
+ ),
+ migrations.AlterField(
+ model_name='operation',
+ name='drassm_code',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Code DRASSM'),
+ ),
+ migrations.AlterField(
+ model_name='operation',
+ name='main_image',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='main_image_operations', to='ishtar_common.Document', verbose_name='Image principale'),
+ ),
+ migrations.AlterField(
+ model_name='operation',
+ name='relation_image',
+ field=models.FileField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Image des relations (SVG g\xe9n\xe9r\xe9)'),
+ ),
+ migrations.AlterField(
+ model_name='operation',
+ name='x',
+ field=models.FloatField(blank=True, null=True, verbose_name='X/Long'),
+ ),
+ migrations.AlterField(
+ model_name='operation',
+ name='y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Y/Lat'),
+ ),
+ ]
diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py
index b76e4841e..d79bce7f3 100644
--- a/archaeological_operations/models.py
+++ b/archaeological_operations/models.py
@@ -39,7 +39,7 @@ from ishtar_common.models import BaseHistorizedItem, Dashboard, \
post_delete_record_relation, post_save_cache, RelationItem, \
ShortMenuItem, SourceType, Town, ValueGetter, get_current_profile, \
document_attached_changed, HistoryModel, SearchAltName, \
- SpatialReferenceSystem
+ GeoItem
from ishtar_common.utils import cached_label_changed, \
force_cached_label_changed, mode, m2m_historization_changed, post_save_geo
@@ -107,7 +107,7 @@ post_save.connect(post_save_cache, sender=RecordQualityType)
post_delete.connect(post_save_cache, sender=RecordQualityType)
-class ArchaeologicalSite(BaseHistorizedItem, OwnPerms, ValueGetter,
+class ArchaeologicalSite(BaseHistorizedItem, GeoItem, OwnPerms, ValueGetter,
ShortMenuItem):
SHOW_URL = 'show-site'
TABLE_COLS = ['reference', 'name', 'towns_label', 'periods', 'remains']
@@ -262,32 +262,6 @@ class ArchaeologicalSite(BaseHistorizedItem, OwnPerms, ValueGetter,
drassm_number = models.CharField(_(u"DRASSM number"), max_length=100,
null=True, blank=True)
- # gis
- x = models.FloatField(_(u'X'), blank=True, null=True)
- y = models.FloatField(_(u'Y'), blank=True, null=True)
- z = models.FloatField(_(u'Z'), blank=True, null=True)
- estimated_error_x = models.FloatField(_(u'Estimated error for X'),
- blank=True, null=True)
- estimated_error_y = models.FloatField(_(u'Estimated error for Y'),
- blank=True, null=True)
- estimated_error_z = models.FloatField(_(u'Estimated error for Z'),
- blank=True, null=True)
- spatial_reference_system = models.ForeignKey(
- SpatialReferenceSystem, verbose_name=_(u"Spatial Reference System"),
- blank=True, null=True)
- point = models.PointField(_(u"Point"), blank=True, null=True, dim=3)
- point_2d = models.PointField(_(u"Point (2D)"), blank=True, null=True)
- point_source = models.CharField(
- _(u"Point source"),
- choices=(('T', _(u"Town")), ('P', _(u"Precise"))), max_length=1,
- blank=True, null=True)
- multi_polygon = models.MultiPolygonField(_(u"Multi polygon"), blank=True,
- null=True)
- multi_polygon_source = models.CharField(
- _(u"Multi-polygon source"),
- choices=(('T', _(u"Town")), ('P', _(u"Precise"))), max_length=1,
- blank=True, null=True)
-
documents = models.ManyToManyField(
Document, related_name="sites", verbose_name=_(u"Documents"),
blank=True)
@@ -403,15 +377,15 @@ class ArchaeologicalSite(BaseHistorizedItem, OwnPerms, ValueGetter,
q = self.towns.filter(center__isnull=False).annotate(
centroid=Centroid(Union('center'))).all()
if not q.count():
- return None
- return q.all()[0].centroid
+ return
+ return q.all()[0].centroid, self._meta.verbose_name
def get_town_polygons(self):
q = self.towns.filter(limit__isnull=False).annotate(
poly=Union('limit')).all()
if not q.count():
- return None
- return q.all()[0].poly
+ return
+ return q.all()[0].poly, self._meta.verbose_name
def _get_base_image_path(self):
return u"{}/{}".format(self.SLUG, self.reference)
@@ -535,7 +509,7 @@ class OperationManager(models.GeoManager):
return self.get(code_patriarche=txt_idx)
-class Operation(ClosedItem, BaseHistorizedItem, OwnPerms, ValueGetter,
+class Operation(ClosedItem, BaseHistorizedItem, GeoItem, OwnPerms, ValueGetter,
ShortMenuItem, DashboardFormItem, RelationItem):
SHOW_URL = 'show-operation'
TABLE_COLS = ['year', 'towns_label', 'common_name', 'operation_type',
@@ -946,31 +920,6 @@ class Operation(ClosedItem, BaseHistorizedItem, OwnPerms, ValueGetter,
name_of_the_protagonist = models.TextField(_(u"Name of the protagonist"),
blank=True, null=True)
- # gis
- x = models.FloatField(_(u'X'), blank=True, null=True)
- y = models.FloatField(_(u'Y'), blank=True, null=True)
- z = models.FloatField(_(u'Z'), blank=True, null=True)
- estimated_error_x = models.FloatField(_(u'Estimated error for X'),
- blank=True, null=True)
- estimated_error_y = models.FloatField(_(u'Estimated error for Y'),
- blank=True, null=True)
- estimated_error_z = models.FloatField(_(u'Estimated error for Z'),
- blank=True, null=True)
- spatial_reference_system = models.ForeignKey(
- SpatialReferenceSystem, verbose_name=_(u"Spatial Reference System"),
- blank=True, null=True)
- point = models.PointField(_(u"Point"), blank=True, null=True, dim=3)
- point_2d = models.PointField(_(u"Point (2D)"), blank=True, null=True)
- point_source = models.CharField(
- _(u"Point source"),
- choices=(('T', _(u"Town")), ('P', _(u"Precise"))), max_length=1,
- blank=True, null=True)
- multi_polygon = models.MultiPolygonField(_(u"Multi polygon"), blank=True,
- null=True)
- multi_polygon_source = models.CharField(
- _(u"Multi-polygon source"),
- choices=(('T', _(u"Town")), ('P', _(u"Precise"))), max_length=1,
- blank=True, null=True)
history = HistoricalRecords(bases=[HistoryModel])
class Meta:
@@ -1141,15 +1090,15 @@ class Operation(ClosedItem, BaseHistorizedItem, OwnPerms, ValueGetter,
q = self.towns.filter(center__isnull=False).annotate(
centroid=Centroid(Union('center'))).all()
if not q.count():
- return None
- return q.all()[0].centroid
+ return
+ return q.all()[0].centroid, self._meta.verbose_name
def get_town_polygons(self):
q = self.towns.filter(limit__isnull=False).annotate(
poly=Union('limit')).all()
if not q.count():
return None
- return q.all()[0].poly
+ return q.all()[0].poly, self._meta.verbose_name
def context_record_relations_q(self):
from archaeological_context_records.models \
diff --git a/archaeological_warehouse/forms.py b/archaeological_warehouse/forms.py
index 7a84b2833..4550970c9 100644
--- a/archaeological_warehouse/forms.py
+++ b/archaeological_warehouse/forms.py
@@ -25,7 +25,7 @@ from django.conf import settings
from django.forms.formsets import formset_factory
from django.utils.translation import ugettext_lazy as _
-from ishtar_common.models import Person, valid_id
+from ishtar_common.models import Person, valid_id, Town, SpatialReferenceSystem
from archaeological_operations.models import ArchaeologicalSite
from archaeological_context_records.models import ContextRecord
from archaeological_finds.models import TreatmentType, FindBasket, \
@@ -40,7 +40,8 @@ from bootstrap_datepicker.widgets import DatePicker
from ishtar_common.forms import name_validator, reverse_lazy, \
get_form_selection, ManageOldType, FinalForm, FormSet, \
- CustomForm, FieldType, HistorySelect
+ CustomForm, FieldType, HistorySelect, FormHeader
+from ishtar_common.forms_common import get_town_field
from archaeological_finds.forms import FindMultipleFormSelection, \
SelectFindBasketForm
@@ -115,16 +116,21 @@ class WarehouseFormSelection(forms.Form):
label="", required=False,
widget=widgets.DataTable(
reverse_lazy('get-warehouse'),
- WarehouseSelect, models.Warehouse),
+ WarehouseSelect, models.Warehouse, gallery=True, map=True),
validators=[valid_id(models.Warehouse)])
class WarehouseForm(CustomForm, ManageOldType, forms.Form):
+ HEADERS = {}
form_label = _(u"Warehouse")
form_admin_name = _(u"Warehouse - 010 - General")
form_slug = "warehouse-010-general"
- associated_models = {'warehouse_type': models.WarehouseType,
- 'person_in_charge': Person}
+ associated_models = {
+ 'warehouse_type': models.WarehouseType,
+ 'person_in_charge': Person,
+ 'precise_town': Town,
+ 'spatial_reference_system': SpatialReferenceSystem
+ }
name = forms.CharField(label=_(u"Name"), max_length=200,
validators=[name_validator])
@@ -145,14 +151,23 @@ class WarehouseForm(CustomForm, ManageOldType, forms.Form):
widget=forms.Textarea, required=False)
postal_code = forms.CharField(label=_(u"Postal code"), max_length=10,
required=False)
- town = forms.CharField(label=_(u"Town"), max_length=30, required=False)
+ town = forms.CharField(label=_(u"Town (freeform)"), max_length=150,
+ required=False)
+ precise_town = get_town_field(required=False)
country = forms.CharField(label=_(u"Country"), max_length=30,
required=False)
phone = forms.CharField(label=_(u"Phone"), max_length=18, required=False)
mobile_phone = forms.CharField(label=_(u"Mobile phone"), max_length=18,
required=False)
+ HEADERS['x'] = FormHeader(_(u"Coordinates"))
+ x = forms.FloatField(label=_(u"X"), required=False)
+ y = forms.FloatField(label=_(u"Y"), required=False)
+ spatial_reference_system = forms.ChoiceField(
+ label=_(u"Spatial Reference System"), required=False, choices=[])
+
TYPES = [
- FieldType('warehouse_type', models.WarehouseType)
+ FieldType('warehouse_type', models.WarehouseType),
+ FieldType('spatial_reference_system', SpatialReferenceSystem)
]
def __init__(self, *args, **kwargs):
@@ -347,7 +362,7 @@ ContainerFormSelection = get_form_selection(
MainContainerFormSelection = get_form_selection(
'ContainerFormSelection', _(u"Container search"), 'pk',
models.Container, ContainerSelect, 'get-container',
- _(u"You should select a container.")
+ _(u"You should select a container."), gallery=True, map=True
)
diff --git a/archaeological_warehouse/migrations/0034_auto_20190218_1808.py b/archaeological_warehouse/migrations/0034_auto_20190218_1808.py
new file mode 100644
index 000000000..2832e1706
--- /dev/null
+++ b/archaeological_warehouse/migrations/0034_auto_20190218_1808.py
@@ -0,0 +1,168 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.10 on 2019-02-18 18:08
+from __future__ import unicode_literals
+
+import django.contrib.gis.db.models.fields
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0088_auto_20190218_1808'),
+ ('archaeological_warehouse', '0033_auto_20190212_1524'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='container',
+ name='estimated_error_x',
+ field=models.FloatField(blank=True, null=True, verbose_name='Erreur estim\xe9e pour X'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='estimated_error_y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Erreur estim\xe9e pour Y'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='estimated_error_z',
+ field=models.FloatField(blank=True, null=True, verbose_name='Erreur estim\xe9e pour Z'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='multi_polygon',
+ field=django.contrib.gis.db.models.fields.MultiPolygonField(blank=True, null=True, srid=4326, verbose_name='Polygones multi-parties'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='multi_polygon_source',
+ field=models.CharField(blank=True, choices=[(b'T', 'Commune'), (b'P', 'Precise')], max_length=1, null=True, verbose_name='Multi-polygon source'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='multi_polygon_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Multi polygon source item'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='point',
+ field=django.contrib.gis.db.models.fields.PointField(blank=True, dim=3, null=True, srid=4326, verbose_name='Point'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='point_2d',
+ field=django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326, verbose_name='Point (2D)'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='point_source',
+ field=models.CharField(blank=True, choices=[(b'T', 'Commune'), (b'P', 'Precise')], max_length=1, null=True, verbose_name='Point source'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='point_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Point source item'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='spatial_reference_system',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.SpatialReferenceSystem', verbose_name='Syst\xe8me de r\xe9f\xe9rence spatiale'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='x',
+ field=models.FloatField(blank=True, null=True, verbose_name='X/Long'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Y/Lat'),
+ ),
+ migrations.AddField(
+ model_name='container',
+ name='z',
+ field=models.FloatField(blank=True, null=True, verbose_name='Z'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='estimated_error_x',
+ field=models.FloatField(blank=True, null=True, verbose_name='Erreur estim\xe9e pour X'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='estimated_error_y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Erreur estim\xe9e pour Y'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='estimated_error_z',
+ field=models.FloatField(blank=True, null=True, verbose_name='Erreur estim\xe9e pour Z'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='multi_polygon',
+ field=django.contrib.gis.db.models.fields.MultiPolygonField(blank=True, null=True, srid=4326, verbose_name='Polygones multi-parties'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='multi_polygon_source',
+ field=models.CharField(blank=True, choices=[(b'T', 'Commune'), (b'P', 'Precise')], max_length=1, null=True, verbose_name='Multi-polygon source'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='multi_polygon_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Multi polygon source item'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='point',
+ field=django.contrib.gis.db.models.fields.PointField(blank=True, dim=3, null=True, srid=4326, verbose_name='Point'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='point_2d',
+ field=django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326, verbose_name='Point (2D)'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='point_source',
+ field=models.CharField(blank=True, choices=[(b'T', 'Commune'), (b'P', 'Precise')], max_length=1, null=True, verbose_name='Point source'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='point_source_item',
+ field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Point source item'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='precise_town',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.Town', verbose_name='Town (precise)'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='spatial_reference_system',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.SpatialReferenceSystem', verbose_name='Syst\xe8me de r\xe9f\xe9rence spatiale'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='x',
+ field=models.FloatField(blank=True, null=True, verbose_name='X/Long'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='y',
+ field=models.FloatField(blank=True, null=True, verbose_name='Y/Lat'),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='z',
+ field=models.FloatField(blank=True, null=True, verbose_name='Z'),
+ ),
+ migrations.AlterField(
+ model_name='warehouse',
+ name='town',
+ field=models.CharField(blank=True, max_length=150, null=True, verbose_name='Town (freeform)'),
+ ),
+ ]
diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py
index c80b4476b..795c879e4 100644
--- a/archaeological_warehouse/models.py
+++ b/archaeological_warehouse/models.py
@@ -31,8 +31,8 @@ from ishtar_common.data_importer import post_importer_action
from ishtar_common.models import Document, GeneralType, get_external_id, \
LightHistorizedItem, OwnPerms, Address, Person, post_save_cache, \
DashboardFormItem, ExternalIdManager, ShortMenuItem, \
- document_attached_changed, SearchAltName, DynamicRequest
-from ishtar_common.utils import cached_label_changed
+ document_attached_changed, SearchAltName, DynamicRequest, GeoItem
+from ishtar_common.utils import cached_label_changed, post_save_geo
class WarehouseType(GeneralType):
@@ -46,7 +46,7 @@ post_save.connect(post_save_cache, sender=WarehouseType)
post_delete.connect(post_save_cache, sender=WarehouseType)
-class Warehouse(Address, DashboardFormItem, OwnPerms,
+class Warehouse(Address, GeoItem, DashboardFormItem, OwnPerms,
ShortMenuItem):
SLUG = 'warehouse'
SHOW_URL = 'show-warehouse'
@@ -69,6 +69,7 @@ class Warehouse(Address, DashboardFormItem, OwnPerms,
'warehouse_type__label__iexact'
),
}
+ GEO_LABEL = "name"
objects = ExternalIdManager()
@@ -258,6 +259,8 @@ class Warehouse(Address, DashboardFormItem, OwnPerms,
m2m_changed.connect(document_attached_changed,
sender=Warehouse.documents.through)
+post_save.connect(post_save_geo, sender=Warehouse)
+
class Collection(LightHistorizedItem):
name = models.CharField(_(u"Name"), max_length=200,
@@ -319,7 +322,7 @@ post_save.connect(post_save_cache, sender=ContainerType)
post_delete.connect(post_save_cache, sender=ContainerType)
-class Container(LightHistorizedItem, OwnPerms):
+class Container(LightHistorizedItem, GeoItem, OwnPerms):
SLUG = 'container'
SHOW_URL = 'show-container'
TABLE_COLS = ['reference', 'container_type__label', 'cached_location',
@@ -350,6 +353,7 @@ class Container(LightHistorizedItem, OwnPerms):
'cached_division': _(u"Precise localisation"),
'container_type__label': _(u"Type")
}
+ GEO_LABEL = "cached_label"
CACHED_LABELS = ['cached_division', 'cached_label', 'cached_location', ]
# alternative names of fields for searches
@@ -555,6 +559,18 @@ class Container(LightHistorizedItem, OwnPerms):
Q(location__person_in_charge__ishtaruser=ishtaruser) | \
Q(responsible__person_in_charge__ishtaruser=ishtaruser)
+ def get_precise_points(self):
+ precise_points = super(Container, self).get_precise_points()
+ if precise_points:
+ return precise_points
+ return self.location.get_precise_points()
+
+ def get_town_centroid(self):
+ return self.location.get_town_centroid()
+
+ def get_town_polygons(self):
+ return self.location.get_town_polygons()
+
@property
def associated_filename(self):
filename = datetime.date.today().strftime('%Y-%m-%d')
@@ -747,8 +763,12 @@ class Container(LightHistorizedItem, OwnPerms):
loca.delete()
-post_save.connect(cached_label_changed, sender=Container)
+def container_post_save(sender, **kwargs):
+ cached_label_changed(sender=sender, **kwargs)
+ post_save_geo(sender=sender, **kwargs)
+
+post_save.connect(container_post_save, sender=Container)
m2m_changed.connect(document_attached_changed,
sender=Container.documents.through)
diff --git a/archaeological_warehouse/templates/ishtar/sheet_container.html b/archaeological_warehouse/templates/ishtar/sheet_container.html
index 27cbf9bee..f40020a92 100644
--- a/archaeological_warehouse/templates/ishtar/sheet_container.html
+++ b/archaeological_warehouse/templates/ishtar/sheet_container.html
@@ -39,6 +39,18 @@
</div>
</div>
+{% if item.point_2d or item.multi_polygon %}
+<h3>{% trans "Localisation"%}</h3>
+<div class='row'>
+ {% with geo_item=item %}
+ {% include "ishtar/blocks/sheet_simple_map.html"%}
+ <div class="col-12 col-lg-6 flex-wrap">
+ {% include "ishtar/blocks/sheet_coordinates.html"%}
+ </div>
+ {% endwith %}
+</div>
+{% endif %}
+
{% if item.finds.count %}
<h4>{% trans "Content" %}</h4>
diff --git a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html
index eed484d5a..6be884e8e 100644
--- a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html
+++ b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html
@@ -31,13 +31,26 @@
<div class='row'>
{% field_flex "Person in charge" item.person_in_charge %}
{% include "ishtar/blocks/sheet_creation_section.html" %}
- {% include "ishtar/blocks/sheet_address_section.html" %}
{% field_flex "Divisions" item.location_types|join:", " %}
{% field_flex_full "Comment" item.comment "<pre>" "</pre>" %}
{% include "ishtar/blocks/sheet_json.html" %}
</div>
+{% if item.point_2d or item.multi_polygon or item.address or item.address_complement or item.postal_code or item.town %}
+<h3>{% trans "Localisation"%}</h3>
+<div class='row'>
+ {% with geo_item=item %}
+ {% include "ishtar/blocks/sheet_simple_map.html"%}
+ <div class="col-12 col-lg-6 flex-wrap">
+ {% include "ishtar/blocks/sheet_coordinates.html"%}
+ {% with full=True %}{% include "ishtar/blocks/sheet_address_section.html" %}{% endwith %}
+ </div>
+ {% endwith %}
+</div>
+{% endif %}
+
+
{% if item.containers.count %}
<h4>{% trans "Containers" %}</h4>
{% dynamic_table_document '' 'containers' 'location_id' item.pk 'TABLE_COLS' output %}
diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py
index 7f9c8a400..311f6fee4 100644
--- a/ishtar_common/forms.py
+++ b/ishtar_common/forms.py
@@ -664,8 +664,7 @@ def get_form_selection(
class_name, label, key, model, base_form, get_url,
not_selected_error=_(u"You should select an item."), new=False,
new_message=_(u"Add a new item"), get_full_url=None,
- gallery=False, map=False
- ):
+ gallery=False, map=False):
"""
Generate a class selection form
class_name -- name of the class
diff --git a/ishtar_common/migrations/0088_auto_20190218_1808.py b/ishtar_common/migrations/0088_auto_20190218_1808.py
new file mode 100644
index 000000000..fc78d7883
--- /dev/null
+++ b/ishtar_common/migrations/0088_auto_20190218_1808.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.10 on 2019-02-18 18:08
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import ishtar_common.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0087_auto_20190204_1149'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='document',
+ options={'ordering': ('title',), 'permissions': (('view_document', 'Peut voir tous les Documents'), ('view_own_document', 'Peut voir ses propres Documents'), ('add_own_document', 'Peut ajouter son propre Document'), ('change_own_document', 'Peut modifier ses propres Documents'), ('delete_own_document', 'Peut supprimer ses propres Documents')), 'verbose_name': 'Document', 'verbose_name_plural': 'Documents'},
+ ),
+ migrations.AlterModelOptions(
+ name='profiletypesummary',
+ options={'verbose_name': 'R\xe9sum\xe9 du type de profil', 'verbose_name_plural': 'R\xe9sum\xe9s des types de profil'},
+ ),
+ migrations.AddField(
+ model_name='historicalorganization',
+ name='precise_town',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='ishtar_common.Town'),
+ ),
+ migrations.AddField(
+ model_name='historicalperson',
+ name='precise_town',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='ishtar_common.Town'),
+ ),
+ migrations.AddField(
+ model_name='organization',
+ name='precise_town',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.Town', verbose_name='Town (precise)'),
+ ),
+ migrations.AddField(
+ model_name='person',
+ name='precise_town',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.Town', verbose_name='Town (precise)'),
+ ),
+ migrations.AlterField(
+ model_name='document',
+ name='associated_file',
+ field=models.FileField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=255, null=True, upload_to=ishtar_common.models.get_image_path),
+ ),
+ migrations.AlterField(
+ model_name='document',
+ name='image',
+ field=models.ImageField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=255, null=True, upload_to=ishtar_common.models.get_image_path),
+ ),
+ migrations.AlterField(
+ model_name='document',
+ name='thumbnail',
+ field=models.ImageField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=255, null=True, upload_to=ishtar_common.models.get_image_path),
+ ),
+ migrations.AlterField(
+ model_name='documenttemplate',
+ name='template',
+ field=models.FileField(help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', upload_to=b'templates/%Y/', verbose_name='Patron'),
+ ),
+ migrations.AlterField(
+ model_name='historicalorganization',
+ name='town',
+ field=models.CharField(blank=True, max_length=150, null=True, verbose_name='Town (freeform)'),
+ ),
+ migrations.AlterField(
+ model_name='historicalperson',
+ name='town',
+ field=models.CharField(blank=True, max_length=150, null=True, verbose_name='Town (freeform)'),
+ ),
+ migrations.AlterField(
+ model_name='import',
+ name='error_file',
+ field=models.FileField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=255, null=True, upload_to=b'upload/imports/%Y/%m/', verbose_name='Fichier erreur'),
+ ),
+ migrations.AlterField(
+ model_name='import',
+ name='imported_file',
+ field=models.FileField(help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=220, upload_to=b'upload/imports/%Y/%m/', verbose_name='Fichier import\xe9'),
+ ),
+ migrations.AlterField(
+ model_name='import',
+ name='imported_images',
+ field=models.FileField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=220, null=True, upload_to=b'upload/imports/%Y/%m/', verbose_name='Images associ\xe9es (fichier zip)'),
+ ),
+ migrations.AlterField(
+ model_name='import',
+ name='match_file',
+ field=models.FileField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=255, null=True, upload_to=b'upload/imports/%Y/%m/', verbose_name='Fichier de correspondance'),
+ ),
+ migrations.AlterField(
+ model_name='import',
+ name='result_file',
+ field=models.FileField(blank=True, help_text='La taille maximale support\xe9e pour le fichier est de 100 Mo.', max_length=255, null=True, upload_to=b'upload/imports/%Y/%m/', verbose_name='Fichier r\xe9sultant'),
+ ),
+ migrations.AlterField(
+ model_name='importertype',
+ name='is_template',
+ field=models.BooleanField(default=False, verbose_name='Peut \xeatre export\xe9'),
+ ),
+ migrations.AlterField(
+ model_name='ishtarsiteprofile',
+ name='config',
+ field=models.CharField(blank=True, choices=[(b'DRASSM', 'DRASSM')], help_text='Choisir une configuration alternative pour les libell\xe9s, gestion des index', max_length=200, null=True, verbose_name='Configuration alternative'),
+ ),
+ migrations.AlterField(
+ model_name='ishtarsiteprofile',
+ name='default_operation_prefix',
+ field=models.CharField(blank=True, default='OP', max_length=20, null=True, verbose_name='Pr\xe9fixe par d\xe9faut pour le code op\xe9ration'),
+ ),
+ migrations.AlterField(
+ model_name='ishtarsiteprofile',
+ name='document_external_id',
+ field=models.TextField(default='{index}', help_text="Formule pour g\xe9rer les identifiants de document. \xc0 manipuler avec pr\xe9caution. Une formule incorrecte peut rendre l'application inutilisable et l'import de donn\xe9es externes peut alors \xeatre destructif.", verbose_name='ID externe document'),
+ ),
+ migrations.AlterField(
+ model_name='ishtarsiteprofile',
+ name='find_use_index',
+ field=models.BooleanField(default=True, verbose_name='Utiliser les index automatiques pour le mobilier'),
+ ),
+ migrations.AlterField(
+ model_name='ishtarsiteprofile',
+ name='operation_prefix',
+ field=models.CharField(blank=True, default='OA', max_length=20, null=True, verbose_name='Pr\xe9fixe principal pour le code op\xe9ration'),
+ ),
+ migrations.AlterField(
+ model_name='ishtarsiteprofile',
+ name='operation_region_code',
+ field=models.CharField(blank=True, max_length=5, null=True, verbose_name='Code r\xe9gion des op\xe9rations'),
+ ),
+ migrations.AlterField(
+ model_name='jsondatafield',
+ name='value_type',
+ field=models.CharField(choices=[(b'T', 'Texte'), (b'LT', 'Texte long'), (b'I', 'Entier'), (b'B', 'Bool\xe9en'), (b'F', 'Nombre \xe0 virgule'), (b'D', 'Date'), (b'C', 'Choix')], default=b'T', max_length=10, verbose_name='Type'),
+ ),
+ migrations.AlterField(
+ model_name='organization',
+ name='town',
+ field=models.CharField(blank=True, max_length=150, null=True, verbose_name='Town (freeform)'),
+ ),
+ migrations.AlterField(
+ model_name='person',
+ name='town',
+ field=models.CharField(blank=True, max_length=150, null=True, verbose_name='Town (freeform)'),
+ ),
+ migrations.AlterField(
+ model_name='userprofile',
+ name='auto_pin',
+ field=models.BooleanField(default=False, verbose_name='\xc9pingler automatiquement'),
+ ),
+ migrations.AlterField(
+ model_name='userprofile',
+ name='display_pin_menu',
+ field=models.BooleanField(default=False, verbose_name="Montrer le menu d'\xe9pinglage"),
+ ),
+ migrations.AlterField(
+ model_name='userprofile',
+ name='show_field_number',
+ field=models.BooleanField(default=False, verbose_name='Afficher les num\xe9ros des champs'),
+ ),
+ ]
diff --git a/ishtar_common/models.py b/ishtar_common/models.py
index 778461562..28e469e31 100644
--- a/ishtar_common/models.py
+++ b/ishtar_common/models.py
@@ -1612,23 +1612,96 @@ class DocumentItem(object):
return actions
-class GeoItem(object):
+class SpatialReferenceSystem(GeneralType):
+ order = models.IntegerField(_(u"Order"), default=10)
+ auth_name = models.CharField(
+ _(u"Authority name"), default=u'EPSG', max_length=256)
+ srid = models.IntegerField(_(u"Authority SRID"))
+
+ class Meta:
+ verbose_name = _(u"Spatial reference system")
+ verbose_name_plural = _(u"Spatial reference systems")
+ ordering = ('label',)
+
+
+post_save.connect(post_save_cache, sender=SpatialReferenceSystem)
+post_delete.connect(post_save_cache, sender=SpatialReferenceSystem)
+
+
+class GeoItem(models.Model):
GEO_SOURCE = ('T', _(u"Town")), ('P', _(u"Precise"))
+ # gis
+ x = models.FloatField(_(u'X'), blank=True, null=True)
+ y = models.FloatField(_(u'Y'), blank=True, null=True)
+ z = models.FloatField(_(u'Z'), blank=True, null=True)
+ estimated_error_x = models.FloatField(_(u'Estimated error for X'),
+ blank=True, null=True)
+ estimated_error_y = models.FloatField(_(u'Estimated error for Y'),
+ blank=True, null=True)
+ estimated_error_z = models.FloatField(_(u'Estimated error for Z'),
+ blank=True, null=True)
+ spatial_reference_system = models.ForeignKey(
+ SpatialReferenceSystem, verbose_name=_(u"Spatial Reference System"),
+ blank=True, null=True)
+ point = models.PointField(_(u"Point"), blank=True, null=True, dim=3)
+ point_2d = models.PointField(_(u"Point (2D)"), blank=True, null=True)
+ point_source = models.CharField(
+ _(u"Point source"), choices=GEO_SOURCE, max_length=1, blank=True,
+ null=True)
+ point_source_item = models.CharField(
+ _(u"Point source item"), max_length=100, blank=True, null=True)
+ multi_polygon = models.MultiPolygonField(_(u"Multi polygon"), blank=True,
+ null=True)
+ multi_polygon_source = models.CharField(
+ _(u"Multi-polygon source"), choices=GEO_SOURCE, max_length=1,
+ blank=True, null=True)
+ multi_polygon_source_item = models.CharField(
+ _(u"Multi polygon source item"), max_length=100, blank=True, null=True)
+
+ GEO_LABEL = ""
+
+ class Meta:
+ abstract = True
+
+ def get_town_centroid(self):
+ raise NotImplementedError
+
+ def get_town_polygons(self):
+ raise NotImplementedError
+
+ def get_precise_points(self):
+ if self.point_source == 'P' and self.point_2d:
+ return self.point_2d, self.point, self.point_source_item
+
+ def get_precise_polygons(self):
+ if self.multi_polygon_source == 'P' and self.multi_polygon:
+ return self.multi_polygon, self.multi_polygon_source_item
+
def geo_point_source(self):
if not self.point_source:
return ""
- return dict(self.GEO_SOURCE)[self.point_source]
+ src = u"{} - {}".format(
+ dict(self.GEO_SOURCE)[self.point_source],
+ self.point_source_item
+ )
+ return src
def geo_polygon_source(self):
if not self.multi_polygon_source:
return ""
- return dict(self.GEO_SOURCE)[self.multi_polygon_source]
+ src = u"{} - {}".format(
+ dict(self.GEO_SOURCE)[self.multi_polygon_source],
+ self.multi_polygon_source_item
+ )
+ return src
def _geojson_serialize(self, geom_attr):
if not hasattr(self, geom_attr):
return ""
cached_label_key = 'cached_label'
+ if self.GEO_LABEL:
+ cached_label_key = self.GEO_LABEL
if getattr(self, "CACHED_LABELS", None):
cached_label_key = self.CACHED_LABELS[-1]
geojson = serialize(
@@ -1658,7 +1731,7 @@ class GeoItem(object):
return self._geojson_serialize('multi_polygon')
-class BaseHistorizedItem(DocumentItem, FullSearch, Imported, JsonData, GeoItem,
+class BaseHistorizedItem(DocumentItem, FullSearch, Imported, JsonData,
FixAssociated):
"""
Historized item with external ID management.
@@ -2883,13 +2956,225 @@ class Department(models.Model):
return res
+class Arrondissement(models.Model):
+ name = models.CharField(u"Nom", max_length=30)
+ department = models.ForeignKey(Department, verbose_name=u"Département")
+
+ def __unicode__(self):
+ return settings.JOINT.join((self.name, unicode(self.department)))
+
+
+class Canton(models.Model):
+ name = models.CharField(u"Nom", max_length=30)
+ arrondissement = models.ForeignKey(Arrondissement,
+ verbose_name=u"Arrondissement")
+
+ def __unicode__(self):
+ return settings.JOINT.join(
+ (self.name, unicode(self.arrondissement)))
+
+
+class TownManager(models.GeoManager):
+ def get_by_natural_key(self, numero_insee, year):
+ return self.get(numero_insee=numero_insee, year=year)
+
+
+class Town(Imported, models.Model):
+ name = models.CharField(_(u"Name"), max_length=100)
+ surface = models.IntegerField(_(u"Surface (m2)"), blank=True, null=True)
+ center = models.PointField(_(u"Localisation"), srid=settings.SRID,
+ blank=True, null=True)
+ limit = models.MultiPolygonField(_(u"Limit"), blank=True, null=True)
+ numero_insee = models.CharField(u"Code commune (numéro INSEE)",
+ max_length=120)
+ departement = models.ForeignKey(
+ Department, verbose_name=_(u"Department"),
+ on_delete=models.SET_NULL, null=True, blank=True)
+ year = models.IntegerField(
+ _("Year of creation"), null=True, blank=True,
+ help_text=_(u"Filling this field is relevant to distinguish old towns "
+ u"from new towns."))
+ children = models.ManyToManyField(
+ 'Town', verbose_name=_(u"Town children"), blank=True,
+ related_name='parents')
+ cached_label = models.CharField(_(u"Cached name"), max_length=500,
+ null=True, blank=True, db_index=True)
+ objects = TownManager()
+
+ class Meta:
+ verbose_name = _(u"Town")
+ verbose_name_plural = _(u"Towns")
+ if settings.COUNTRY == 'fr':
+ ordering = ['numero_insee']
+ unique_together = (('numero_insee', 'year'),)
+
+ def natural_key(self):
+ return (self.numero_insee, self.year)
+
+ def history_compress(self):
+ values = {'numero_insee': self.numero_insee,
+ 'year': self.year or ""}
+ return values
+
+ @classmethod
+ def history_decompress(cls, full_value, create=False):
+ if not full_value:
+ return []
+ res = []
+ for value in full_value:
+ try:
+ res.append(
+ cls.objects.get(numero_insee=value['numero_insee'],
+ year=value['year'] or None))
+ except cls.DoesNotExist:
+ continue
+ return res
+
+ def __unicode__(self):
+ if self.cached_label:
+ return self.cached_label
+ self.save()
+ return self.cached_label
+
+ @property
+ def label_with_areas(self):
+ label = [self.name]
+ if self.numero_insee:
+ label.append(u"({})".format(self.numero_insee))
+ for area in self.areas.all():
+ label.append(u" - ")
+ label.append(area.full_label)
+ return u" ".join(label)
+
+ def generate_geo(self, force=False):
+ force = self.generate_limit(force=force)
+ self.generate_center(force=force)
+ self.generate_area(force=force)
+
+ def generate_limit(self, force=False):
+ if not force and self.limit:
+ return
+ parents = None
+ if not self.parents.count():
+ return
+ for parent in self.parents.all():
+ if not parent.limit:
+ return
+ if not parents:
+ parents = parent.limit
+ else:
+ parents = parents.union(parent.limit)
+ # if union is a simple polygon make it a multi
+ if 'MULTI' not in parents.wkt:
+ parents = parents.wkt.replace('POLYGON', 'MULTIPOLYGON(') + ")"
+ if not parents:
+ return
+ self.limit = parents
+ self.save()
+ return True
+
+ def generate_center(self, force=False):
+ if not force and (self.center or not self.limit):
+ return
+ self.center = self.limit.centroid
+ if not self.center:
+ return False
+ self.save()
+ return True
+
+ def generate_area(self, force=False):
+ if not force and (self.surface or not self.limit):
+ return
+ self.surface = self.limit.transform(settings.SURFACE_SRID,
+ clone=True).area
+ if not self.surface:
+ return False
+ self.save()
+ return True
+
+ def update_town_code(self):
+ if not self.numero_insee or not self.children.count() or not self.year:
+ return
+ old_num = self.numero_insee[:]
+ numero = old_num.split('-')[0]
+ self.numero_insee = u"{}-{}".format(numero, self.year)
+ if self.numero_insee != old_num:
+ return True
+
+ def _generate_cached_label(self):
+ cached_label = self.name
+ if settings.COUNTRY == "fr" and self.numero_insee:
+ dpt_len = 2
+ if self.numero_insee.startswith('97') or \
+ self.numero_insee.startswith('98') or \
+ self.numero_insee[0] not in ('0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9'):
+ dpt_len = 3
+ cached_label = u"%s - %s" % (self.name, self.numero_insee[:dpt_len])
+ if self.year and self.children.count():
+ cached_label += u" ({})".format(self.year)
+ return cached_label
+
+
+def post_save_town(sender, **kwargs):
+ cached_label_changed(sender, **kwargs)
+ town = kwargs['instance']
+ town.generate_geo()
+ if town.update_town_code():
+ town.save()
+
+
+post_save.connect(post_save_town, sender=Town)
+
+
+def town_child_changed(sender, **kwargs):
+ town = kwargs['instance']
+ if town.update_town_code():
+ town.save()
+
+
+m2m_changed.connect(town_child_changed, sender=Town.children.through)
+
+
+class Area(HierarchicalType):
+ towns = models.ManyToManyField(Town, verbose_name=_(u"Towns"), blank=True,
+ related_name='areas')
+ reference = models.CharField(_(u"Reference"), max_length=200, blank=True,
+ null=True)
+ parent = models.ForeignKey(
+ 'self', blank=True, null=True, verbose_name=_(u"Parent"),
+ help_text=_(u"Only four level of parent are managed."),
+ related_name='children', on_delete=models.SET_NULL
+ )
+
+ class Meta:
+ verbose_name = _(u"Area")
+ verbose_name_plural = _(u"Areas")
+ ordering = ('label',)
+
+ def __unicode__(self):
+ if not self.reference:
+ return self.label
+ return u"{} ({})".format(self.label, self.reference)
+
+ @property
+ def full_label(self):
+ label = [unicode(self)]
+ if self.parent:
+ label.append(self.parent.full_label)
+ return u" / ".join(label)
+
+
class Address(BaseHistorizedItem):
address = models.TextField(_(u"Address"), null=True, blank=True)
address_complement = models.TextField(_(u"Address complement"), null=True,
blank=True)
postal_code = models.CharField(_(u"Postal code"), max_length=10, null=True,
blank=True)
- town = models.CharField(_(u"Town"), max_length=70, null=True, blank=True)
+ town = models.CharField(_(u"Town (freeform)"), max_length=150, null=True,
+ blank=True)
+ precise_town = models.ForeignKey(
+ Town, verbose_name=_(u"Town (precise)"), null=True, blank=True)
country = models.CharField(_(u"Country"), max_length=30, null=True,
blank=True)
alt_address = models.TextField(_(u"Other address: address"), null=True,
@@ -2925,6 +3210,14 @@ class Address(BaseHistorizedItem):
class Meta:
abstract = True
+ def get_town_centroid(self):
+ if self.precise_town:
+ return self.precise_town.center, self._meta.verbose_name
+
+ def get_town_polygons(self):
+ if self.precise_town:
+ return self.precise_town.limit, self._meta.verbose_name
+
def simple_lbl(self):
return unicode(self)
@@ -4400,215 +4693,6 @@ def document_attached_changed(sender, **kwargs):
post_save.connect(cached_label_changed, sender=Document)
-class Arrondissement(models.Model):
- name = models.CharField(u"Nom", max_length=30)
- department = models.ForeignKey(Department, verbose_name=u"Département")
-
- def __unicode__(self):
- return settings.JOINT.join((self.name, unicode(self.department)))
-
-
-class Canton(models.Model):
- name = models.CharField(u"Nom", max_length=30)
- arrondissement = models.ForeignKey(Arrondissement,
- verbose_name=u"Arrondissement")
-
- def __unicode__(self):
- return settings.JOINT.join(
- (self.name, unicode(self.arrondissement)))
-
-
-class TownManager(models.GeoManager):
- def get_by_natural_key(self, numero_insee, year):
- return self.get(numero_insee=numero_insee, year=year)
-
-
-class Town(Imported, models.Model):
- name = models.CharField(_(u"Name"), max_length=100)
- surface = models.IntegerField(_(u"Surface (m2)"), blank=True, null=True)
- center = models.PointField(_(u"Localisation"), srid=settings.SRID,
- blank=True, null=True)
- limit = models.MultiPolygonField(_(u"Limit"), blank=True, null=True)
- numero_insee = models.CharField(u"Code commune (numéro INSEE)",
- max_length=120)
- departement = models.ForeignKey(
- Department, verbose_name=_(u"Department"),
- on_delete=models.SET_NULL, null=True, blank=True)
- year = models.IntegerField(
- _("Year of creation"), null=True, blank=True,
- help_text=_(u"Filling this field is relevant to distinguish old towns "
- u"from new towns."))
- children = models.ManyToManyField(
- 'Town', verbose_name=_(u"Town children"), blank=True,
- related_name='parents')
- cached_label = models.CharField(_(u"Cached name"), max_length=500,
- null=True, blank=True, db_index=True)
- objects = TownManager()
-
- class Meta:
- verbose_name = _(u"Town")
- verbose_name_plural = _(u"Towns")
- if settings.COUNTRY == 'fr':
- ordering = ['numero_insee']
- unique_together = (('numero_insee', 'year'),)
-
- def natural_key(self):
- return (self.numero_insee, self.year)
-
- def history_compress(self):
- values = {'numero_insee': self.numero_insee,
- 'year': self.year or ""}
- return values
-
- @classmethod
- def history_decompress(cls, full_value, create=False):
- if not full_value:
- return []
- res = []
- for value in full_value:
- try:
- res.append(
- cls.objects.get(numero_insee=value['numero_insee'],
- year=value['year'] or None))
- except cls.DoesNotExist:
- continue
- return res
-
- def __unicode__(self):
- if self.cached_label:
- return self.cached_label
- self.save()
- return self.cached_label
-
- @property
- def label_with_areas(self):
- label = [self.name]
- if self.numero_insee:
- label.append(u"({})".format(self.numero_insee))
- for area in self.areas.all():
- label.append(u" - ")
- label.append(area.full_label)
- return u" ".join(label)
-
- def generate_geo(self, force=False):
- force = self.generate_limit(force=force)
- self.generate_center(force=force)
- self.generate_area(force=force)
-
- def generate_limit(self, force=False):
- if not force and self.limit:
- return
- parents = None
- if not self.parents.count():
- return
- for parent in self.parents.all():
- if not parent.limit:
- return
- if not parents:
- parents = parent.limit
- else:
- parents = parents.union(parent.limit)
- # if union is a simple polygon make it a multi
- if 'MULTI' not in parents.wkt:
- parents = parents.wkt.replace('POLYGON', 'MULTIPOLYGON(') + ")"
- if not parents:
- return
- self.limit = parents
- self.save()
- return True
-
- def generate_center(self, force=False):
- if not force and (self.center or not self.limit):
- return
- self.center = self.limit.centroid
- if not self.center:
- return False
- self.save()
- return True
-
- def generate_area(self, force=False):
- if not force and (self.surface or not self.limit):
- return
- self.surface = self.limit.transform(settings.SURFACE_SRID,
- clone=True).area
- if not self.surface:
- return False
- self.save()
- return True
-
- def update_town_code(self):
- if not self.numero_insee or not self.children.count() or not self.year:
- return
- old_num = self.numero_insee[:]
- numero = old_num.split('-')[0]
- self.numero_insee = u"{}-{}".format(numero, self.year)
- if self.numero_insee != old_num:
- return True
-
- def _generate_cached_label(self):
- cached_label = self.name
- if settings.COUNTRY == "fr" and self.numero_insee:
- dpt_len = 2
- if self.numero_insee.startswith('97') or \
- self.numero_insee.startswith('98') or \
- self.numero_insee[0] not in ('0', '1', '2', '3', '4', '5',
- '6', '7', '8', '9'):
- dpt_len = 3
- cached_label = u"%s - %s" % (self.name, self.numero_insee[:dpt_len])
- if self.year and self.children.count():
- cached_label += u" ({})".format(self.year)
- return cached_label
-
-
-def post_save_town(sender, **kwargs):
- cached_label_changed(sender, **kwargs)
- town = kwargs['instance']
- town.generate_geo()
- if town.update_town_code():
- town.save()
-
-
-post_save.connect(post_save_town, sender=Town)
-
-
-def town_child_changed(sender, **kwargs):
- town = kwargs['instance']
- if town.update_town_code():
- town.save()
-
-
-m2m_changed.connect(town_child_changed, sender=Town.children.through)
-
-
-class Area(HierarchicalType):
- towns = models.ManyToManyField(Town, verbose_name=_(u"Towns"), blank=True,
- related_name='areas')
- reference = models.CharField(_(u"Reference"), max_length=200, blank=True,
- null=True)
- parent = models.ForeignKey(
- 'self', blank=True, null=True, verbose_name=_(u"Parent"),
- help_text=_(u"Only four level of parent are managed."),
- related_name='children', on_delete=models.SET_NULL
- )
-
- class Meta:
- verbose_name = _(u"Area")
- verbose_name_plural = _(u"Areas")
- ordering = ('label',)
-
- def __unicode__(self):
- if not self.reference:
- return self.label
- return u"{} ({})".format(self.label, self.reference)
-
- @property
- def full_label(self):
- label = [unicode(self)]
- if self.parent:
- label.append(self.parent.full_label)
- return u" / ".join(label)
-
-
class OperationType(GeneralType):
order = models.IntegerField(_(u"Order"), default=1)
preventive = models.BooleanField(_(u"Is preventive"), default=True)
@@ -4691,22 +4775,6 @@ post_save.connect(post_save_cache, sender=OperationType)
post_delete.connect(post_save_cache, sender=OperationType)
-class SpatialReferenceSystem(GeneralType):
- order = models.IntegerField(_(u"Order"), default=10)
- auth_name = models.CharField(
- _(u"Authority name"), default=u'EPSG', max_length=256)
- srid = models.IntegerField(_(u"Authority SRID"))
-
- class Meta:
- verbose_name = _(u"Spatial reference system")
- verbose_name_plural = _(u"Spatial reference systems")
- ordering = ('label',)
-
-
-post_save.connect(post_save_cache, sender=SpatialReferenceSystem)
-post_delete.connect(post_save_cache, sender=SpatialReferenceSystem)
-
-
class AdministrationScript(models.Model):
path = models.CharField(_(u"Filename"), max_length=30)
name = models.TextField(_(u"Name"),
diff --git a/ishtar_common/templates/ishtar/blocks/sheet_address_section.html b/ishtar_common/templates/ishtar/blocks/sheet_address_section.html
index fe4bd80bb..a42cd6cca 100644
--- a/ishtar_common/templates/ishtar/blocks/sheet_address_section.html
+++ b/ishtar_common/templates/ishtar/blocks/sheet_address_section.html
@@ -1,10 +1,10 @@
{% load i18n %}
-{% if item.address or item.address_complement or item.postal_code or item.town %}
-<div class="col-12 col-md-6 col-lg-4 d-flex flex-wrap row">
- <dt class="col-5">{% trans "Address" %}</dt>
- <dd class="col-7">
+{% if item.address or item.address_complement or item.postal_code or item.town or item.precise_town%}
+<dl class="{% if full %}col-12 col-lg-6{% else %}col-12 col-md-6 col-lg-4 d-flex row{% endif %} flex-wrap">
+ <dt>{% trans "Address" %}</dt>
+ <dd>
<pre>{% if item.address %}{{item.address}}{% endif %}{% if item.address_complement %}
-{{item.address_complement}}{% endif %}{% if item.postal_code or item.town %}
-{{item.postal_code}} {{item.town}}{% endif %}</pre>
+{{item.address_complement}}{% endif %}{% if item.postal_code or item.town or item.precise_town %}
+{{item.postal_code}} {% if item.precise_town %}{{item.precise_town}}{% else %}{{item.town}}{% endif %}{% endif %}</pre>
</dd>
-</div>{% endif %}
+</dl>{% endif %}
diff --git a/ishtar_common/templates/ishtar/blocks/sheet_coordinates.html b/ishtar_common/templates/ishtar/blocks/sheet_coordinates.html
index bd0c1ef19..d73ef9c96 100644
--- a/ishtar_common/templates/ishtar/blocks/sheet_coordinates.html
+++ b/ishtar_common/templates/ishtar/blocks/sheet_coordinates.html
@@ -1,7 +1,7 @@
{% load i18n window_field %}{% if geo_item.x or geo_item.y or geo_item.z %}
-<dl class="col-12 d-flex flex-wrap">
- <dt class="col-12">{% trans "Coordinates" %}</dt>
- <dd class="col-12">
+<dl class="col-12">
+ <dt>{% trans "Coordinates" %}</dt>
+ <dd>
{% trans "X:"%} {{geo_item.x|default_if_none:"-"}}
{% if geo_item.estimated_error_x %} ({% trans "error:" %} {{geo_item.estimated_error_x}}){% endif %},
{% trans "Y:"%} {{geo_item.y|default_if_none:"-"}},
diff --git a/ishtar_common/tests.py b/ishtar_common/tests.py
index 53ad67faa..55c43ac21 100644
--- a/ishtar_common/tests.py
+++ b/ishtar_common/tests.py
@@ -1576,6 +1576,7 @@ class GeomaticTest(TestCase):
self.y = y
self.z = z
self.spatial_reference_system = spatial_reference_system
+ self.point_source = 'P'
self.point = point
self.point_2d = point_2d
diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py
index 408309c4e..2aa8db298 100644
--- a/ishtar_common/utils.py
+++ b/ishtar_common/utils.py
@@ -487,18 +487,28 @@ def post_save_geo(sender, **kwargs):
"""
Convert raw x, y, z point to real geo field
"""
- if not kwargs.get('instance'):
+ if not kwargs.get('instance') or getattr(kwargs['instance'],
+ '_post_saved_geo', False):
return
from ishtar_common.models import get_current_profile # not clean but utils
# is loaded before models
instance = kwargs.get('instance')
+ current_source = unicode(instance.__class__._meta.verbose_name)
+ if instance.point_source_item and \
+ instance.point_source_item != current_source: # refetch
+ instance.point, instance.point_2d = None, None
+ instance.x, instance.y = None, None
+ instance.point_source = None
+
point = instance.point
point_2d = instance.point_2d
profile = get_current_profile()
modified = False
+ print (instance)
- if (point or point_2d) and instance.x is None: # db source
+ if (point or point_2d) and instance.x is None and not \
+ instance.point_source: # db source
if point:
current_point = point
instance.z = point.z
@@ -509,11 +519,13 @@ def post_save_geo(sender, **kwargs):
srs = get_srid_obj_from_point(current_point)
instance.spatial_reference_system = srs
instance.point_source = 'P'
+ instance.point_source_item = current_source
if not point_2d:
instance.point_2d = convert_coordinates_to_point(
instance.point.x, instance.point.y,
srid=current_point.srid)
- elif not instance.point_source and instance.x and instance.y and \
+ modified = True
+ elif instance.x and instance.y and \
instance.spatial_reference_system and \
instance.spatial_reference_system.auth_name == 'EPSG' and \
instance.spatial_reference_system.srid != 0: # form input
@@ -523,43 +535,95 @@ def post_save_geo(sender, **kwargs):
srid=instance.spatial_reference_system.srid)
except forms.ValidationError:
return # irrelevant data in DB
+ distance = 1 # arbitrary
+ if point_2d and instance.point_2d:
+ distance = point_2d.transform(
+ 4326, clone=True).distance(
+ instance.point_2d.transform(4326, clone=True))
+
if instance.z:
point = convert_coordinates_to_point(
instance.x, instance.y, instance.z,
srid=instance.spatial_reference_system.srid)
- if point_2d != instance.point_2d or point != instance.point:
+ # no change if distance inf to 1 mm
+ if distance >= 0.0001 and (point_2d != instance.point_2d
+ or point != instance.point):
instance.point = point
instance.point_2d = point_2d
instance.point_source = 'P'
+ instance.point_source_item = current_source
modified = True
- elif not point_2d and profile.use_town_for_geo: # try to get from parent
- point_2d = instance.get_town_centroid()
- if point_2d != instance.point_2d:
+ else:
+ instance.point_source = None
+ # get coordinates from parents
+ precise_points = instance.get_precise_points()
+ if precise_points:
+ point_2d, point, source_item = precise_points
instance.point_2d = point_2d
- instance.point_source = 'T' # town
+ instance.point = point
+ instance.point_source = 'P'
+ instance.point_source_item = source_item
instance.x = point_2d.x
instance.y = point_2d.y
+ if point:
+ instance.z = point.z
srs = get_srid_obj_from_point(point_2d)
instance.spatial_reference_system = srs
modified = True
- if not hasattr(instance, 'multi_polygon'):
- return
-
- if instance.multi_polygon and not instance.multi_polygon_source:
- # should be a db source
- instance.multi_polygon_source = 'P'
- modified = True
- elif profile.use_town_for_geo and instance.multi_polygon_source != 'P':
- poly = instance.get_town_polygons()
- if poly and poly != instance.multi_polygon:
- instance.multi_polygon_source = 'T' # town
- instance.multi_polygon = poly
+ elif profile.use_town_for_geo: # try to get from parent
+ centroid = instance.get_town_centroid()
+ if centroid and centroid[0]:
+ instance.point_2d, instance.point_source_item = centroid
+ instance.point = None
+ instance.point_source = 'T'
+ instance.x = instance.point_2d.x
+ instance.y = instance.point_2d.y
+ srs = get_srid_obj_from_point(instance.point_2d)
+ instance.spatial_reference_system = srs
+ modified = True
+ else:
+ instance.point_2d, instance.point_source_item = None, None
+ instance.point = None
+ instance.point_source = None
+ modified = True
+
+ if hasattr(instance, 'multi_polygon'):
+ if instance.multi_polygon_source_item and \
+ instance.multi_polygon_source_item != current_source: # refetch
+ instance.multi_polygon = None
+ instance.multi_polygon_source = None
+
+ if instance.multi_polygon and not instance.multi_polygon_source:
+ # should be a db source
+ instance.multi_polygon_source = 'P'
+ instance.multi_polygon_source_item = current_source
modified = True
+ elif instance.multi_polygon_source != 'P':
+ precise_poly = instance.get_precise_polygons()
+ if precise_poly:
+ poly, source_item = precise_poly
+ instance.multi_polygon = poly
+ instance.multi_polygon_source = 'P'
+ instance.multi_polygon_source_item = source_item
+ modified = True
+ elif profile.use_town_for_geo:
+ poly = instance.get_town_polygons()
+ if poly:
+ poly, poly_source = poly
+ if poly != instance.multi_polygon:
+ instance.multi_polygon_source_item = poly_source
+ instance.multi_polygon_source = 'T' # town
+ try:
+ instance.multi_polygon = poly
+ modified = True
+ except TypeError:
+ print(instance, instance.pk)
if modified:
instance.skip_history_when_saving = True
+ instance._post_saved_geo = True
instance.save()
return