2020-05-28 04:54:53 +01:00
2024-12-15 18:54:47 +00:00
# from tinymce.widgets import TinyMCE
import re
2011-07-11 02:10:22 +01:00
import django . forms as forms
2024-12-15 18:54:47 +00:00
from django . core . exceptions import ValidationError
2020-05-28 04:54:53 +01:00
from django . forms import ModelForm
2012-06-10 14:59:21 +01:00
from django . forms . models import modelformset_factory
2023-01-19 18:35:56 +00:00
2023-01-29 16:47:46 +00:00
from troggle . core . models . caves import Cave , CaveAndEntrance , Entrance
2023-01-19 18:35:56 +00:00
from troggle . core . views . editor_helpers import HTMLarea
2020-05-28 04:54:53 +01:00
2023-01-30 19:04:36 +00:00
""" These are all the class-based Forms used by troggle.
2022-03-22 02:22:15 +00:00
There are other , simpler , upload forms in view / uploads . py
2023-07-31 13:49:54 +01:00
class - based forms are quicker to set up ( for Django experts ) but
2023-11-02 19:05:08 +00:00
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 . ( ! )
2024-12-16 13:08:58 +00:00
Django handles three distinct parts of the work involved in forms :
- preparing and restructuring data to make it ready for rendering
- creating HTML forms for the data
- receiving and processing submitted forms and data from the client
It is possible to write code that does all of this manually , but Django can take care of it all for you .
READ https : / / docs . djangoproject . com / en / 5.1 / topics / forms / and thoroughly digest it , also :
https : / / pythontimes . com / django - forms - deep - dive - advanced - techniques - for - form - handling /
https : / / docs . djangoproject . com / en / 5.1 / ref / forms / models /
https : / / stackoverflow . com / questions / 53035151 / django - formset - factory - vs - modelformset - factory - vs - inlineformset - factory
https : / / micropyramid . com / blog / understanding - djangos - model - formsets - in - detail - and - their - advanced - usage
https : / / www . geeksforgeeks . org / django - modelformsets /
https : / / www . codeunderscored . com / model - formsets - in - django /
https : / / django - formset . fly . dev / styling /
2023-01-30 19:04:36 +00:00
"""
todo = """
"""
2021-04-13 01:37:42 +01:00
2021-04-21 19:08:42 +01:00
2012-01-07 19:05:25 +00:00
class CaveForm ( ModelForm ) :
2023-01-30 19:04:36 +00:00
""" Only those fields for which we want to override defaults are listed here
2023-09-11 18:38:14 +01:00
the other fields of the class Cave are present on the form , but use the default presentation style
2023-01-30 19:04:36 +00:00
"""
2023-11-14 13:55:11 +00:00
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 " } ) )
2023-01-30 19:04:36 +00:00
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}))
2023-11-20 19:27:09 +00:00
# kataster_status = forms.CharField(required=False,
# label = "Kataster status, see below",
# widget=forms.TextInput(attrs={"placeholder": "see example below"})
# )
2023-11-14 13:55:11 +00:00
kataster_code = forms . CharField ( required = False ,
2024-12-14 01:41:26 +00:00
label = " Kataster code, see bottom of page " ,
2023-11-14 13:55:11 +00:00
widget = forms . TextInput ( attrs = { " placeholder " : " see example below " } )
)
2023-11-17 19:41:07 +00:00
# underground_centre_line = forms.CharField(
# required=False,
# widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Enter page content (using HTML)"}),
# )
2023-01-30 19:04:36 +00:00
notes = forms . CharField (
required = False ,
2023-11-20 19:27:09 +00:00
label = " Notes, e.g. progress on issuing kataster no. " ,
2023-01-30 19:04:36 +00:00
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) " } ) ,
)
2023-11-14 13:55:11 +00:00
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 = " " )
2023-01-30 19:04:36 +00:00
survex_file = forms . CharField (
2023-04-30 19:00:22 +01:00
required = False , label = " Survex file eg. caves-1623/000/000.svx " , widget = forms . TextInput ( attrs = { " size " : " 45 " } )
2023-01-30 19:04:36 +00:00
)
2023-11-14 13:55:11 +00:00
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 " } ) )
2024-12-14 01:41:26 +00:00
subarea = forms . CharField ( required = False , label = " Subarea (do not use for new caves) " , widget = forms . TextInput ( attrs = { " placeholder " : " usually blank, archaic " } ) )
2023-11-14 13:55:11 +00:00
2023-07-05 17:43:57 +01:00
#cave_slug = forms.CharField()
2023-01-30 19:04:36 +00:00
2012-01-07 19:05:25 +00:00
class Meta :
model = Cave
2023-11-20 19:27:09 +00:00
exclude = ( " filename " , " url " , " underground_centre_line " , " kataster_status " )
2023-04-30 19:00:22 +01:00
2023-11-14 13:55:11 +00:00
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 ' ]
2023-09-11 18:38:14 +01:00
2023-04-30 19:00:22 +01:00
def clean_cave_slug ( self ) :
if self . cleaned_data [ " cave_slug " ] == " " :
2023-09-11 18:38:14 +01:00
myArea = self . cleaned_data [ " areacode " ]
2023-04-30 19:00:22 +01:00
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- " , " - " )
2012-08-14 21:51:15 +01:00
def clean ( self ) :
2023-08-03 14:11:46 +01:00
cleaned_data = super ( CaveForm , self ) . clean ( ) # where is this code hidden? How does this work??
2023-04-30 19:00:22 +01:00
if self . data . get ( " kataster_number " ) == " " and self . data . get ( " unofficial_number " ) == " " :
2023-01-30 19:04:36 +00:00
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."])
2023-11-17 16:30:00 +00:00
# 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 /."])
2023-04-30 19:00:22 +01:00
return cleaned_data
2012-08-14 21:51:15 +01:00
2023-01-30 19:04:36 +00:00
2012-06-10 14:59:21 +01:00
class EntranceForm ( ModelForm ) :
2023-01-30 19:04:36 +00:00
""" Only those fields for which we want to override defaults are listed here
2023-07-26 15:38:19 +01:00
the other fields are present on the form , but use the default presentation style
2023-01-30 19:04:36 +00:00
"""
2023-11-14 13:55:11 +00:00
name = forms . CharField ( required = False , widget = forms . TextInput ( attrs = { " size " : " 45 " , " placeholder " : " usually leave this blank " } ) )
2023-01-30 19:04:36 +00:00
entrance_description = forms . CharField (
required = False ,
2023-07-26 15:38:19 +01:00
widget = HTMLarea ( attrs = { " height " : " 80 % " , " rows " : 20 , " placeholder " : " Enter text (using HTML) " } ) ,
2023-01-30 19:04:36 +00:00
)
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 (
2023-11-14 13:55:11 +00:00
label = " Map (is this used?) " ,
2023-01-30 19:04:36 +00:00
required = False ,
2023-07-26 15:38:19 +01:00
widget = HTMLarea ( attrs = { " height " : " 80 % " , " rows " : 20 , " placeholder " : " Enter text (using HTML) " } ) ,
2023-01-30 19:04:36 +00:00
)
location_description = forms . CharField (
2023-11-14 13:55:11 +00:00
label = " Location " ,
2023-01-30 19:04:36 +00:00
required = False ,
2023-07-26 15:38:19 +01:00
widget = HTMLarea ( attrs = { " height " : " 80 % " , " rows " : 20 , " placeholder " : " Enter text (using HTML) " } ) ,
2023-01-30 19:04:36 +00:00
)
lastvisit = forms . CharField (
2023-11-14 13:55:11 +00:00
required = False , widget = forms . TextInput ( attrs = { " size " : " 10 " } ) , label = " Last visit date, e.g. 2023-07-11 "
2023-01-30 19:04:36 +00:00
)
approach = forms . CharField (
required = False ,
2023-07-26 15:38:19 +01:00
widget = HTMLarea ( attrs = { " height " : " 80 % " , " rows " : 20 , " placeholder " : " Enter text (using HTML) " } ) ,
2023-01-30 19:04:36 +00:00
)
underground_description = forms . CharField (
required = False ,
2023-07-26 15:38:19 +01:00
widget = HTMLarea ( attrs = { " height " : " 80 % " , " rows " : 20 , " placeholder " : " Enter text (using HTML) " } ) ,
2023-01-30 19:04:36 +00:00
)
photo = forms . CharField (
2023-11-14 13:55:11 +00:00
label = " Photos (use ' image ' button) " ,
2023-01-30 19:04:36 +00:00
required = False ,
2023-11-14 13:55:11 +00:00
widget = HTMLarea ( attrs = { " height " : " 80 % " , " rows " : 20 , " placeholder " : " Use button on right to add HTML link " } ) ,
2023-01-30 19:04:36 +00:00
)
marking_comment = forms . CharField (
2023-11-14 13:55:11 +00:00
label = " Marking text " ,
2023-01-30 19:04:36 +00:00
required = False ,
2023-11-14 13:55:11 +00:00
widget = HTMLarea ( attrs = { " height " : " 80 % " , " rows " : 20 , " placeholder " : " Enter exact tag text, e.g. ' CUCC 2035 ZB-03 ' " } ) ,
2023-01-30 19:04:36 +00:00
)
findability_description = forms . CharField (
required = False ,
2023-11-14 13:55:11 +00:00
label = " How to find it " ,
2023-07-26 15:38:19 +01:00
widget = HTMLarea ( attrs = { " height " : " 80 % " , " rows " : 20 , " placeholder " : " Enter text (using HTML) " } ) ,
2023-01-30 19:04:36 +00:00
)
other_description = forms . CharField (
2023-11-14 13:55:11 +00:00
label = " Other comments " ,
2023-01-30 19:04:36 +00:00
required = False ,
2023-11-14 13:55:11 +00:00
widget = HTMLarea ( attrs = { " height " : " 80 % " , " rows " : 20 , " placeholder " : " Usually blank " } ) ,
2023-01-30 19:04:36 +00:00
)
2023-11-18 13:27:08 +00:00
# bearings = forms.CharField(
# label="Bearings (obsolete)",
# required=False,
# widget=HTMLarea(attrs={"height": "80%", "rows": 20, "placeholder": "Usually blank"}),
# )
2023-07-26 15:38:19 +01:00
tag_station = forms . CharField (
required = False ,
2023-11-14 13:55:11 +00:00
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 "
2023-07-26 15:38:19 +01:00
)
other_station = forms . CharField (
required = False ,
2023-11-14 13:55:11 +00:00
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 "
2023-01-30 19:04:36 +00:00
)
2023-10-11 22:25:24 +01:00
2023-07-26 15:38:19 +01:00
lat_wgs84 = forms . CharField (
2023-11-14 13:55:11 +00:00
required = False , widget = forms . TextInput ( attrs = { " size " : " 10 " , " placeholder " : " e.g. 47.123456 " } ) ,
label = " Latitude (WSG84) - if no other location "
2023-07-26 15:38:19 +01:00
)
2023-01-30 19:04:36 +00:00
long_wgs84 = forms . CharField (
2023-11-14 13:55:11 +00:00
required = False , widget = forms . TextInput ( attrs = { " size " : " 10 " , " placeholder " : " e.g. 13.123456 " } ) ,
label = " Longitude (WSG84) - if no other location "
2023-07-26 15:38:19 +01:00
)
2023-10-11 22:58:20 +01:00
alt = forms . CharField ( required = False , label = " Altitude (m) - from GPS if you have it, but let it settle. " )
2023-11-14 13:55:11 +00:00
# url = forms.CharField(required=False, label="URL [usually blank]", widget=forms.TextInput(attrs={"size": "45"}))
2023-01-30 19:04:36 +00:00
2023-11-14 13:55:11 +00:00
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 ' ]
2023-04-30 21:41:17 +01:00
2012-06-10 14:59:21 +01:00
class Meta :
model = Entrance
2023-01-30 19:04:36 +00:00
exclude = (
" cached_primary_slug " ,
2023-11-14 13:55:11 +00:00
" filename " ,
2023-11-18 13:27:08 +00:00
" slug " ,
" bearings "
2023-01-30 19:04:36 +00:00
)
2015-09-16 01:52:45 +01:00
def clean ( self ) :
2023-11-17 16:30:00 +00:00
# if self.cleaned_data.get("url"): # can remove this as the form does not have a url field any more, which was never used anyway
# if self.cleaned_data.get("url").startswith("/"):
# self._errors["url"] = self.error_class(["This field cannot start with a /."])
2015-09-16 01:52:45 +01:00
return self . cleaned_data
2012-06-10 14:59:21 +01:00
2012-01-07 19:05:25 +00:00
2024-12-14 00:27:19 +00:00
# # This next line is sufficient to create an entire entry for for the cave fields automatically
# # 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 templates/editcave.html which is called only to edit caves in core/views/cave.py
2012-01-07 19:05:25 +00:00
2012-08-14 21:51:15 +01:00
class EntranceLetterForm ( ModelForm ) :
2023-01-30 19:04:36 +00:00
""" 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 .
2022-08-01 15:04:22 +01:00
With entrances gaining new caves and letters when caves are joined .
2023-01-30 19:04:36 +00:00
"""
2023-11-05 13:20:45 +00:00
# This only needs to be required=True for the second and subsequent entrances, not the first. Tricky.
2023-11-07 00:12:57 +00:00
entranceletter = forms . CharField ( required = False , widget = forms . TextInput ( attrs = { " size " : " 2 " } ) )
2023-01-30 19:04:36 +00:00
2012-08-14 21:51:15 +01:00
class Meta :
model = CaveAndEntrance
2023-01-30 19:04:36 +00:00
exclude = ( " cave " , " entrance " )
2022-08-01 15:04:22 +01:00
def full_clean ( self ) :
super ( EntranceLetterForm , self ) . full_clean ( )
try :
self . instance . validate_unique ( )
except forms . ValidationError as e :
self . _update_errors ( e )