summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Containerfile83
-rw-r--r--core/TESTS/test_urls.py4
-rw-r--r--parsers/caves.py39
-rw-r--r--parsers/locations.py23
-rw-r--r--parsers/people.py2
-rw-r--r--parsers/survex.py43
-rw-r--r--templates/logbookform.html4
7 files changed, 130 insertions, 68 deletions
diff --git a/Containerfile b/Containerfile
index acefff1..c771b40 100644
--- a/Containerfile
+++ b/Containerfile
@@ -1,46 +1,71 @@
-# Containerfile originally created by MS Copilot 1st July 2025
-# hand edited Philip Sargent
-# move this to the directory above troggle, loser etc before running it.
+# create an oci container image with
+# cd /home/expo && podman build -t expo:dev --rm -f troggle/Containerfile
#
-FROM ghcr.io/astral-sh/uv:python3.13-bookworm
-WORKDIR /EXPO
+FROM docker.io/library/debian:bookworm
+
+WORKDIR /home/expo2
-USER root
RUN apt update && \
- apt-get install sqlite3 survex rsync -y && \
- useradd -m expopod -G sudo -s /bin/bash
+ apt install -y postgresql apache2 survex rsync git cgit proftpd \
+ python3 python3-django python3-pil python3-piexif \
+ python3-bs4 python3-unidecode python3-cryptography \
+ libjs-codemirror
+# do we need libjs-leaflet? libjs-sizzle? libjs-mgrs?
+# Install non-packaged dependencies
+# apt install CaveView and bins and libjs-proj4 from local repo/backports?
# Copy only the dependency files first
-COPY troggle/pyproject.toml troggle/uv.lock ./troggle/
-
-COPY expofiles/surveyscans/2018 /EXPO/expofiles/surveyscans/2018
-COPY expofiles/photos/2018/PhilipSargent/ /EXPO/expofiles/photos/2018/PhilipSargent
-COPY troggle /EXPO/troggle
-COPY expoweb /EXPO/expoweb
-COPY loser /EXPO/loser
-COPY drawings /EXPO/drawings
-RUN chown -R expopod:expopod .
-USER expopod
-
-# Install dependencies and Django
-RUN cd troggle && uv sync --frozen
-
-RUN git config --global user.email "expopod@potato.hut"
-RUN git config --global user.name "expopod"
+#wget troggle/pyproject.toml troggle/uv.lock
+#RUN wget troggle/pyproject.toml && uv sync --frozen
+
+RUN useradd -m expo -G sudo -s /bin/bash
+
+# Optional:install and configure BoE
+
+#add apache config, enable modules
+#configure postgres
+
+
+#Start up services for apache, proftpd, postgresql, cron?
+
+#end of system stage
+
+
+# User files - separate layer?
+RUN chown expo:expo .
+USER expo
+
+RUN mkdir -p repositories/git && cd repositories/git && \
+ git clone http://expo.survex.com/repositories/troggle/.git && \
+ git clone http://expo.survex.com/repositories/expoweb/.git && \
+ git clone http://expo.survex.com/repositories/loser/.git && \
+ git clone http://expo.survex.com/repositories/drawings/.git
+RUN ln -s repositories/git/troggle troggle && \
+ ln -s repositories/git/troggle expoweb && \
+ ln -s repositories/git/troggle loser && \
+ ln -s repositories/git/troggle drawings
+
+RUN git config --global user.email "expo@potato.hut"
+RUN git config --global user.name "expo"
RUN git config --global pull.rebase true
-RUN cd troggle && uv run databaseReset.py reset INIT
+#rsync -az expo.survex.com:expofiles expofiles
+#demo short version
+#rsync -az expo.survex.com:expofiles/surveyscans/2018 expofiles/surveyscans/2018
+#rsync -az expo.survex.com:expofiles/photos/2018/PhilipSargent/ expofiles/photos/2018/PhilipSargent
+
+#/bin/sh is missing at this point - why?
+RUN cd troggle && run databaseReset.py reset INIT
EXPOSE 8080
+#Run postres process
CMD ["uv", "run", "python", "troggle/manage.py", "runserver", "0.0.0.0:8080"]
CMD ["bash"]
# move this file to the directory above troggle, loser etc before running the podman image build command.
-# if this was used like this:
-# podman image build . --tag expo-01
-# run this image interactively with
-# podman run -it --network=host --rm expo-01 \ No newline at end of file
+# used image with:
+# podman run -it --network=host --rm expo:dev
diff --git a/core/TESTS/test_urls.py b/core/TESTS/test_urls.py
index 861ae2c..3070d66 100644
--- a/core/TESTS/test_urls.py
+++ b/core/TESTS/test_urls.py
@@ -116,7 +116,9 @@ class URLTests(TestCase):
response = self.client.get("/statistics")
self.assertEqual(response.status_code, HTTPStatus.OK)
content = response.content.decode()
- ph = r"0 expeditions: 0 people, 0 caves and 0 logbook entries."
+ with open('_test_response.html', 'w') as f:
+ f.write(content)
+ ph = r"0 expeditions: 0 people, 0 caves, 0 wallets and 0 logbook entries"
phmatch = re.search(ph, content)
self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph + "'")
diff --git a/parsers/caves.py b/parsers/caves.py
index f063731..b8430ea 100644
--- a/parsers/caves.py
+++ b/parsers/caves.py
@@ -474,10 +474,11 @@ def read_entrance(filename, ent=None):
context = filename
# Derive the letter, entrance slug and cave slug from the filename
+ # only use lower case, even if the filename is uppercase.
entslug_fn = filename[:-5] # remove .html
- if entslug_fn[-1] in LETTERS:
+ if entslug_fn[-1].lower() in LETTERS:
caveslug_fn = entslug_fn[:-1]
- letter_fn = entslug_fn[-1]
+ letter_fn = entslug_fn[-1].lower()
else:
caveslug_fn = entslug_fn
letter_fn = ""
@@ -654,19 +655,12 @@ def read_cave(filename, mvf=None, cave=None):
if len(eslugs) < 1:
print(f"TYPO IN cave_data file <entrance> tag contents\n <entrance> contents: {e}\n {eslugs=}")
eslug = eslugs[0]
- # if eslug.endswith(('a','b','c','d','e','f')):
- # print(f"! Entrance {eslug}")
- # if eslug.endswith('a b'):
- # message = f' - Entrance has weird name slug:"{eslug}" cave:"{cave}" caveslug:"{slug}" filename:"cave_data/{filename}"'
- # DataIssue.objects.create(parser="xEntrances", message=message, url=f"{cave.url}_cave_edit/")
- # print(message)
-
letter = getXML(e, "letter", maxItems=1, context=context)[0]
if len(entrances) > 1 and letter =="":
# Usually the second entrance is 'b', but the first is still unlettered. So probably 'a'
letter = eslug[-1].lower()
- if letter.lower() not in list(string.ascii_lowercase):
+ if letter.lower() not in LETTERS:
letter = "a"
message = f"- Warning - Empty 'letter' field for '{eslug}' in multiple-entrance cave '{cave}', setting to {letter}."
#eurl = f"{cave.url}_cave_edit/"
@@ -686,12 +680,29 @@ def read_cave(filename, mvf=None, cave=None):
else:
# print(f"eslug {eslug} looking up entrance ")
try:
+ entrance_count = Entrance.objects.filter(slug=eslug).count()
+ except:
+ message = f"! Fail entrance count {eslug}. {entrance_count=}"
+ print(message)
+ if entrance_count == 1:
entrance = Entrance.objects.get(slug=eslug)
entrances_xslug[eslug] = entrance
- except:
- message = f"! Fail entrance loading {eslug} /entrance_data/{eslug} file does not exist or loading it from {filename} failed."
- DataIssue.objects.create(parser="entrances", message=message, url=f"{cave.url}_cave_edit/")
- print(message)
+ else:
+ message = f"! Entrance count {entrance_count=} for {eslug}. "
+ print(message)
+ eslug = eslug[:-1] + eslug[-1].upper()
+ entrance_count = Entrance.objects.filter(slug=eslug).count()
+ message = f"! Entrance count {entrance_count=} for uppercase variant {eslug}. "
+ print(message)
+ if entrance_count == 1:
+ entrance = Entrance.objects.get(slug=eslug)
+ entrances_xslug[eslug] = entrance
+ message = f"! Warning entrance loading {eslug} only exists as upper case variant "
+ DataIssue.objects.create(parser="entrances", message=message, url=f"{cave.url}_cave_edit/")
+ else:
+ message = f"! Fail entrance loading {eslug}. Probably /entrance_data/{eslug} file does not exist or loading it from {filename} failed."
+ DataIssue.objects.create(parser="entrances", message=message, url=f"{cave.url}_cave_edit/")
+ print(message)
print(e)
return
diff --git a/parsers/locations.py b/parsers/locations.py
index 2984af2..9a6a65e 100644
--- a/parsers/locations.py
+++ b/parsers/locations.py
@@ -290,27 +290,8 @@ def LoadPositions():
d3dpath = topdata.with_suffix(".3d")
pospath = topdata.with_suffix(".pos")
- runcavern3d(f"Regen {settings.DEVSERVER=}") # always regenerate .3d and .pos as the *includes may have changed
- # if not settings.DEVSERVER:
- # runcavern3d(f"Regen - on server {settings.DEVSERVER=}") # always regenerate .3d and .pos on the server
- # else:
- # # These basic tests fail to capture the case where a *included svx file has changed,
- # # typically this is one of the fixedpts *fix files.
- # for p in [pospath, d3dpath]:
- # if not p.is_file():
- # runcavern3d(f"Creating {p}.3d, .pos")
- # svx_t = svxpath.stat().st_mtime
- # d3d_t = d3dpath.stat().st_mtime # os.path.getmtime(d3dpath)
- # svx_d = datetime.fromtimestamp(svx_t).strftime('%d %b %Y %H:%M:%S')
- # d3d_d = datetime.fromtimestamp(d3d_t).strftime('%d %b %Y %H:%M:%S')
-
- # now = time.time()
- # if d3d_t - svx_t < 0: # stale, 3d older than svx file . But .svx timestamp does not reflect *include timestamps
- # runcavern3d(f"Regen - stale {d3d_d} earlier than {svx_d}")
- # elif now - d3d_t > 24 * 60 * 60: # >1 days old, re-run anyway
- # runcavern3d(f"Regen - old")
- # elif d3d_t - cav_t < 0: # new version of cavern
- # runcavern3d(f"Regen - new survex version {d3d_d} earlier than {cav_d} ")
+ # we do not need to do this as the previous 'survex' step in databaseReset generated the .3d and .pos file
+ # runcavern3d(f"Regen {settings.DEVSERVER=}") # always regenerate .3d and .pos as the *includes may have changed
mappoints = {}
found_points = {}
diff --git a/parsers/people.py b/parsers/people.py
index 5426683..079ea3e 100644
--- a/parsers/people.py
+++ b/parsers/people.py
@@ -441,6 +441,8 @@ def GetPersonExpeditionNameLookup(expedition):
possnames.append("El Stobbarto")
if f"{f} {l}" == "Rob Watson".lower():
possnames.append("nobrotson")
+ if f"{f} {l}" == "hannah urquhart".lower():
+ possnames.append("hannah ug")
for i in [3, 4, 5, 6]:
lim = min(i, len(f) + 1) # short form, e.g. Dan for Daniel.
diff --git a/parsers/survex.py b/parsers/survex.py
index 28c16ab..4efc6f9 100644
--- a/parsers/survex.py
+++ b/parsers/survex.py
@@ -2270,8 +2270,47 @@ def FindAndLoadSurvex():
svx_scan.depthinclude = 0
fullpathtotop = str(Path(survexfileroot.path).parent / survexfileroot.path)
- print(f" - RunSurvexIfNeeded cavern on '{fullpathtotop}'", file=sys.stderr)
- svx_scan.RunSurvexIfNeeded(fullpathtotop, fullpathtotop)
+ # In fact we always want to run this, and the location stuff later needs the .pos file
+ # so we should not be using the RunSurvexIfNeeded function.
+ print(f" - Running cavern on '{fullpathtotop}'", file=sys.stderr)
+ logpath = Path(fullpathtotop + ".log")
+
+ try:
+ print(
+ f" - Regenerating cavern .pos .log and .3d for '{fullpathtotop}'\n at '{logpath}'\n"
+ )
+
+ outputdir = Path(str(f"{fullpathtotop}.svx")).parent
+ sp = subprocess.run(
+ [settings.CAVERN, "--log", "--pos", f"--output={outputdir}", f"{fullpathtotop}.svx"],
+ capture_output=True,
+ check=False,
+ text=True,
+ )
+
+ if sp.returncode != 0:
+ message = f" ! Error when running {settings.CAVERN}: {fullpathtotop}"
+ url = f"/survexfile{fullpathtotop}.svx".replace(str(settings.SURVEX_DATA), "")
+ stash_data_issue(parser="survex", message=message, url=url)
+ print(message)
+ print(
+ "stderr:\n\n" + str(sp.stderr) + "\n\n" + str(sp.stdout) + "\n\nreturn code: " + str(sp.returncode)
+ )
+ self.caverncount += 1
+
+ # should also collect all the .err files too and create a DataIssue for each one which
+ # - is nonzero in size AND
+ # - has Error greater than 5% anywhere, or some other more serious error
+
+ errpath = Path(fullpathtotop + ".err")
+ if errpath.is_file():
+ if errpath.stat().st_size == 0:
+ errpath.unlink() # delete empty closure error file
+ except:
+ message = f' ! FAIL running cavern on survex file "{fullpathtotop}"'
+ stash_data_issue(parser="survex", message=message)
+ print(message)
+
svx_scan.uniquefile[str(survexfileroot)] = ["0"]
indent = ""
diff --git a/templates/logbookform.html b/templates/logbookform.html
index 8d9e86e..28a28c8 100644
--- a/templates/logbookform.html
+++ b/templates/logbookform.html
@@ -11,6 +11,7 @@
{% endif %}
<div align=center>
{% if date %}<p>Link to <em><a href="/logbookentry/{{date}}/{{slug}}#">this entry</a></em> &nbsp;&nbsp;{% endif %}
+&nbsp;&nbsp;<a href="/logreport/{{year}}">logbook report {{year}}</a>&nbsp;&nbsp;
<a href="/handbook/logbooks.html#form">How to use this form</a>
</div>
{% if save_bad %}
@@ -70,6 +71,7 @@
title="Place: cave name, or 'plateau', 'topcamp' "
{% if place %}value="{{place}}"{% else %}placeholder="basecamp" {% endif %}
required />
+<br>(If any cave is visited at all, put in the cave id and not 'plateau')
<br /><br />
<label for="title">Title</label>
<input {% if not user.username %} disabled{% endif %}
@@ -81,7 +83,7 @@
<textarea {% if not user.username %} disabled{% endif %}
rows="{% if textrows%}{{textrows}}{% else %}5{% endif %}" cols="70"
label = "" name = "text"
- required />{% if entry %}{{entry}}{% else %}We had a lot of fun...{% endif %}
+ required />{% if entry %}{{entry}}{% else %}We had a lot of fun in the rain...{% endif %}
</textarea>
<br>
[Type in text in <a href="/handbook/logbooks.html#form">Logbook HTML format</a>.]