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 | 
