From 7908257d630648d239d3e5b21f444d442e599385 Mon Sep 17 00:00:00 2001 From: Philip Sargent Date: Wed, 10 Jul 2024 21:07:45 +0200 Subject: [PATCH] fix directory for uploaded cave description photos --- core/views/caves.py | 135 ++++++++++++++++++++++------------ core/views/editor_helpers.py | 40 +++++++++- templates/dataformat/cave.xml | 4 +- urls.py | 8 +- 4 files changed, 132 insertions(+), 55 deletions(-) diff --git a/core/views/caves.py b/core/views/caves.py index 3bdc6f4..d7699d0 100644 --- a/core/views/caves.py +++ b/core/views/caves.py @@ -46,7 +46,65 @@ todo = """ own directory 16xx/NNN/ even if they have no images to put in it. """ +# def cavepagefwd(request, karea=None, subpath=None): + # """archaic, just send to the caves list page + # """ + # return redirect("/caves") + +def get_cave_from_slug(caveslug): + """This is the old way of doing it, usingt eh CaveSlug intermediate object which does + the many:many relationship between slugs and caves - whcih we don't do (and never did in practice) + """ + caves = [] + + print(f"get_cave_from_slug(): {caveslug} ...") + areacode = caveslug[:4] # e.g. 1623 + id = caveslug[5:] # e.g. 161 or 2023-MM-02 + thisarea = Cave.objects.filter(areacode=areacode) + + caves_k = thisarea.filter(kataster_number=id) + if len(caves_k) == 1: + caves.append(caves_k[0]) + print(f"get_cave_from_slug(): {caves_k=} {len(caves_k)=}") + + caves_id = thisarea.filter(unofficial_number=id) + if len(caves_id) == 1: + caves.append(caves_id[0]) + print(f"get_cave_from_slug(): {caves_id=} {len(caves_id)=}") + + if len(caves) > 1: + print(f"get_cave_from_slug(): {caveslug} More than 1 \n{caves}") + if len(caves) <1: + print(f"get_cave_from_slug(): {caveslug} Nowt found..") + cave = caves[0] + print(f"get_cave_from_slug(): {caveslug} FOUND {cave}") + + try: + cave_zero = Cave.objects.get(caveslug__slug=caveslug) + print(f"Getting cave from '{caveslug}'") + if cave_zero != cave: + print(f"get_cave_from_slug(): {caveslug} BAD DISCREPANCY {cave_zero=} {cave=}") + else: + print(f"get_cave_from_slug(): {caveslug} SUCCESS") + + return cave_zero + except: + return None + +def caveslugfwd(request, slug): + """This is ass backwards. It would be better style to have the slug-identified request be the master, and have + other paths redirect to it, rather than what we have here. + Pending a change where we remove cave.url as a field and have an explicit fixed convention instead. + """ + if not slug: + message = f"Failed to find cave from identifier given: {slug}." + return render(request, "errors/generic.html", {"message": message}) + Gcavelookup = GetCaveLookup() + if slug in Gcavelookup: + cave = Gcavelookup[slug] + + return redirect(f"/{cave.url}") def getCaves(cave_id): @@ -93,7 +151,7 @@ def pad5(x): def padnumber(x): - return re.sub("\d+", pad5, x) # SyntaxWarning: invalid escape sequence '\d' + return re.sub(r"\d+", pad5, x) def numericalcmp(x, y): @@ -311,24 +369,7 @@ def rendercave(request, cave, slug, cave_id=""): ) # crashes here with NoReverseMatch if url not set up for 'edit_cave' in urls.py return r -def cavepagefwd(request, karea=None, subpath=None): - """archaic, just send to the caves list page - """ - return redirect("/caves") -def caveslugfwd(request, slug): - """This is ass backwards. It would be better style to have the slug-identified request be the master, and have - other paths redirect to it, rather than what we have here. - Pending a change where we remove cave.url as a field and have an explicit fixed convention instead. - """ - if slug: - Gcavelookup = GetCaveLookup() - if slug in Gcavelookup: - cave = Gcavelookup[slug] - else: - message = f"Failed to find cave from identifier given: {slug}." - return render(request, "errors/generic.html", {"message": message}) - return redirect(f"/{cave.url}") def cavepage(request, karea=None, subpath=None): """Displays a cave description page @@ -368,9 +409,7 @@ def cavepage(request, karea=None, subpath=None): subparts = parts[0].split(".") caveid = subparts[0] slug = f"{karea}-{caveid}" - caves = Cave.objects.filter(caveslug__slug=slug) - if len(caves) == 1: - cave = caves[0] + if cave:= get_cave_from_slug(slug): # walrus operator return redirect(f"/{cave.url}") else: return redirect(f"/caves") @@ -410,31 +449,31 @@ def edit_cave(request, path="", slug=None): """ print(f"edit_cave(): {path=} {slug=}") message = "" - if slug is not None: - print(f"{slug=}") - try: - cave = Cave.objects.get(caveslug__slug=slug) - except: - return render(request, "errors/badslug.html", {"badslug": f"{slug} - from edit_cave()"}) + if slug is None: + cave = Cave() # create a New Cave else: - cave = Cave() + print(f"{slug=}") + if not (cave:= get_cave_from_slug(slug)): # walrus operator + return render(request, "errors/badslug.html", {"badslug": f"for cave {caveslug} - from edit_cave()"}) + if request.POST: form = CaveForm(request.POST, instance=cave) #ceFormSet = CaveAndEntranceFormSet(request.POST) if form.is_valid(): # and ceFormSet.is_valid(): - # print(f'! POST is valid. {cave}') + print(f'edit_cave(): POST is valid. Editing {cave}') cave = form.save(commit=False) - print(cave) + # print(cave) if not cave.filename: cave.filename = cave.areacode + "-" + cave.number() + ".html" if not cave.url: cave.url = cave.areacode + "/" + cave.number() cave.save() - form.save_m2m() + form.save_m2m() # this does the many-to-many relationship saving between caves and entrances if slug is None: # it is not visible on the form so it always will be None slug = f"{cave.areacode}-{cave.number()}" cs = CaveSlug(cave=cave, slug=slug, primary=True) + print(f"edit_cave(): New CaveSlug saved {slug}") cs.save() #ceinsts = ceFormSet.save(commit=False) #for ceinst in ceinsts: @@ -450,6 +489,8 @@ def edit_cave(request, path="", slug=None): except subprocess.SubprocessError: message = f"CANNOT git on server for this file {cave.filename}. Edits may not be committed.\nAsk a nerd to fix this." return render(request, "errors/generic.html", {"message": message}) + except: + raise if cave.entrances().count() > 0: return HttpResponseRedirect("/" + cave.url) else: @@ -458,8 +499,8 @@ def edit_cave(request, path="", slug=None): else: if slug is not None: # re-read cave data from file. - #print(f"edit_cave(): {cave=} {cave.filename=}") - #print(f"edit_cave(): {cave.slug()=}") + print(f"edit_cave(): {cave=} {cave.filename=}") + print(f"edit_cave(): {cave.slug()=}") if cave.filename: try: read_cave(cave.filename, cave=cave) @@ -472,7 +513,8 @@ def edit_cave(request, path="", slug=None): else: form = CaveForm() #ceFormSet = CaveAndEntranceFormSet(queryset=CaveAndEntrance.objects.none()) - + + print(f"edit_cave(): returning render()") return render( request, "editcave.html", @@ -486,6 +528,7 @@ def edit_cave(request, path="", slug=None): ) + @login_required_if_public def edit_entrance(request, path="", caveslug=None, entslug=None): """This is the form that edits the entrance data for a single entrance and writes out @@ -497,6 +540,9 @@ def edit_entrance(request, path="", caveslug=None, entslug=None): It does save the data into into the database directly, not by parsing the file. + 'path' comes from the urls.py regex but is usually empty (!) + So we make the proper path for storing the images ourselves. + GET RID of all this entranceletter stuff. Far too overcomplexified. We don't need it. Just the entrance slug is fine, then check uniqueness. """ @@ -526,9 +572,7 @@ def edit_entrance(request, path="", caveslug=None, entslug=None): nextletter = chr(ord(letter)+1) return check_new_slugname_ok(slug, nextletter) - try: - cave = Cave.objects.get(caveslug__slug=caveslug) - except: + if not (cave:= get_cave_from_slug(caveslug)): # walrus operator return render(request, "errors/badslug.html", {"badslug": f"for cave {caveslug} - from edit_entrance()"}) if entslug: @@ -541,7 +585,7 @@ def edit_entrance(request, path="", caveslug=None, entslug=None): entrance = None if entslug: - print(f"{caveslug=} {entslug=} {path=} number of ents:{cave.entrances().count()}") + print(f"Edit Entrance {caveslug=} {entslug=} {path=} number of ents:{cave.entrances().count()}") caveAndEntrance = CaveAndEntrance.objects.get(entrance=entrance, cave=cave) entlettereditable = False else: @@ -556,6 +600,11 @@ def edit_entrance(request, path="", caveslug=None, entslug=None): print(f"{entlettereditable=}") # if the entletter is not editable, then the entletterform does not appear and so is always invalid. + + print(f"{caveslug=}") + print(f"{cave=}") + imgpath = Path(path) / cave.areacode / cave.number() + print(f"Edit Entrance {imgpath=}") if request.POST: print(f"POST Online edit of entrance: '{entrance}' where {cave=}") @@ -683,7 +732,7 @@ def edit_entrance(request, path="", caveslug=None, entslug=None): "entletter": entletter, "entletterform": entletterform, # is unset if not being used "entlettereditable": entlettereditable, - "path": path + "/", # used for saving images if attached + "path": str(imgpath) + "/", # used for saving images if attached }, ) @@ -719,9 +768,7 @@ def caveslist(request): {"caves": caves, "year": current_expo()}, ) def get_entrances(request, caveslug): - try: - cave = Cave.objects.get(caveslug__slug=caveslug) - except: + if not (cave:= get_cave_from_slug(caveslug)): # walrus operator return render(request, "errors/badslug.html", {"badslug": f"{caveslug} - from get_entrances()"}) return render( request, "options.html", {"year": current_expo(), "items": [(e.entrance.slug(), e.entrance.slug()) for e in cave.entrances()]} @@ -733,9 +780,7 @@ def caveQMs(request, slug, open=False): relies on the template to find all the QMs for the cave specified in the slug, e.g. '1623-161' Now working in July 2022 """ - try: - cave = Cave.objects.get(caveslug__slug=slug) - except: + if not (cave:= get_cave_from_slug(caveslug)): # walrus operator return render(request, "errors/badslug.html", {"badslug": f"{slug} - from caveQMs()"}) if cave.non_public and settings.PUBLIC_SITE and not request.user.is_authenticated: diff --git a/core/views/editor_helpers.py b/core/views/editor_helpers.py index 0c3f5b6..9db71c2 100644 --- a/core/views/editor_helpers.py +++ b/core/views/editor_helpers.py @@ -26,6 +26,11 @@ THUMBNAIL_HEIGHT = 200 It uses a lot of opinionated Django default magic, none of which I am familiar with. Philip + +The function image_selector() is only called from a template file doing a popup input form, +but it stores files directly in expoweb/i /l /t instead of in +/expoweb/1623/2018-MS-01/i , /l, /t +so ALL new caves have photos in the smae place, which makes the default upload EXTREMELY CONFUSING """ def get_dir(path): @@ -38,7 +43,10 @@ def get_dir(path): def image_selector(request, path): - """Returns available images""" + """Returns available images + called from + templates/html_editor_scripts_css.html: $('#image_popup_content').load("{% url 'image_selector' path %}" + path, function() { + """ directory = get_dir(path) print(f" - image selector {directory=} {path=}") thumbnailspath = Path(settings.EXPOWEB) / directory / "t" @@ -89,15 +97,23 @@ def reorient_image(img, exif_dict): def new_image_form(request, path): """Manages a form to upload new images""" directory = get_dir(path) + print(f"new_image_form(): {directory=} {path=}") if request.method == "POST": + print(f"new_image_form(): POST ") form = NewWebImageForm(request.POST, request.FILES, directory=directory) if form.is_valid(): + print(f"new_image_form(): form is valid ") + print(f"new_image_form(): files: {request.FILES["file_"]}") f = request.FILES["file_"] binary_data = io.BytesIO() for chunk in f.chunks(): binary_data.write(chunk) + print(f"new_image_form(): binary chnks written") i = Image.open(binary_data) + print(f"new_image_form(): {i=}") if "exif" in i.info: + print(f"new_image_form(): exif: {i=}") + exif_dict = piexif.load(i.info["exif"]) i = reorient_image(i, exif_dict) exif_dict['Exif'][41729] = b'1' @@ -109,13 +125,21 @@ def new_image_form(request, path): else: exif = None + print(f"new_image_form(): No exif problems") width, height = i.size + print(f"new_image_form(): {i.size=}") if width > MAX_IMAGE_WIDTH or height > MAX_IMAGE_HEIGHT: + print(f"new_image_form(): rescaling ") scale = max(width / MAX_IMAGE_WIDTH, height / MAX_IMAGE_HEIGHT) - i = i.resize((int(width / scale), int(height / scale)), Image.ANTIALIAS) + print(f"new_image_form(): rescaling {scale=}") + try: + i = i.resize((int(width / scale), int(height / scale)), Image.LANCZOS) + except Exception as e: + print(f"new_image_form(): rescaling exception: {e} ") + + print(f"new_image_form(): rescaled ") tscale = max(width / THUMBNAIL_WIDTH, height / THUMBNAIL_HEIGHT) - thumbnail = i.resize((int(width / tscale), int(height / tscale)), Image.ANTIALIAS) - + thumbnail = i.resize((int(width / tscale), int(height / tscale)), Image.LANCZOS) ib = io.BytesIO() i = i.convert('RGB') i.save(ib, format="jpeg", quality = 75) @@ -123,6 +147,7 @@ def new_image_form(request, path): thumbnail = thumbnail.convert('RGB') thumbnail.save(tb, format="jpeg", quality = 70) image_rel_path, thumb_rel_path, desc_rel_path = form.get_rel_paths() + print(f"new_image_form(): \n {image_rel_path=}\n {thumb_rel_path=}\n {desc_rel_path=}") image_page_template = loader.get_template("image_page_template.html") # this is the .html file in the /l/ folder image_page = image_page_template.render( { @@ -149,14 +174,21 @@ def new_image_form(request, path): f"{change_message} - online adding of an image", ) except WriteAndCommitError as e: + print(f"new_image_form(): WriteAndCommitError: {e.message}") return JsonResponse({"error": e.message}) + except Exception as e: + print(f"new_image_form(): EXCEPTION: {e.message}") + return JsonResponse({"error": e.message}) + linked_image_template = loader.get_template("linked_image_template.html") html_snippet = linked_image_template.render( {"thumbnail_url": f"/{thumb_rel_path}", "page_url": f"/{desc_rel_path}"}, request ) return JsonResponse({"html": html_snippet}) else: + print(f"new_image_form(): not POST ") form = NewWebImageForm(directory=directory) + print(f"new_image_form(): POST and not POST ") template = loader.get_template("new_image_form.html") htmlform = template.render({"form": form, "path": path}, request) return JsonResponse({"form": htmlform}) diff --git a/templates/dataformat/cave.xml b/templates/dataformat/cave.xml index c6032f0..e2bf6dd 100644 --- a/templates/dataformat/cave.xml +++ b/templates/dataformat/cave.xml @@ -16,8 +16,8 @@ the form documented at
-{{ cave.non_public }}{% for slug in cave.caveslug_set.all %} - +{{ cave.non_public }} + {{ cave.official_name|default_if_none:""|safe }} {{cave.areacode |safe }} {{ cave.kataster_code|default_if_none:"-"|safe }}