diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2023-09-13 12:59:20 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2024-04-16 16:38:32 +0200 |
commit | 3e9cfaeaed2eced1e3f9b2bd80f2f93b730d06e9 (patch) | |
tree | f340b493ce89008b1999a6767da6ee8d58dabf79 | |
parent | c2764f756035048756866fa4e4d4c75908e5b392 (diff) | |
download | Ishtar-3e9cfaeaed2eced1e3f9b2bd80f2f93b730d06e9.tar.bz2 Ishtar-3e9cfaeaed2eced1e3f9b2bd80f2f93b730d06e9.zip |
✨ Pre-import form: form choice field - black format
-rw-r--r-- | archaeological_operations/forms.py | 3 | ||||
-rw-r--r-- | ishtar_common/forms.py | 27 | ||||
-rw-r--r-- | ishtar_common/forms_common.py | 252 |
3 files changed, 181 insertions, 101 deletions
diff --git a/archaeological_operations/forms.py b/archaeological_operations/forms.py index 97a479505..98152f189 100644 --- a/archaeological_operations/forms.py +++ b/archaeological_operations/forms.py @@ -1157,7 +1157,8 @@ class ArchaeologicalSiteForm(ManageOldType): def save(self, user): dct = self.cleaned_data dct['history_modifier'] = user - for typ in self.TYPES: + type_lst = self._types or self.TYPES + for typ in type_lst: if not dct[typ.key]: dct[typ.key] = None if typ.is_multiple: diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py index 0f1263bef..c560dc5ec 100644 --- a/ishtar_common/forms.py +++ b/ishtar_common/forms.py @@ -681,7 +681,9 @@ class IshtarForm(forms.Form, BSForm): # to manage translation def __init__(self, *args, **kwargs): - super(IshtarForm, self).__init__(*args, **kwargs) + self._headers = {} # used for dynamic headers + self._types = [] # used for dynamic types + super().__init__(*args, **kwargs) profile = None if self.PROFILE_FILTER or self.SITE_KEYS: profile = models.get_current_profile() @@ -694,8 +696,6 @@ class IshtarForm(forms.Form, BSForm): self.fields.pop(field_key) if getattr(self, "confirm", False): return - for field in self.TYPES: - self._init_type(field) if self.SITE_KEYS: field_keys = list(self.fields.keys()) for site_key in list(self.SITE_KEYS.keys()): @@ -722,8 +722,14 @@ class IshtarForm(forms.Form, BSForm): continue for option, value in options.items(): setattr(self.fields[field_name].widget, option, value) + self._init_types() self._post_init() + def _init_types(self): + type_lst = self._types or self.TYPES + for field in type_lst: + self._init_type(field) + def _init_type(self, field): if field.key not in self.fields: return @@ -731,6 +737,8 @@ class IshtarForm(forms.Form, BSForm): self.fields[field.key].help_text = field.get_help() def get_headers(self): + if self._headers: + return self._headers return self.HEADERS def headers(self, key): @@ -775,10 +783,11 @@ class IshtarForm(forms.Form, BSForm): conditional_fields = self.CONDITIONAL_FIELDS if hasattr(self, "get_conditional_fields"): conditional_fields = self.get_conditional_fields() - if not conditional_fields or not self.TYPES: + type_lst = self._types or self.TYPES + if not conditional_fields or not type_lst: return - type_dict = dict([(typ.key, typ.model) for typ in self.TYPES]) + type_dict = dict([(typ.key, typ.model) for typ in type_lst]) html = "" for condition, target_names in conditional_fields: condition_field, condition_attr, condition_val = condition @@ -851,12 +860,13 @@ class IshtarForm(forms.Form, BSForm): all_values, ) = self.get_conditional_filter_fields() - types = [typ.key for typ in self.TYPES] + type_lst = self._types or self.TYPES + type_lst = [typ.key for typ in type_lst] html = "" outputs = set() for input_key in conditional_fields: - if input_key not in types: + if input_key not in type_lst: continue name = input_key if self.prefix: @@ -1159,7 +1169,8 @@ class ManageOldType(IshtarForm): self.init_data[k].append(val) self.init_data = MultiValueDict(self.init_data) super(ManageOldType, self).__init__(*args, **kwargs) - for field in self.TYPES: + type_lst = self._types or self.TYPES + for field in type_lst: self._init_type(field) def _init_type(self, field): diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 1b59206a6..3e3488ae9 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -30,13 +30,16 @@ from urllib.parse import urlparse, quote import zipfile from django import forms +from django.apps import apps from django.conf import settings from django.contrib.auth import password_validation from django.contrib.auth.models import User -from django.contrib.auth.forms import UsernameField, \ - AuthenticationForm as AuthAuthenticationForm, \ - PasswordChangeForm as AuthPasswordChangeForm, \ - SetPasswordForm as AuthSetPasswordForm +from django.contrib.auth.forms import ( + UsernameField, + AuthenticationForm as AuthAuthenticationForm, + PasswordChangeForm as AuthPasswordChangeForm, + SetPasswordForm as AuthSetPasswordForm, +) from django.core import validators from django.core.exceptions import ObjectDoesNotExist from django.core.files import File @@ -73,8 +76,12 @@ from .forms import ( LockForm, ) from ishtar_common.data_importer import ImporterError -from ishtar_common.utils import is_downloadable, clean_session_cache, max_size_help, \ - reverse_coordinates +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 @@ -138,14 +145,15 @@ def get_person_field(label=_("Person"), required=True, person_types=None): class AuthenticationForm(AuthAuthenticationForm): - username = UsernameField(widget=forms.TextInput( - attrs={'autofocus': True, 'class': 'no-append'}) + username = UsernameField( + widget=forms.TextInput(attrs={"autofocus": True, "class": "no-append"}) ) password = forms.CharField( label=_("Password"), strip=False, widget=forms.PasswordInput( - attrs={'autocomplete': 'off', 'data-toggle': 'password'}) + attrs={"autocomplete": "off", "data-toggle": "password"} + ), ) @@ -154,14 +162,16 @@ class SetPasswordForm(AuthSetPasswordForm): label=_("New password"), strip=False, help_text=password_validation.password_validators_help_text_html(), - widget=forms.PasswordInput(attrs={'autocomplete': 'off', - 'data-toggle': 'password'}), + widget=forms.PasswordInput( + attrs={"autocomplete": "off", "data-toggle": "password"} + ), ) new_password2 = forms.CharField( label=_("New password confirmation"), strip=False, - widget=forms.PasswordInput(attrs={'autocomplete': 'off', - 'data-toggle': 'password'}), + widget=forms.PasswordInput( + attrs={"autocomplete": "off", "data-toggle": "password"} + ), ) @@ -169,9 +179,9 @@ class PasswordChangeForm(SetPasswordForm, AuthPasswordChangeForm): old_password = forms.CharField( label=_("Old password"), strip=False, - widget=forms.PasswordInput(attrs={'autofocus': True, 'autocomplete': 'off', - 'data-toggle': 'password'}), - + widget=forms.PasswordInput( + attrs={"autofocus": True, "autocomplete": "off", "data-toggle": "password"} + ), ) @@ -246,9 +256,9 @@ class BaseImportForm(IshtarForm, forms.ModelForm): def _filter_importer_type(self): self.fields["importer_type"].choices = [("", "--")] + [ (imp.pk, imp.name) - for imp in models.ImporterType.objects.filter(available=True, - is_import=True, - type=self.importer_type) + for imp in models.ImporterType.objects.filter( + available=True, is_import=True, type=self.importer_type + ) ] def _filter_group(self, user): @@ -294,9 +304,9 @@ class BaseImportForm(IshtarForm, forms.ModelForm): def clean(self): data = self.cleaned_data if ( - data.get("conservative_import", None) - and data.get("importer_type") - and not data.get("importer_type").unicity_keys + data.get("conservative_import", None) + and data.get("importer_type") + and not data.get("importer_type").unicity_keys ): raise forms.ValidationError( _( @@ -363,13 +373,13 @@ class NewImportForm(BaseImportForm): zf.testzip() except zipfile.BadZipFile: raise forms.ValidationError( - _("\"Associated images\" field must be a valid zip file.") + _('"Associated images" field must be a valid zip file.') ) self._clean_csv(is_csv=True) archive_required = self._need_archive(data) if archive_required and ( - not data.get("imported_images", None) and - not data.get("imported_images_link", None) + not data.get("imported_images", None) + and not data.get("imported_images_link", None) ): raise forms.ValidationError(_("This importer need a document archive.")) return data @@ -448,7 +458,9 @@ class NewImportGISForm(BaseImportForm): raise forms.ValidationError("") except forms.ValidationError: raise forms.ValidationError( - _("GIS file must be a zip containing a ShapeFile or GeoPackage file.") + _( + "GIS file must be a zip containing a ShapeFile or GeoPackage file." + ) ) return value @@ -624,39 +636,70 @@ class TargetKeyForm(forms.ModelForm): class PreImportForm(IshtarForm): def __init__(self, *args, **kwargs): - self.HEADERS = {} + self._headers = {} + self._types = [] self.import_item = kwargs.pop("import_item") super().__init__(*args, **kwargs) - readonly = self.import_item.state not in ("C", "AP", "A") + readonly = self.import_item.state not in ("C", "AP", "A") for column in self.column_query.order_by("col_number"): q = column.targets if not q.count(): continue target = q.all()[0] - field_name, widget_name = FORMATER_WIDGETS_DCT[target.formater_type.formater_type] + field_name, widget_name = FORMATER_WIDGETS_DCT[ + target.formater_type.formater_type + ] attrs = { "label": column.label, "required": column.required, } - q = models.ImportColumnValue.objects.filter(column=column, - import_item=self.import_item) + q = models.ImportColumnValue.objects.filter( + column=column, import_item=self.import_item + ) if q.count(): - attrs["initial"] = q.all()[0].value + value = q.all()[0].value + if target.formater_type.many_split: + if value.startswith("['") and value.endswith("']"): + value = value[2:-2].split("', '") + else: + value = "" + attrs["initial"] = value if column.description: attrs["help_text"] = column.description if widget_name: attrs["widget"] = getattr(forms, widget_name) key = f"col_{- column.col_number}" - self.fields[key] = getattr(forms, field_name)( - **attrs - ) + form_field = getattr(forms, field_name) + if field_name == "ChoiceField": + if not target.formater_type.options: + continue + options = target.formater_type.options.split(".") + if len(options) == 1: + app = "ishtar_common" + else: + app = options[0] + model_name = options[-1] + try: + model = apps.get_model(app, model_name) + except LookupError: + continue + if target.formater_type.many_split: + form_field = widgets.Select2MultipleField + self._types.append( + FieldType(key, model, is_multiple=bool(target.formater_type.many_split)) + ) + self.fields[key] = form_field(**attrs) if readonly: self.fields[key].widget.attrs["readonly"] = True - if not self.HEADERS: - self.HEADERS[key] = FormHeader(self.import_item) + if not self._headers: + self._headers[key] = FormHeader(self.import_item) if self.import_item.importer_type.pre_import_message: - self.HEADERS[key].help_message = self.import_item.importer_type.pre_import_message + self._headers[ + key + ].help_message = self.import_item.importer_type.pre_import_message + self._init_types() + self._post_init() @property def column_query(self): @@ -668,8 +711,7 @@ class PreImportForm(IshtarForm): if key not in self.cleaned_data: continue col_value, __ = models.ImportColumnValue.objects.get_or_create( - column=column, - import_item=self.import_item + column=column, import_item=self.import_item ) col_value.value = self.cleaned_data[key] col_value.save() @@ -736,7 +778,9 @@ class OrganizationForm(ManageOldType, NewItemForm): ) if dct["precise_town_id"]: try: - dct["precise_town_id"] = models.Town.objects.get(pk=dct["precise_town_id"]).pk + dct["precise_town_id"] = models.Town.objects.get( + pk=dct["precise_town_id"] + ).pk except models.Town.DoesNotExist: dct.pop("precise_town_id") if not item: @@ -1334,14 +1378,18 @@ class AccountForm(IshtarForm): hidden_password = forms.CharField( label=_("New password"), max_length=128, - widget=forms.PasswordInput(attrs={'autocomplete': 'off', 'data-toggle': 'password'}), + widget=forms.PasswordInput( + attrs={"autocomplete": "off", "data-toggle": "password"} + ), required=False, validators=[validators.MinLengthValidator(4)], ) hidden_password_confirm = forms.CharField( label=_("New password (confirmation)"), max_length=128, - widget=forms.PasswordInput(attrs={'autocomplete': 'off', 'data-toggle': 'password'}), + widget=forms.PasswordInput( + attrs={"autocomplete": "off", "data-toggle": "password"} + ), required=False, ) @@ -1396,9 +1444,7 @@ class AccountForm(IshtarForm): _("You must provide a correct " "password.") ) # check username unicity - q = User.objects.filter( - username=cleaned_data.get("username") - ) + q = User.objects.filter(username=cleaned_data.get("username")) if cleaned_data.get("pk"): q = q.exclude(ishtaruser__person__pk=cleaned_data.get("pk")) if q.count(): @@ -1442,10 +1488,7 @@ class ProfileFormsetBase(FormSetWithDeleteSwitches): for data in self.cleaned_data: if not data.get("profile_type", None): continue - value = ( - data.get("profile_type", None), - data.get("name", None) - ) + value = (data.get("profile_type", None), data.get("name", None)) if value in values: raise forms.ValidationError(_("Choose different name for profiles.")) values.append(value) @@ -2338,8 +2381,7 @@ class DocumentSelect(HistorySelect): validators=[models.valid_id(Operation)], ) operations__operation_type = forms.ChoiceField( - label=_("Operation - type"), - choices=[] + label=_("Operation - type"), choices=[] ) operations__year = forms.IntegerField(label=_("Operation - year")) context_record = forms.IntegerField( @@ -2823,13 +2865,17 @@ class GISForm(forms.ModelForm, CustomForm, ManageOldType): 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."), + 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) + label="", required=True, widget=forms.HiddenInput, disabled=True + ) source_id = forms.IntegerField( - label="", required=True, widget=forms.HiddenInput, disabled=True) + label="", required=True, widget=forms.HiddenInput, disabled=True + ) data_type = widgets.ModelChoiceField( model=models.GeoDataType, label=_("Data type"), choices=[], required=False ) @@ -2840,7 +2886,8 @@ class GISForm(forms.ModelForm, CustomForm, ManageOldType): model=models.GeoProviderType, label=_("Provider"), choices=[], required=False ) buffer = forms.FloatField( - label=_("Buffer"), required=False, + label=_("Buffer"), + required=False, widget=widgets.MeterCentimeterWidget, ) buffer_type = widgets.ModelChoiceField( @@ -2848,15 +2895,18 @@ class GISForm(forms.ModelForm, CustomForm, ManageOldType): ) comment = forms.CharField(label=_("Comment"), widget=forms.Textarea, required=False) estimated_error_x = forms.FloatField( - label=_("Estimated error for X"), required=False, + label=_("Estimated error for X"), + required=False, widget=widgets.MeterCentimeterWidget, ) estimated_error_y = forms.FloatField( - label=_("Estimated error for Y"), required=False, + label=_("Estimated error for Y"), + required=False, widget=widgets.MeterCentimeterWidget, ) estimated_error_z = forms.FloatField( - label=_("Estimated error for Z"), required=False, + label=_("Estimated error for Z"), + required=False, widget=widgets.MeterCentimeterWidget, ) @@ -2869,12 +2919,21 @@ class GISForm(forms.ModelForm, CustomForm, ManageOldType): class Meta: model = models.GeoVectorData - exclude = ["need_update", "imports", "cached_x", "cached_y", "cached_z", - "source_content_type", "timestamp_label", "timestamp_geo"] + exclude = [ + "need_update", + "imports", + "cached_x", + "cached_y", + "cached_z", + "source_content_type", + "timestamp_label", + "timestamp_geo" + ] HEADERS = { "related_items_ishtar_common_town": FormHeader( - _("Related items"), collapse=True), + _("Related items"), collapse=True + ), "name": FormHeader(_("Metadata")), "geo_field": FormHeader(_("Geography")), "buffer": FormHeader(_("Buffer"), collapse=True), @@ -2911,8 +2970,9 @@ class GISForm(forms.ModelForm, CustomForm, ManageOldType): if "too_many" in kwargs: self.too_many = kwargs.pop("too_many") self.user = None - self.geom_type = kwargs.pop("geom_type") \ - if kwargs.get("geom_type", None) else None + self.geom_type = ( + kwargs.pop("geom_type") if kwargs.get("geom_type", None) else None + ) if kwargs.get("user", None): self.user = kwargs.pop("user") instance = kwargs.get("instance", False) @@ -2923,10 +2983,12 @@ class GISForm(forms.ModelForm, CustomForm, ManageOldType): super().__init__(*args, **kwargs) if back_url: self.fields["back_url"] = forms.CharField( - label="", required=False, widget=forms.HiddenInput, initial=back_url) + label="", required=False, widget=forms.HiddenInput, initial=back_url + ) if find_id: self.fields["find_id"] = forms.CharField( - label="", required=False, widget=forms.HiddenInput, initial=find_id) + label="", required=False, widget=forms.HiddenInput, initial=find_id + ) if not self.fields["import_key"].initial: self.fields.pop("import_key") if not self.source_content_type: @@ -2951,7 +3013,7 @@ class GISForm(forms.ModelForm, CustomForm, ManageOldType): for field_key, label in main_items_fields[related_key]: disabled = False if kwargs.get("initial", None) and kwargs["initial"].get( - field_key, False + field_key, False ): disabled = True fields[field_key] = forms.BooleanField( @@ -2976,9 +3038,12 @@ class GISForm(forms.ModelForm, CustomForm, ManageOldType): extra_geo = ["buffer", "buffer_type"] if self.geom_type == "coordinates": geo_keys = [ - "x", "estimated_error_x", - "y", "estimated_error_y", - "z", "estimated_error_z", + "x", + "estimated_error_x", + "y", + "estimated_error_y", + "z", + "estimated_error_z", "spatial_reference_system", ] base_widget_attrs = {} @@ -3038,16 +3103,22 @@ class GISForm(forms.ModelForm, CustomForm, ManageOldType): if map_srid == 4326: widget = widgets.ReversedOSMWidget self.fields[keys[0]].widget = widget( - attrs={"map_srid": map_srid, - "geom_type": geom.geom_type, - "cols": True}) + attrs={ + "map_srid": map_srid, + "geom_type": geom.geom_type, + "cols": True, + } + ) self.fields.pop("spatial_reference_system") geo_keys = keys[:] else: geo_keys = [ - "x", "estimated_error_x", - "y", "estimated_error_y", - "z", "estimated_error_z", + "x", + "estimated_error_x", + "y", + "estimated_error_y", + "z", + "estimated_error_z", "spatial_reference_system", ] for geo_fields in self.GEO_FIELDS: @@ -3074,21 +3145,17 @@ class GISForm(forms.ModelForm, CustomForm, ManageOldType): def clean(self): cleaned_data = self.cleaned_data - if cleaned_data.get("buffer", None) \ - and not cleaned_data.get("buffer_type", None): - raise forms.ValidationError( - _("If you set a buffer set a buffer type.") - ) - if cleaned_data.get("buffer_type", None) \ - and not cleaned_data.get("buffer", None): - raise forms.ValidationError( - _("If you set a buffer type set a buffer.") - ) + if cleaned_data.get("buffer", None) and not cleaned_data.get( + "buffer_type", None + ): + raise forms.ValidationError(_("If you set a buffer set a buffer type.")) + if cleaned_data.get("buffer_type", None) and not cleaned_data.get( + "buffer", None + ): + raise forms.ValidationError(_("If you set a buffer type set a buffer.")) if cleaned_data.get("point_3d"): if "z" not in cleaned_data: - raise forms.ValidationError( - _("A value is expected for Z.") - ) + raise forms.ValidationError(_("A value is expected for Z.")) value = cleaned_data["point_3d"].ewkt if "POINT Z" not in value: value = value.replace("POINT", "POINT Z") @@ -3154,7 +3221,7 @@ class PreGISForm(IshtarForm): ("multi_points", _("Multi-points")), ("multi_line", _("Multi-lines")), ("multi_polygon", _("Multi-polygons")), - ) + ), ) HEADERS = { "geom_type": FormHeader(_("Type")), @@ -3167,4 +3234,5 @@ class PreGISForm(IshtarForm): super().__init__(*args, **kwargs) if back_url: self.fields["back_url"] = forms.CharField( - label="", required=False, widget=forms.HiddenInput, initial=back_url) + label="", required=False, widget=forms.HiddenInput, initial=back_url + ) |