diff options
Diffstat (limited to 'core/views/uploads.py')
-rw-r--r-- | core/views/uploads.py | 213 |
1 files changed, 177 insertions, 36 deletions
diff --git a/core/views/uploads.py b/core/views/uploads.py index b92ca4e..58efb37 100644 --- a/core/views/uploads.py +++ b/core/views/uploads.py @@ -1,5 +1,6 @@ import subprocess import hashlib +import string from datetime import datetime from pathlib import Path @@ -8,8 +9,11 @@ from django.core.files.storage import FileSystemStorage from django.shortcuts import render, redirect import settings -from troggle.core.models.logbooks import LogbookEntry, PersonLogEntry +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.parsers.people import GetPersonExpeditionNameLookup, known_foreigner # from databaseReset import reinit_db # don't do this. databaseRest runs code *at import time* @@ -21,11 +25,6 @@ and that core/forms.py contains Django class-based forms for caves and entrances """ todo = """ -- munge the URL of images in the logbook entry so that they work from both the " /logbookedit/" page, - the logbook /years/2023/ page and the logbook fragment page /logbookentry/<date>/ - Note that this munging has already been done when the entry is imported into the database, so - when doing an online edit it has already been fixed. - - Ideally we should validate uploaded file as being a valid file type, not a dubious script or hack Validate image files using a magic recogniser in walletedit() https://pypi.org/project/reportlab/ or @@ -48,6 +47,89 @@ todo = """ """ sha = hashlib.new('sha256') +def unique_slug(text, n): + """This gives each logbook entry a unique id based on the date+content, so the order of entries on a particular day + does not matter. This is a change (August 2023) from previous process. + + 2 hex digits would seem adequate for each expo day, but we might get a collision. + The hash is based on the content after substitution of <p> so should be stable. Which means these ids + can be used elsewhere in the troggle system as permanent slugs. + + When SAVING an edited entry (as opposed to a new one) we will have a different hash so we will have to + delete the original database object + """ + sha.update(text.encode('utf-8')) + return sha.hexdigest()[0:n] + +def create_new_lbe_slug(date): + onthisdate = LogbookEntry.objects.filter(date=date) + n = len(onthisdate) + # print(f" Already entries on this date: {n}\n {onthisdate}") + + alphabet = list(string.ascii_lowercase) + tid = f"{date}{alphabet[n]}" + print(tid) + return tid + +def store_edited_entry_into_database(date, place, title, text, others, author, tu, slug): + """saves a single logbook entry and related personlogentry items + + Rather similar to similarly named function in parsers/logbooks but circular reference prevents us using it directly, + and they need refactoring anyway. + """ + + year = slug[0:4] + expedition = Expedition.objects.get(year=year) + cave = GetCaveLookup().get(place.lower()) + # print(f"{place} {cave=}") + + if LogbookEntry.objects.filter(slug=slug).exists(): + # oops. + message = " ! - DUPLICATE SLUG for logbook entry " + tripdate + " - " + slug + DataIssue.objects.create(parser="logbooks", message=message) + slug = slug + "_" + unique_slug(text,2) + + nonLookupAttribs = { + "place": place, + "text": text, + "expedition": expedition, + "time_underground": tu, + "cave_slug": str(cave), + } + lookupAttribs = {"slug": slug, "date": date, "title": title} + + lbo = LogbookEntry.objects.create(**nonLookupAttribs, **lookupAttribs) + + pt_list = [] + # These entities have to be PersonExpedition objects + team = others.split(",") + team.append(author) + for name in team: + name = name.strip() + if name[0] != "*": # a name prefix of "*" is special, just a string. + try: + personyear = GetPersonExpeditionNameLookup(expedition).get(name.lower()) + if not personyear: + if known_foreigner(name): + message = f" ! - Known foreigner: '{name}' in entry {slug=}" + print(message) + else: + message = f" ! - No name match for: '{name}' in entry {slug=}" + print(message) + DataIssue.objects.create(parser="logbooks", message=message) + else: + lookupAttribs = {"personexpedition": personyear, "nickname_used": name, "logbook_entry": lbo} # lbo is primary key + nonLookupAttribs = {"time_underground": tu, "is_logbook_entry_author": (name==author)} + pt_list.append(PersonLogEntry(**nonLookupAttribs, **lookupAttribs)) + + except: + # This should not happen. We do not raise exceptions in that function + message = f" ! - EXCEPTION: '{name}' in entry {slug=}" + print(message) + DataIssue.objects.create(parser="logbooks", message=message) + raise + + PersonLogEntry.objects.bulk_create(pt_list) class FilesForm(forms.Form): # not a model-form, just a form-form uploadfiles = forms.FileField() @@ -70,9 +152,7 @@ def logbookedit(request, year=None, slug=None): """Edit a logbook entry This is daft: we have the parsed identity of the person and we render it to text as 'nickname_used' (or, previously, 'fullname'), to be re-parsed on re-importing. - And there is no guarantee that this will be the same thing. - - Someone can put in a nickname which is invalid (e.g. 2 Sophies on expo). When is this checked? + And there is no guarantee that this will be the same thing. Oh well. """ def clean_tu(tu): if tu =="": @@ -82,28 +162,14 @@ def logbookedit(request, year=None, slug=None): except: return 0 return tu - - def unique_id(text, n): - """This gives each logbook entry a unique id based on the date+content, so the order of entries on a particular day - does not matter. This is a change (August 2023) from previous process. - Otherwise we could get 2023-07-20a and 2023-07-20b swapped on exporting and re-importing logbooks - because the database does not record precedence. - 2 hex digits would seem adequate for each expo day, but we might get a collision. - The hash is based on the content after substitution of <p> so should be stable. Which means these ids - can be used elsewhere in the troggle system as permanent slugs. - - When SAVING an edited entry (as opposed to a new one) we will have a different hash so we will have to - delete the original database object - """ - sha.update(text.encode('utf-8')) - return sha.hexdigest()[0:n] if not year: if not slug: - year = 2023 + year = 2023 # we need a CURRENT_EXPO() function, we use this in a lot of places.. else: year = slug[0:4] print(year) + author = "" if request.method == "POST": form = LogbookEditForm(request.POST) @@ -112,11 +178,13 @@ def logbookedit(request, year=None, slug=None): print(message) return render(request, "errors/generic.html", {"message": message}) else: + # if there is no slug then this is a completely new lbe and we need to enter it into the db + # otherwise it is an update # validation all to be done yet.. date = request.POST["date"].strip() author = request.POST["author"].strip() # TODO check against personexpedition others = request.POST["others"].strip() # TODO check each against personexpedition - place = request.POST["place"].strip().replace('-','=') # no hyphens ! + place = request.POST["place"].strip().replace(' - ',' = ') # no hyphens ! title = request.POST["title"].strip() entry = request.POST["text"].strip() entry = entry.replace('\r','') # remove HTML-standard CR inserted @@ -135,16 +203,30 @@ def logbookedit(request, year=None, slug=None): dateflag = True date = odate.isoformat() - newslug = f"{date}_{unique_id(entry,2)}" - if slug: - if slug != newslug: - print(f"! Entry id changed! from {slug} to {newslug}") + if not slug: + # Creating a new logbook entry with all the gubbins + slug = create_new_lbe_slug(date) + else: + # OK we could patch the object in place, but if the people on the trip have changed this + # would get very messy. So we delete it and recreate it and all its links + print(f"- Deleting the LogBookEntry {slug}") + LogbookEntry.objects.filter(slug=slug).delete() + + print(f"- Creating the LogBookEntry {slug}") + store_edited_entry_into_database(date, place, title, entry, others, author, tu, slug) + print(f"- Rewriting the entire {year} logbook to disc ") + filename= "logbook.html" + try: + writelogbook(year, filename) # uses a template, not the code fragment below + except: + message = f'! - Logbook saving failed - \n!! Permissions failure ?! on attempting to save file "logbook.html"' + print(message) + return render(request, "errors/generic.html", {"message": message}) - # OK this could be done by rendering a template, but for such a small bit of HTML, it is easier to have - # it all in one place: here + # Code fragment illustration - not actually what gets saved to database output = f''' -<div class="tripdate" id="{newslug}">{date}</div> +<div class="tripdate" id="{slug}">{date}</div> <div class="trippeople"><u>{author}</u>, {others}</div> <div class="triptitle">{place} - {title}</div> @@ -154,6 +236,65 @@ def logbookedit(request, year=None, slug=None): <hr /> ''' + # Successful POST + # So save to database and then write out whole new logbook.html file + + #TO DO author and team validation, and check that 'place' is not deleted and that *bloke not forgotten + git = settings.GIT + dirpath = Path(settings.EXPOWEB) / "years" / year + lbe_add = subprocess.run( + [git, "add", filename], cwd=dirpath, capture_output=True, text=True + ) + msgdata = ( + lbe_add.stderr + + "\n" + + lbe_add.stdout + + "\nreturn code: " + + str(lbe_add.returncode) + ) + message = f'! - FORM Logbook Edit {slug} - Success: git ADD on server for this file {filename}.' + msgdata + print(message) + if lbe_add.returncode != 0: + msgdata = ( + "Ask a nerd to fix this.\n\n" + + lbe_add.stderr + + "\n\n" + + lbe_add.stdout + + "\n\nreturn code: " + + str(lbe_add.returncode) + ) + message = ( + f"! - FORM Logbook Edit - CANNOT git ADD on server for this file {filename}. {slug} edits saved but not added to git.\n" + + msgdata + ) + print(message) + return render(request, "errors/generic.html", {"message": message}) + + lbe_commit = subprocess.run( + [git, "commit", "-m", f"Logbook edited {slug}"], + cwd=dirpath, + capture_output=True, + text=True, + ) + message = f'! - FORM Logbook Edit - {filename}. {slug} edits saved, added to git, and COMMITTED.\n' + msgdata + print(message) + #This produces return code = 1 if it commits OK + if lbe_commit.returncode != 0: + msgdata = ( + "Ask a nerd to fix this.\n\n" + + lbe_commit.stderr + + "\n" + + lbe_commit.stdout + + "\nreturn code: " + + str(lbe_commit.returncode) + ) + message = ( + f"! - FORM Logbook Edit -Error code with git on server for {filename}. {slug} edits saved, added to git, but NOT committed.\n" + + msgdata + ) + print(message) + return render(request, "errors/generic.html", {"message": message}) + return render( request, "logbookform.html", @@ -212,10 +353,10 @@ def logbookedit(request, year=None, slug=None): "tu": tu, "entry": text, "textrows": rows, - #"output": output, - }, + }, ) - else: + else: # no slug + # NEW logbook entry return render( request, "logbookform.html", |