diff --git a/core/models/caves.py b/core/models/caves.py index c9405ee..4cf0512 100644 --- a/core/models/caves.py +++ b/core/models/caves.py @@ -12,7 +12,7 @@ import settings from troggle.core.models.logbooks import QM from troggle.core.models.survex import SurvexStation, utmToLatLng from troggle.core.models.troggle import DataIssue, TroggleModel -from troggle.core.utils import TROG, writetrogglefile +from troggle.core.utils import TROG, writetrogglefile, parse_aliases # Use the TROG global object to cache the cave lookup list. No good for multi-user.., or even multi-page. Pointless in fact. Gcavelookup = TROG["caves"]["gcavelookup"] @@ -532,10 +532,19 @@ def GetCaveLookup(): print(cave, cave.slug()) # These might alse create more duplicate entries - # Yes, this should be set in, and imported from, an easily editable file + aliases = [] + # read the two files in /cave_data/ + for ca in ["cavealiasesold.txt", "cavealiases.txt"]: + pairs, report = parse_aliases(ca) + aliases += pairs + + # print(f"Loaded aliases, {len(aliases)} found\n{report}\n {aliases}") + # On reset, these aliases only work if the cave already properly exists with an entry in :expoweb:/cave_data/ # but as the aliases are recomputed repeatedly, eventually they work on PENDING caves too - aliases = [ + + # oldaliases are NOT USED. We are reading from the files instead now. Pending deletion.. + oldaliases = [ ("1987-02", "1623-267"), ("1990-01", "1623-171"), ("1990-02", "1623-172"), diff --git a/core/utils.py b/core/utils.py index 6670a65..40483e3 100644 --- a/core/utils.py +++ b/core/utils.py @@ -2,6 +2,7 @@ import hashlib import logging import math import os +import re import random import resource import string @@ -43,6 +44,8 @@ sha = hashlib.new('sha256') throw = 35.0 + + # This is module-level executable. This is a Bad Thing. Especially when it touches the file system. try: logging.basicConfig(level=logging.DEBUG, filename=settings.LOGFILE, filemode="w") @@ -131,6 +134,52 @@ def current_expo(): else: return settings.EPOCH.year # this is 1970 +def parse_aliases(aliasfile): + """Reads a long text string containing pairs of strings: + (alias, target) + where the alias is an old name for a cave and the target is the current, valid name for the cave, e.g. + ("2015-mf-06", "1623-288"), + returns a list of tuple pairs + + May fail if there is more than one space after the comma separating strings + """ + + report = [] # Messages about failed lines + aliases = [] + + filepath = Path(settings.EXPOWEB) / "cave_data" / aliasfile + if not filepath.is_file(): + message = f' ** {filepath} is not a file.' + print(message) + return [(None, None)] + try: + with open(filepath, "r") as aliasfile: + for line in aliasfile: + l, sep, tail = line.partition('#') + l, sep, tail = l.partition(';') + l = l.strip().strip(',') # remove terminal comma if present + l = l.strip().strip('()') + l = l.replace("\"","") + l = l.replace("\'","") + l = l.replace(" ","") # removes all spaces + l = " ".join(l.split(',')) # subtle, splits on comma, joins with one space + if len(l) == 0: + # print(f"no parseable content: {line}") + continue + key, sep, target = l.partition(' ') + if len(key) == 0 or len(target) == 0: + message = f' ** Parsing failure for {line}' + print(message) + continue + + print(f"{key} => {target}") + aliases.append((key,target)) + except: + message = f' ** Cannot open {filepath} for text file reading even though it is a file.' + print(message) + return [(None, None)], "Fail on file reading" + return aliases, report + def only_commit(fname, message): """Only used to commit a survex file edited and saved in view/survex.py""" git = settings.GIT diff --git a/core/views/uploads.py b/core/views/uploads.py index 6ebec8c..3d4e987 100644 --- a/core/views/uploads.py +++ b/core/views/uploads.py @@ -12,7 +12,7 @@ from troggle.core.models.caves import GetCaveLookup from troggle.core.models.logbooks import LogbookEntry, writelogbook, PersonLogEntry from troggle.core.models.survex import DrawingFile from troggle.core.models.troggle import DataIssue, Expedition, PersonExpedition -from troggle.core.utils import alphabet_suffix, current_expo, sanitize_name, unique_slug +from troggle.core.utils import alphabet_suffix, current_expo, sanitize_name, unique_slug, write_and_commit from troggle.parsers.people import GetPersonExpeditionNameLookup, known_foreigner # from databaseReset import reinit_db # don't do this. databaseRest runs code *at import time* @@ -152,11 +152,11 @@ class ExpotextfileForm(forms.Form): # not a model-form, just a form-form class LogbookEditForm(forms.Form): # not a model-form, just a form-form author = forms.CharField(strip=True, required=False) +@login_required_if_public def edittxtpage(request, path, filepath): """Editing a .txt file on expoweb/ """ - message="" - def simple_get(): + def simple_get(viewtext): form = ExpotextfileForm() return render( request, @@ -166,23 +166,26 @@ def edittxtpage(request, path, filepath): "path": path, "message": message, "filepath": filepath, - "text": text, + "text": viewtext, }, ) + message="" + if not filepath.is_file(): print(f"Not a file: {filepath}") errpage = f"" + default_head + f"

File not found '{filepath}'

failure detected in expowebpage() in views.expo.py

" return HttpResponse(errpage) try: - with open(filepath) as f: - text = f.read() + with open(filepath, "r") as f: + originaltext = f.read() except IOError: - print("### File reading failue, but it exists.. ### ", filepath) - filefound = False + message = f'Cannot open {filepath} for text file reading even though it is a file.' + print(message) + return render(request, "errors/generic.html", {"message": message}) if request.method == "GET": - return simple_get() + return simple_get(originaltext) elif request.method == "POST": form = ExpotextfileForm(request.POST) @@ -191,22 +194,44 @@ def edittxtpage(request, path, filepath): print(message) return render(request, "errors/generic.html", {"message": message}) else: - for i in request.POST: - print(":: ",i, " => ", request.POST[i]) + # for i in request.POST: + # print(":: ",i, " => ", request.POST[i]) + newtext = request.POST["text"] print("POST") if "Cancel" in request.POST: print("cancel") - return simple_get() + return simple_get(originaltext) if "Save" in request.POST: print("submitted for saving..") - message="submitted for saving.. not implemented yet.." - # INSERT FILE SAVING AND git committing on server - return simple_get() - # mistake, abort + if newtext != originaltext: # Check if content has changed at all + print("text changed.. saving and committing") + try: + write_and_commit([(filepath, newtext, "utf-8")], f"Online edit of {path}") + except WriteAndCommitError as e: + return render(request, "errors/generic.html", {"message": e.message}) + + print("re-reading from file..") + try: + with open(filepath) as f: + rereadtext = f.read() + except: + print("### File reading failure, but it exists.. ### ", filepath) + return render(request, "errors/generic.html", {"message": e.message}) + savepath = "/" + path + print(f"redirect {savepath}") + return redirect(savepath) # Redirect after POST + + else: + # no changes + pass + return simple_get(originaltext) + else: + # mistake not POST or GET message="Something went wrong" - return simple_get() + print(message) + return simple_get(originaltext) @login_required_if_public diff --git a/templates/textfileform.html b/templates/textfileform.html index 2110598..0fbd753 100644 --- a/templates/textfileform.html +++ b/templates/textfileform.html @@ -23,7 +23,7 @@ Full path on server: {{filepath}}
[Edit the text by just typing in the box.]