diff options
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 |