summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilip Sargent <philip.sargent@gmail.com>2023-11-07 02:12:57 +0200
committerPhilip Sargent <philip.sargent@gmail.com>2023-11-07 02:12:57 +0200
commit07d9365747abda0e08fc3240219d142e340b6b20 (patch)
tree6c5d3b5fba857536876af5f6324b2c72e62067bb
parentbd0a9332df03952b8d7d7c6e6803b774fd2e2278 (diff)
downloadtroggle-07d9365747abda0e08fc3240219d142e340b6b20.tar.gz
troggle-07d9365747abda0e08fc3240219d142e340b6b20.tar.bz2
troggle-07d9365747abda0e08fc3240219d142e340b6b20.zip
fixed many problems in creating new entrances
-rw-r--r--core/forms.py2
-rw-r--r--core/models/caves.py34
-rw-r--r--core/utils.py5
-rw-r--r--core/views/caves.py104
-rw-r--r--templates/caveindex.html4
-rw-r--r--templates/caveslist.html29
-rw-r--r--templates/errors/generic.html4
-rw-r--r--urls.py7
8 files changed, 136 insertions, 53 deletions
diff --git a/core/forms.py b/core/forms.py
index 5cbbe95..1c2c299 100644
--- a/core/forms.py
+++ b/core/forms.py
@@ -210,7 +210,7 @@ class EntranceLetterForm(ModelForm):
"""
# This only needs to be required=True for the second and subsequent entrances, not the first. Tricky.
- entranceletter = forms.CharField(required=True, widget=forms.TextInput(attrs={"size": "2"}))
+ entranceletter = forms.CharField(required=False, widget=forms.TextInput(attrs={"size": "2"}))
class Meta:
model = CaveAndEntrance
diff --git a/core/models/caves.py b/core/models/caves.py
index ab8414b..01ec4a3 100644
--- a/core/models/caves.py
+++ b/core/models/caves.py
@@ -198,22 +198,13 @@ class Cave(TroggleModel):
res += "&ndash;" + prevR
return res
- def writeDataFile(self):
- """Seems to be a duplicate of file_output() ?!
- REFACTOR"""
- filepath = os.path.join(settings.CAVEDESCRIPTIONS, self.filename)
-
- t = loader.get_template("dataformat/cave.xml")
- now = datetime.now(timezone.utc)
- print(now)
- c = dict({"cave": self, "date": now})
- u = t.render(c)
- writetrogglefile(filepath, u)
- return
-
def file_output(self):
"""This produces the content which wll be re-saved as the cave_data html file.
"""
+ if not self.filename:
+ self.filename = self.slug() + ".html"
+ self.save()
+
filepath = Path(settings.CAVEDESCRIPTIONS, self.filename)
t = loader.get_template("dataformat/cave.xml")
@@ -221,6 +212,11 @@ class Cave(TroggleModel):
c = dict({"cave": self, "date": now})
content = t.render(c)
return (filepath, content, "utf8")
+
+ def writeDataFile(self):
+ filepath, content, coding = self.file_output()
+ writetrogglefile(filepath, content)
+ return
class Entrance(TroggleModel):
MARKING_CHOICES = (
@@ -388,6 +384,9 @@ class Entrance(TroggleModel):
return Path(settings.ENTRANCEDESCRIPTIONS, self.filename)
def file_output(self):
+ if not self.filename:
+ self.filename = self.slug + ".html"
+ self.save()
filepath = Path(os.path.join(settings.ENTRANCEDESCRIPTIONS, self.filename))
t = loader.get_template("dataformat/entrance.xml")
@@ -397,13 +396,8 @@ class Entrance(TroggleModel):
return (filepath, content, "utf8")
def writeDataFile(self):
- filepath = os.path.join(settings.ENTRANCEDESCRIPTIONS, self.filename)
-
- t = loader.get_template("dataformat/entrance.xml")
- now = datetime.now(timezone.utc)
- c = dict({"entrance": self, "date": now})
- u = t.render(c)
- writetrogglefile(filepath, u)
+ filepath, content, coding = self.file_output()
+ writetrogglefile(filepath, content)
return
def url_parent(self):
diff --git a/core/utils.py b/core/utils.py
index 0e0dfcc..7111c0c 100644
--- a/core/utils.py
+++ b/core/utils.py
@@ -152,6 +152,8 @@ def write_and_commit(files, message):
# GIT see also core/views/uploads.py dwgupload()
# GIT see also core/views/expo.py editexpopage()
os.makedirs(os.path.dirname(filepath), exist_ok = True)
+ if filepath.is_dir():
+ return False
if encoding:
mode = "w"
kwargs = {"encoding": encoding}
@@ -160,7 +162,7 @@ def write_and_commit(files, message):
kwargs = {}
try:
with open(filepath, mode, **kwargs) as f:
- print(f"WRITING {cwd}---{filename} ")
+ print(f"WRITING {cwd}/{filename} ")
f.write(content)
except PermissionError:
raise WriteAndCommitError(
@@ -226,6 +228,7 @@ def write_and_commit(files, message):
raise WriteAndCommitError(
f"CANNOT git on server for this file {filename}. Subprocess error. Edits not saved.\nAsk a nerd to fix this."
)
+ return True
class WriteAndCommitError(Exception):
diff --git a/core/views/caves.py b/core/views/caves.py
index adb3398..9d2512d 100644
--- a/core/views/caves.py
+++ b/core/views/caves.py
@@ -475,6 +475,30 @@ def edit_entrance(request, path="", caveslug=None, entslug=None):
GET RID of all this entranceletter stuff. Far too overcomplexified.
We don't need it. Just the entrance slug is fine, then check uniqueness.
"""
+ def check_new_slugname_ok(slug, letter):
+ """In Nov.2023 it is possible to create a 2nd entrance and not set an entrance letter,
+ which leads to a constraint uniqueness crash. FIX THIS.
+ The letter may be set to an existing letter, OR it may be unset, but there may
+ be an existing unlettered single entrance. Both of these will crash unless fixed.
+ """
+ slugname = f"{slug}{letter}"
+ nents = Entrance.objects.filter(slug=slugname).count()
+ print(f"NUM ents {slugname=} => {nents}")
+ if nents == 0:
+ # looks good, but we need to check the CaveaAndEntrance object too
+ e = entrance #Entrance.objects.get(slug=slugname) # does not exist yet!
+ gcl = GetCaveLookup()
+ c = gcl[slug]
+ nce = CaveAndEntrance.objects.filter(cave=c, entrance=e).count()
+ if nce == 0 :
+ return slugname, letter
+
+ # That entrance already exists, or the CE does, OK.. do recursive call, starting at "b"
+ if letter =="":
+ return check_new_slugname_ok(slug, "b")
+ else:
+ nextletter = chr(ord(letter)+1)
+ return check_new_slugname_ok(slug, nextletter)
try:
cave = Cave.objects.get(caveslug__slug=caveslug)
@@ -495,42 +519,60 @@ def edit_entrance(request, path="", caveslug=None, entslug=None):
caveAndEntrance = CaveAndEntrance.objects.get(entrance=entrance, cave=cave)
entlettereditable = False
else:
- caveAndEntrance = CaveAndEntrance(cave=cave, entrance=Entrance())
+ caveAndEntrance = CaveAndEntrance(cave=cave, entrance=Entrance()) # creates a new Entrance object as well as a new CE object
entlettereditable = True
- if caveAndEntrance.entranceletter == "" and cave.entrances().count() > 0 :
+ ce = caveAndEntrance
+ if ce.entranceletter == "" and cave.entrances().count() > 0 :
# this should not be blank on a multiple-entrance cave
# but it doesn't trigger the entrance letter form unless entletter has a value
- entlettereditable = True
+ entlettereditable = True # but the user has to remember to actually set it...
print(f"{entlettereditable=}")
- # if the entletter is no editable, then the entletterform does not appear and so is always invalid.
+ # if the entletter is not editable, then the entletterform does not appear and so is always invalid.
if request.POST:
print(f"POST Online edit of entrance: '{entrance}' where {cave=}")
entform = EntranceForm(request.POST, instance=entrance)
+
if not entlettereditable:
- entranceletter = caveAndEntrance.entranceletter
- ce = caveAndEntrance
+ entranceletter = ce.entranceletter
else:
- entletterform = EntranceLetterForm(request.POST, instance=caveAndEntrance)
+ entletterform = EntranceLetterForm(request.POST, instance=ce)
if entletterform.is_valid():
ce = entletterform.save(commit=False)
entranceletter = entletterform.cleaned_data["entranceletter"]
+ message = f"- POST valid {caveslug=} {entslug=} {path=} entletterform valid \n {entletterform=}."
+ print(message)
else:
- print(f"- POST INVALID {caveslug=} {entslug=} {path=} entletterform invalid.")
- return render(request, "errors/badslug.html", {"badslug": "entletter problem in edit_entrances()"})
- # if entform.is_valid() and entletterform.is_valid():
- if entform.is_valid():
+ # maybe this doesn't matter? It just means entranceletter unset ?
+ # probably because 'Cave and entrance with this Cave and Entranceletter already exists.'
+ message = f"- POST INVALID {caveslug=} {entslug=} {path=} entletterform invalid \n{entletterform.errors=}\n{entletterform=}."
+ print(message)
+ # if entletterform.errors:
+ # for field in entletterform:
+ # for error in field.errors:
+ # print(f"ERR {field=} {error=}")
+ # return render(request, "errors/generic.html", {"message": message})
+ entranceletter=""
+
+ if not entform.is_valid():
+ message = f"- POST INVALID {caveslug=} {entslug=} {path=} entform valid:{entform.is_valid()} entletterform valid:{entletterform.is_valid()}"
entrance = entform.save(commit=False)
+ print(message)
+ return render(request, "errors/generic.html", {"message": message})
+ else:
print(f"- POST {caveslug=} {entslug=} {entranceletter=} {path=}")
if entslug is None:
+ # we are creating a new entrance
if entranceletter:
- slugname = cave.slug() + entranceletter
- print(f"- POST letter {entranceletter=}")
+ slugname, letter = check_new_slugname_ok(cave.slug(), entranceletter)
else:
- slugname = cave.slug()
+ slugname, letter = check_new_slugname_ok(cave.slug(), "")
+ ce.entranceletter = letter
+ entrance = ce.entrance # the one we created earlier
+
entrance.slug = slugname
entrance.cached_primary_slug = slugname
entrance.filename = slugname + ".html"
@@ -541,41 +583,48 @@ def edit_entrance(request, path="", caveslug=None, entslug=None):
try:
entrance.save()
except:
- # fails with uniqueness constraint failure. Which is on CaveAndEntrance, not just on entrance, which is bizarre.
+ # fails with uniqueness constraint failure. Which is on CaveAndEntrance, not just on entrance,
+ # which is confusing to a user who is just editing an Entrance.
+ # Can happen when user specifies an existing letter! (or none, when they should set one)
print(f"SAVE EXCEPTION FAIL {entrance=}")
print(f"CAVE {cave}")
for ce in cave.entrances():
print(f"CAVE:{ce.cave} - ENT:{ce.entrance} - LETTER:'{ce.entranceletter}'")
raise
ce.entrance = entrance
+ # try not to do this:
+ # UNIQUE constraint failed: core_caveandentrance.cave_id, core_caveandentrance.entranceletter
ce.save()
entrance_file = entrance.file_output()
cave_file = cave.file_output()
-
print(f"- POST WRITE letter: '{ce}' {entrance=}")
- write_and_commit([entrance_file, cave_file], f"Online edit of entrance {entrance.slug}")
- return HttpResponseRedirect("/" + cave.url)
- else: # one of the forms is not valid
- print(f"- POST INVALID {caveslug=} {entslug=} {path=} entform valid:{entform.is_valid()} entletterform valid:{entletterform.is_valid()}")
+ if write_and_commit([entrance_file, cave_file], f"Online edit of entrance {entrance.slug}"):
+ return HttpResponseRedirect("/" + cave.url)
+ else:
+ efilepath, econtent, eencoding = entrance_file
+ cfilepath, ccontent, cencoding = cave_file
+ message = f"- FAIL write_and_commit \n entr:'{efilepath}'\n cave:'{cfilepath}'"
+ print(message)
+ return render(request, "errors/generic.html", {"message": message})
else: # GET the page, not POST, or if either of the forms were invalid when POSTed
entletterform = None
entletter = ""
- print(f"ENTRANCE in db: entranceletter = '{caveAndEntrance.entranceletter}'")
+ print(f"ENTRANCE in db: entranceletter = '{ce.entranceletter}'")
if entrance:
# re-read entrance data from file.
filename = str(entrance.slug +".html")
ent = read_entrance(filename, ent=entrance)
- print(f"ENTRANCE from file: entranceletter = '{caveAndEntrance.entranceletter}'")
+ print(f"ENTRANCE from file: entranceletter = '{ce.entranceletter}'")
entform = EntranceForm(instance=entrance)
if entslug is None:
entletterform = EntranceLetterForm()
# print(f" Getting entletter from EntranceLetterForm")
else:
- entletter = caveAndEntrance.entranceletter
+ entletter = ce.entranceletter
if entletter == "":
entletterform = EntranceLetterForm()
print(f" Blank value: getting entletter from EntranceLetterForm")
@@ -619,7 +668,14 @@ def cave_debug(request):
{"ents": ents},
)
-
+def caveslist(request):
+ caves = Cave.objects.all()
+ print("CAVESLIST")
+ return render(
+ request,
+ "caveslist.html",
+ {"caves": caves},
+ )
def get_entrances(request, caveslug):
try:
cave = Cave.objects.get(caveslug__slug=caveslug)
diff --git a/templates/caveindex.html b/templates/caveindex.html
index 135fcc8..a07350e 100644
--- a/templates/caveindex.html
+++ b/templates/caveindex.html
@@ -46,8 +46,8 @@ Black triangle <span style="color: black">&#x25B2;</span> against a name indicat
{% if cave.no_location %}<span title="the cave has an Entrance, but no entrances have located survey station" style="color: black">&#x25B2;</span>{% endif %}
{% else %}<span title="the cave has no Entrance (and is not 'pending')" style="color: orange">&#x25B2;</span>
{% endif %}
- {% else %}<span title="the cave is 'pending' creation properly" style="color: #43C6DB">&#x25BC;</span>
- {% endif %}
+ {% else %}<span title="the cave is 'pending' creation properly" style="color: #43C6DB">&#x25BC;</span>
+ {% endif %}
{% if cave.survex_file %}{% else %}<span title="no survex file is explicitly associated with the cave (but there might be a *fix somewhere)" style="color: red">*</span>{% endif %}
</td></tr>
diff --git a/templates/caveslist.html b/templates/caveslist.html
new file mode 100644
index 0000000..d950fe4
--- /dev/null
+++ b/templates/caveslist.html
@@ -0,0 +1,29 @@
+{% extends "base.html" %}
+{% block title %}Caves List page
+<!-- caveslist.html - this text visible because this template has been included -->
+{% endblock %}
+
+{% block content %}
+
+<h2 id="cmult">Caves</h2>
+<p>Debug.. gets edited to whatever is needed by programmer..
+<table>
+<tr><th>Cave</th>
+<th>cave filename</th>
+
+
+
+</tr>
+{% for c in caves %}
+<tr>
+ <td>
+ {{c}}
+ </td>
+ <td>
+ {{c.filename}}
+ </td>
+{% endfor %}
+</table>
+
+
+{% endblock %}
diff --git a/templates/errors/generic.html b/templates/errors/generic.html
index 9f345c0..272a2ee 100644
--- a/templates/errors/generic.html
+++ b/templates/errors/generic.html
@@ -7,11 +7,11 @@
<h2>Troggle Error</h2>
</div>
-
+<a href="/piclinks/wkmob.htm"><img align="right" style="margin:10px" src="/images/wkmob.jpg"></a>
<div class='middle3 login'>
<div class='space'></div>
<div class='align-center'>
- <h3>There has been an error. This is either bad data or a bug in the software.</h3>
+ <h3>There has been an error. This is either bad data troggle can't cope with (yet), or a bug in the software.</h3>
<h4>
<font color="red">
diff --git a/urls.py b/urls.py
index 7d02627..db3a8a8 100644
--- a/urls.py
+++ b/urls.py
@@ -6,7 +6,7 @@ from django.conf.urls.static import static
from troggle.core.views import statistics, survex
from troggle.core.views.auth import expologin, expologout
-from troggle.core.views.caves import (cave3d, caveindex, entranceindex,
+from troggle.core.views.caves import (cave3d, caveindex, entranceindex, caveslist,
cavepage, caveslugfwd, cavepagefwd, caveQMs, edit_cave, cave_debug,
edit_entrance, get_entrances, qm, expo_kml, expo_kmz)
from troggle.core.views.drawings import dwgallfiles, dwgfilesingle
@@ -94,8 +94,9 @@ trogglepatterns = [
re_path(r'^caves$', caveindex, name="caveindex"),
re_path(r'^indxal.htm$', caveindex, name="caveindex"), # ~420 hrefs to this url in expoweb files
re_path(r'^people/?$', notablepersons, name="notablepersons"),
-
- re_path(r'^entrances$', entranceindex, name="entranceindex"),
+ path('caveslist', caveslist, name="caveslist"),
+
+ path('entrances', entranceindex, name="entranceindex"),
re_path(r'^admin/doc/', include('django.contrib.admindocs.urls')), # needs docutils Python module (http://docutils.sf.net/).
re_path(r'^admin/', admin.site.urls), # includes admin login & logout urls & /admin/jsi18n/