import django.forms as forms from django.forms import ModelForm from django.forms.models import modelformset_factory from troggle.core.models.caves import Cave, CaveAndEntrance, Entrance from troggle.core.views.editor_helpers import HTMLarea from django.core.exceptions import ValidationError # from tinymce.widgets import TinyMCE import re """These are all the class-based Forms used by troggle. There are other, simpler, upload forms in view/uploads.py class-based forms are quicker to set up (for Django experts) but are more difficult to maintain (or even begin to understand) by non-Django experts. Notes to self, as I try to work out what the hell is going on: Note that HTMLarea invokes a widget which sets a CSS class which calls javascript in templates/html_editor_scripts_css.html - which imports jquery and codemirror directly, without declaring it anywhere or locally installing it. (!) """ todo = """ """ class CaveForm(ModelForm): """Only those fields for which we want to override defaults are listed here the other fields of the class Cave are present on the form, but use the default presentation style """ unofficial_number= forms.CharField(required=False, label="Unofficial Number used to construct internal identifiers", widget=forms.TextInput( attrs={"size": "45", "placeholder": "2035-ZB-03"})) official_name = forms.CharField(required=False, label="Name:",widget=forms.TextInput( attrs={"size": "45", "placeholder": "ideally official name in German, but any name is OK"})) underground_description = forms.CharField( required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter page content (using HTML)"}), ) explorers = forms.CharField( required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter page content (using HTML)"}), ) equipment = forms.CharField( required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter page content (using HTML)"}), ) survey = forms.CharField( required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter page content (using HTML)"}), ) # survey = forms.CharField(required = False, widget=TinyMCE(attrs={'cols': 80, 'rows': 10})) kataster_status = forms.CharField(required=False, label = "Kataster status, see below", widget=forms.TextInput(attrs={"placeholder": "see example below"}) ) kataster_code = forms.CharField(required=False, label = "Kataster code, see below", widget=forms.TextInput(attrs={"placeholder": "see example below"}) ) underground_centre_line = forms.CharField( required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter page content (using HTML)"}), ) notes = forms.CharField( required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter page content (using HTML)"}), ) references = forms.CharField( required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter page content (using HTML)"}), ) description_file = forms.CharField(required=False, label="Path of top-level description file for this cave, when a separate file is used. Otherwise blank.", widget=forms.TextInput(attrs={"size": "45","placeholder": "usually blank"}), help_text="") survex_file = forms.CharField( required=False, label="Survex file eg. caves-1623/000/000.svx", widget=forms.TextInput(attrs={"size": "45"}) ) length = forms.CharField(required=False, label="Length (m)", widget=forms.TextInput(attrs={"placeholder": "usually blank"})) depth = forms.CharField(required=False, label="Depth (m)", widget=forms.TextInput(attrs={"placeholder": "usually blank"})) extent = forms.CharField(required=False, label="Extent (m)", widget=forms.TextInput(attrs={"placeholder": "usually blank"})) subarea = forms.CharField(required=False, label="Subarea", widget=forms.TextInput(attrs={"placeholder": "usually blank, archaic"})) #cave_slug = forms.CharField() class Meta: model = Cave exclude = ("filename","url") field_order = ['unofficial_number', 'kataster_number', 'official_name', 'underground_description', 'survey', 'underground_centre_line', 'explorers', 'equipment', 'notes', 'references', 'description_file', 'survex_file', 'areacode', 'length', 'depth', 'extent', 'kataster_code', 'kataster_status' ] def clean_cave_slug(self): if self.cleaned_data["cave_slug"] == "": myArea = self.cleaned_data["areacode"] if self.data["kataster_number"]: cave_slug = f"{myArea}-{self.cleaned_data['kataster_number']}" else: cave_slug = f"{myArea}-{self.cleaned_data['unofficial_number']}" else: cave_slug = self.cleaned_data["cave_slug"] # Converting a PENDING cave to a real cave by saving this form print("EEE", cave_slug.replace("-PENDING-", "-")) return cave_slug.replace("-PENDING-", "-") def clean(self): cleaned_data = super(CaveForm, self).clean() # where is this code hidden? How does this work?? if self.data.get("kataster_number") == "" and self.data.get("unofficial_number") == "": self._errors["unofficial_number"] = self.error_class( ["Either the kataster or unoffical number is required."] ) # if self.cleaned_data.get("kataster_number") != "" and self.cleaned_data.get("official_name") == "": # self._errors["official_name"] = self.error_class(["This field is required when there is a kataster number."]) if cleaned_data.get("url") == []: self._errors["url"] = self.error_class(["This field is required."]) if cleaned_data.get("url") and cleaned_data.get("url").startswith("/"): self._errors["url"] = self.error_class(["This field cannot start with a /."]) return cleaned_data class EntranceForm(ModelForm): """Only those fields for which we want to override defaults are listed here the other fields are present on the form, but use the default presentation style """ name = forms.CharField(required=False, widget=forms.TextInput(attrs={"size": "45", "placeholder": "usually leave this blank"})) entrance_description = forms.CharField( required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter text (using HTML)"}), ) explorers = forms.CharField(required=False, widget=forms.TextInput(attrs={"size": "45"})) # explorers = forms.CharField(required = False, widget=TinyMCE(attrs={'cols': 80, 'rows': 10})) map_description = forms.CharField( label="Map (is this used?)", required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter text (using HTML)"}), ) location_description = forms.CharField( label="Location", required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter text (using HTML)"}), ) lastvisit = forms.CharField( required=False, widget=forms.TextInput(attrs={"size": "10"}), label="Last visit date, e.g. 2023-07-11" ) approach = forms.CharField( required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter text (using HTML)"}), ) underground_description = forms.CharField( required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter text (using HTML)"}), ) photo = forms.CharField( label="Photos (use 'image' button)", required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Use button on right to add HTML link"}), ) marking_comment = forms.CharField( label="Marking text", required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter exact tag text, e.g. 'CUCC 2035 ZB-03'"}), ) findability_description = forms.CharField( required=False, label="How to find it", widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter text (using HTML)"}), ) other_description = forms.CharField( label="Other comments", required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Usually blank"}), ) bearings = forms.CharField( label="Bearings (obsolete)", required=False, widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Usually blank"}), ) tag_station = forms.CharField( required=False, widget=forms.TextInput(attrs={"size": "50","placeholder": "e.g. 1623.t2035-zb-03a"}), label="Tag station: Survex station id, e.g. 1623.p2023-aa-01" ) other_station = forms.CharField( required=False, widget=forms.TextInput(attrs={"size": "50","placeholder": "e.g. 1623.p2035-zb-03c"}), label="Other station: Survex station id, e.g. 1623.gps2018-aa-01" ) lat_wgs84 = forms.CharField( required=False, widget=forms.TextInput(attrs={"size": "10","placeholder": "e.g. 47.123456"}), label="Latitude (WSG84) - if no other location" ) long_wgs84 = forms.CharField( required=False, widget=forms.TextInput(attrs={"size": "10","placeholder": "e.g. 13.123456"}), label="Longitude (WSG84) - if no other location" ) alt = forms.CharField(required=False, label="Altitude (m) - from GPS if you have it, but let it settle.") # url = forms.CharField(required=False, label="URL [usually blank]", widget=forms.TextInput(attrs={"size": "45"})) field_order = ['name', 'entrance_description', 'explorers', 'map_description', 'location_description', 'lastvisit', 'approach', 'underground_description', 'photo', 'marking_comment', 'findability_description', 'other_description', 'bearings', 'tag_station', 'other_station', 'easting', 'northing', 'lat_wgs84', 'long_wgs84', 'alt'] class Meta: model = Entrance exclude = ( "cached_primary_slug", "filename", "slug" ) def clean(self): if self.cleaned_data.get("url").startswith("/"): self._errors["url"] = self.error_class(["This field cannot start with a /."]) return self.cleaned_data # This next line is called from the templates/edit_cave.html template. # This is sufficient to create an entire entry for for the cave fields automatically # http://localhost:8000/cave/new/ # using django built-in Deep Magic. https://docs.djangoproject.com/en/dev/topics/forms/modelforms/ # for forms which map directly onto a Django Model CaveAndEntranceFormSet = modelformset_factory(CaveAndEntrance, exclude=("cave",)) # This is used only in edit_entrance() in views/caves.py class EntranceLetterForm(ModelForm): """Form to link entrances to caves, along with an entrance number. Nb. The relationship between caves and entrances has historically been a many to many relationship. With entrances gaining new caves and letters when caves are joined. """ # This only needs to be required=True for the second and subsequent entrances, not the first. Tricky. entranceletter = forms.CharField(required=False, widget=forms.TextInput(attrs={"size": "2"})) class Meta: model = CaveAndEntrance exclude = ("cave", "entrance") def full_clean(self): super(EntranceLetterForm, self).full_clean() try: self.instance.validate_unique() except forms.ValidationError as e: self._update_errors(e)