From df8a001203e9e936ae5bae4e06f5631d87fb3ada Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Sun, 22 May 2022 20:31:19 +0200 Subject: Geodata - geo forms: new forms - many adaptations --- ishtar_common/forms_common.py | 204 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 202 insertions(+), 2 deletions(-) (limited to 'ishtar_common/forms_common.py') diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 6e9e11006..695638120 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -30,6 +30,7 @@ from urllib.parse import urlparse, quote import zipfile from django import forms +from django.contrib.gis import forms as gis_forms from django.conf import settings from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType @@ -71,7 +72,8 @@ from .forms import ( LockForm, ) from ishtar_common.data_importer import ImporterError -from ishtar_common.utils import is_downloadable, clean_session_cache, max_size_help +from ishtar_common.utils import is_downloadable, clean_session_cache, max_size_help, \ + reverse_coordinates from archaeological_operations.models import Operation, OperationType from archaeological_context_records.models import ContextRecord @@ -1972,7 +1974,7 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): if cleaned_data.get(rel, None): return cleaned_data raise forms.ValidationError( - _("A document has to be attached at least " "to one item") + _("A document has to be attached at least to one item") ) def clean_publisher(self): @@ -2562,3 +2564,201 @@ class QRSearchForm(forms.Form): os.makedirs(dest_dir) shutil.move(filename, dest_dir) return os.path.join(settings.MEDIA_URL, "tmp", base_filename) + + +class GISForm(forms.ModelForm, CustomForm, ManageOldType): + form_label = _("Geo item") + form_admin_name = _("Geo item - General") + form_slug = "geoitem-general" + + extra_form_modals = [] + associated_models = { + "data_type": models.GeoDataType, + "origin": models.GeoOriginType, + "provider": models.GeoProviderType, + } + + pk = forms.IntegerField(label="", required=False, widget=forms.HiddenInput) + name = forms.CharField( + label=_("Name"), + required=False, + validators=[validators.MaxLengthValidator(500)], + ) + import_key = forms.CharField( + label=_("Import key"), + required=False, + disabled=True, + help_text=_("An update via import corresponding to the source element and " + "this key will overwrite the data."), + ) + source_content_type_id = forms.IntegerField( + label="", required=True, widget=forms.HiddenInput, disabled=True) + source_id = forms.IntegerField( + label="", required=True, widget=forms.HiddenInput, disabled=True) + data_type = widgets.ModelChoiceField( + model=models.GeoDataType, label=_("Data type"), choices=[], required=False + ) + origin = widgets.ModelChoiceField( + model=models.GeoOriginType, label=_("Origin"), choices=[], required=False + ) + provider = widgets.ModelChoiceField( + model=models.GeoProviderType, label=_("Provider"), choices=[], required=False + ) + comment = forms.CharField(label=_("Comment"), widget=forms.Textarea, required=False) + + TYPES = [ + FieldType("origin", models.GeoOriginType), + FieldType("data_type", models.GeoDataType), + FieldType("provider", models.GeoProviderType), + ] + + class Meta: + model = models.GeoVectorData + exclude = ["need_update", "imports", "cached_x", "cached_y", "cached_z", + "point_3d"] + + HEADERS = { + "related_items_ishtar_common_town": FormHeader( + _("Related items"), collapse=True), + "name": FormHeader(_("Meta-data")), + "geo_field": FormHeader(_("Geography")), + } + OPTIONS_PERMISSIONS = [ + # field name, permission, options + ("tags", ("ishtar_common.add_documenttag",), {"new": True}), + ] + + GEO_FIELDS = ( + ("point_2d",), + ("multi_points",), + ("multi_line",), + ("multi_polygon",), + ("x", "z") + ) + + def __init__(self, *args, **kwargs): + main_items_fields = {} + if "main_items_fields" in kwargs: + main_items_fields = kwargs.pop("main_items_fields") + self.user = None + if kwargs.get("user", None): + self.user = kwargs.pop("user") + instance = kwargs.get("instance", False) + self.is_instancied = bool(instance) + super(GISForm, self).__init__(*args, **kwargs) + if not self.fields["import_key"].initial: + self.fields.pop("import_key") + self.source_content_type = kwargs.pop("source_content_type", None) + self.source_id = kwargs.pop("source_id", None) + if not self.source_content_type: + self.fields.pop("source_content_type_id") + self.fields.pop("source_id") + else: + self.fields["source_content_type_id"].initial = self.source_content_type + self.fields["source_id"].initial = self.source_id + self.geo_keys = [] + if instance: + for keys in self.GEO_FIELDS: + if any(getattr(instance, key) for key in keys): + if keys[0] != "x": + map_srid = getattr(instance, keys[0]).srid or 4326 + widget = gis_forms.OSMWidget + if map_srid == 4326: + widget = widgets.ReversedOSMWidget + self.fields[keys[0]].widget = widget( + attrs={"map_srid": map_srid}) + self.fields.pop("spatial_reference_system") + self.geo_keys = keys[:] + else: + self.geo_keys = [ + "x", "estimated_error_x", + "y", "estimated_error_y", + "z", "estimated_error_z", + "spatial_reference_system", + ] + for geo_fields in self.GEO_FIELDS: + if geo_fields != keys: + for geo_field in geo_fields: + self.fields.pop(geo_field) + if geo_field == "x": + self.fields.pop("estimated_error_x") + self.fields.pop("y") + self.fields.pop("estimated_error_y") + if geo_field == "z": + self.fields.pop("estimated_error_z") + break + if not self.geo_keys: + # TODO.... + pass + + fields = OrderedDict() + for related_key in models.GeoVectorData.RELATED_MODELS: + model = models.GeoVectorData._meta.get_field(related_key).related_model + fields[related_key] = widgets.Select2MultipleField( + model=model, + remote=True, + label=model._meta.verbose_name_plural, + required=False, + style="width: 100%", + ) + if related_key in main_items_fields: + for field_key, label in main_items_fields[related_key]: + disabled = False + if kwargs.get("initial", None) and kwargs["initial"].get( + field_key, False + ): + disabled = True + fields[field_key] = forms.BooleanField( + label=label, required=False, disabled=disabled + ) + for k in self.geo_keys: + fields[k] = self.fields[k] + for k in self.fields: + if k not in self.geo_keys: + fields[k] = self.fields[k] + self.fields = fields + + def get_headers(self): + headers = self.HEADERS.copy() + if self.geo_keys: + headers[self.geo_keys[0]] = headers.pop("geo_field") + return headers + + def clean(self): + cleaned_data = self.cleaned_data + if "x" not in self.geo_keys: + # reverse... + geo_value = cleaned_data[self.geo_keys[0]] + if geo_value: + if not isinstance(geo_value, str): + geo_value = geo_value.ewkt + if geo_value.startswith("SRID=4326;"): + cleaned_data[self.geo_keys[0]] = reverse_coordinates(geo_value) + for rel in models.GeoVectorData.RELATED_MODELS: + if cleaned_data.get(rel, None): + return cleaned_data + raise forms.ValidationError( + _("A geo item has to be attached at least to one item") + ) + + def save(self, commit=True): + item = super().save(commit=commit) + for related_key in models.GeoVectorData.RELATED_MODELS: + related = getattr(item, related_key) + initial = dict([(rel.pk, rel) for rel in related.all()]) + new = [int(pk) for pk in sorted(self.cleaned_data.get(related_key, []))] + for pk, value in initial.items(): + if pk in new: + continue + related.remove(value) + for new_pk in new: + related_item = related.model.objects.get(pk=new_pk) + if new_pk not in initial.keys(): + related.add(related_item) + item = models.GeoVectorData.objects.get(pk=item.pk) + if self.user: + item.history_creator = self.user + item.history_modifier = self.user + item.skip_history_when_saving = True + item.save() # resave to regen the attached items + return item -- cgit v1.2.3