mirror of
https://expo.survex.com/repositories/troggle/.git
synced 2024-11-25 08:41:51 +00:00
hack wallet scan rename job
This commit is contained in:
parent
5f07f234ef
commit
89c1c65340
@ -14,7 +14,9 @@ import re
|
|||||||
|
|
||||||
"""These are all the class-based Forms used by troggle.
|
"""These are all the class-based Forms used by troggle.
|
||||||
There are other, simpler, upload forms in view/uploads.py
|
There are other, simpler, upload forms in view/uploads.py
|
||||||
Some are not used and need renovating or destroying.
|
|
||||||
|
class-based forms are quicker to set up (for Django experts) but
|
||||||
|
are more difficult to maintain by non-Django experts.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
todo = """
|
todo = """
|
||||||
|
@ -3,7 +3,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.files.storage import FileSystemStorage
|
from django.core.files.storage import FileSystemStorage
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render, redirect
|
||||||
|
|
||||||
import settings
|
import settings
|
||||||
from troggle.core.models.survex import DrawingFile
|
from troggle.core.models.survex import DrawingFile
|
||||||
@ -13,6 +13,8 @@ from troggle.core.models.survex import DrawingFile
|
|||||||
from .auth import login_required_if_public
|
from .auth import login_required_if_public
|
||||||
|
|
||||||
"""File upload 'views'
|
"""File upload 'views'
|
||||||
|
Note that there are other file upload forms in views/wallet_edit.py
|
||||||
|
and that core/forms.py contains Django class-based forms for caves and entrances.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
todo = """
|
todo = """
|
||||||
@ -25,12 +27,14 @@ todo = """
|
|||||||
Need to validate it as being a valid GPX file using an XML parser, not a dubious script or hack
|
Need to validate it as being a valid GPX file using an XML parser, not a dubious script or hack
|
||||||
|
|
||||||
- Validate Tunnel & Therion files using an XML parser in dwgupload(). Though Julian says
|
- Validate Tunnel & Therion files using an XML parser in dwgupload(). Though Julian says
|
||||||
tunnel is only mostly correct XML
|
tunnel is only mostly correct XML, and it does fail at least one XML parser.
|
||||||
|
|
||||||
- parse the uploaded drawing file for links to wallets and scan files as done
|
- parse the uploaded drawing file for links to wallets and scan files as done
|
||||||
in parsers/drawings.py
|
in parsers/drawings.py
|
||||||
|
|
||||||
- Enable folder creation in dwguploads or as a separate form
|
- Enable folder creation in dwguploads or as a separate form
|
||||||
|
|
||||||
|
- Enable file rename on expofiles, particularly in /surveyscans/ (aka wallets)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class FilesForm(forms.Form): # not a model-form, just a form-form
|
class FilesForm(forms.Form): # not a model-form, just a form-form
|
||||||
@ -43,6 +47,63 @@ class FilesRenameForm(forms.Form): # not a model-form, just a form-form
|
|||||||
class TextForm(forms.Form): # not a model-form, just a form-form
|
class TextForm(forms.Form): # not a model-form, just a form-form
|
||||||
photographer = forms.CharField(strip=True)
|
photographer = forms.CharField(strip=True)
|
||||||
|
|
||||||
|
class ExpofileRenameForm(forms.Form): # not a model-form, just a form-form
|
||||||
|
renameto = forms.CharField(strip=True, required=False)
|
||||||
|
|
||||||
|
@login_required_if_public
|
||||||
|
def expofilerename(request, filepath):
|
||||||
|
"""Rename any single file in /expofiles/ - eventually.
|
||||||
|
Currently this just does files within wallets i.e. in /surveyscans/
|
||||||
|
and it returns control to the original wallet edit page
|
||||||
|
"""
|
||||||
|
if filepath:
|
||||||
|
actualpath = Path(settings.EXPOFILES) / Path(filepath)
|
||||||
|
else:
|
||||||
|
message = f'\n File to rename not specified "{filepath}"'
|
||||||
|
print(message)
|
||||||
|
return render(request, "errors/generic.html", {"message": message})
|
||||||
|
|
||||||
|
if not actualpath.is_file():
|
||||||
|
message = f'\n File not found when attempting rename "{filepath}"'
|
||||||
|
print(message)
|
||||||
|
return render(request, "errors/generic.html", {"message": message})
|
||||||
|
else:
|
||||||
|
filename = Path(filepath).name
|
||||||
|
folder = Path(actualpath).parent
|
||||||
|
|
||||||
|
if not actualpath.is_relative_to(Path(settings.SCANS_ROOT)):
|
||||||
|
message = f'\n Can only do rename within wallets (expofiles/surveyscans/) currently, sorry. "{actualpath}" '
|
||||||
|
print(message)
|
||||||
|
return render(request, "errors/generic.html", {"message": message})
|
||||||
|
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
form = ExpofileRenameForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
renameto = request.POST["renameto"]
|
||||||
|
|
||||||
|
if (folder / renameto).is_file():
|
||||||
|
message = f'\n Cannot rename to an existing file. "{filename}" -> "{(folder / renameto)}"'
|
||||||
|
print(message)
|
||||||
|
return render(request, "errors/generic.html", {"message": message})
|
||||||
|
else:
|
||||||
|
actualpath.rename((folder / renameto))
|
||||||
|
message = f'\n RENAMED "{filename}" -> "{(folder / renameto)}"'
|
||||||
|
print(message)
|
||||||
|
return redirect('/survey_scans/2023%252314/')
|
||||||
|
|
||||||
|
else:
|
||||||
|
form = ExpofileRenameForm()
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"renameform.html",
|
||||||
|
{
|
||||||
|
"form": form,
|
||||||
|
"filepath": filepath,
|
||||||
|
"filename": filename,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
@login_required_if_public
|
@login_required_if_public
|
||||||
def photoupload(request, folder=None):
|
def photoupload(request, folder=None):
|
||||||
"""Upload photo image files into /expofiles/photos/<year>/<photographer>/
|
"""Upload photo image files into /expofiles/photos/<year>/<photographer>/
|
||||||
@ -55,6 +116,8 @@ def photoupload(request, folder=None):
|
|||||||
be renamed to something useful before starting the upload.
|
be renamed to something useful before starting the upload.
|
||||||
Unfortunately this only works when uploading one file at at time ,
|
Unfortunately this only works when uploading one file at at time ,
|
||||||
inevitable once you think about it.
|
inevitable once you think about it.
|
||||||
|
|
||||||
|
Pending generic file renaming capability more generally.
|
||||||
"""
|
"""
|
||||||
year = settings.PHOTOS_YEAR
|
year = settings.PHOTOS_YEAR
|
||||||
filesaved = False
|
filesaved = False
|
||||||
@ -186,7 +249,8 @@ def dwgupload(request, folder=None, gitdisable="no"):
|
|||||||
This does NOT use a Django model linked to a Django form. Just a simple Django form.
|
This does NOT use a Django model linked to a Django form. Just a simple Django form.
|
||||||
You will find the Django documentation on forms very confusing, This is simpler.
|
You will find the Django documentation on forms very confusing, This is simpler.
|
||||||
|
|
||||||
We could validate the uploaded files as being a valid files using an XML parser, not a dubious script or hack
|
We could validate the uploaded files as being a valid files using an XML parser, not a dubious script or hack,
|
||||||
|
but this won't work on Tunnel files as Tunnel does not produce exactly valid xml
|
||||||
|
|
||||||
We use get_or_create instead of simply creating a new object in case someone uploads the same file
|
We use get_or_create instead of simply creating a new object in case someone uploads the same file
|
||||||
several times in one session, and expects them to be overwritten in the database. Although
|
several times in one session, and expects them to be overwritten in the database. Although
|
||||||
|
@ -272,11 +272,15 @@ def walletedit(request, path=None):
|
|||||||
|
|
||||||
This does NOT use a Django model linked to a Django form. Just a simple Django form.
|
This does NOT use a Django model linked to a Django form. Just a simple Django form.
|
||||||
You will find the Django documentation on forms very confusing,
|
You will find the Django documentation on forms very confusing,
|
||||||
as it covers many very differnet things we do not need. This is simpler.
|
as it covers many very different things we do not need. This is simpler.
|
||||||
|
(See also view/uploads.py for other simpler forms, as opposed to core/forms.py
|
||||||
|
which contains a couple of Django class-based forms.)
|
||||||
|
|
||||||
This subsumes much of the code which was in the pre-2022 non-troggle wallets.py script
|
This subsumes much of the code which was in the pre-2022 non-troggle wallets.py script
|
||||||
and so this function is very long indeed and needs refactoring.
|
and so this function is very long indeed and needs refactoring.
|
||||||
|
|
||||||
|
Much of the logic used here lives in the Class functions for Wallet.
|
||||||
|
|
||||||
REWRITE bits using the ticklist, dateify, caveify, populate etc utility functions in core.view.scans.py
|
REWRITE bits using the ticklist, dateify, caveify, populate etc utility functions in core.view.scans.py
|
||||||
"""
|
"""
|
||||||
git = settings.GIT
|
git = settings.GIT
|
||||||
|
@ -1781,7 +1781,7 @@ class LoadingSurvex:
|
|||||||
|
|
||||||
# this is a python generator idiom.
|
# this is a python generator idiom.
|
||||||
# see https://realpython.com/introduction-to-python-generators/
|
# see https://realpython.com/introduction-to-python-generators/
|
||||||
# this is the first use of generators in troggle (Oct.2022) and save 21 MB of memory
|
# this is the first use of generators in troggle (Oct.2022) and saves 21 MB of memory
|
||||||
with open(collatefilename, "r") as fcollate:
|
with open(collatefilename, "r") as fcollate:
|
||||||
for svxline in fcollate:
|
for svxline in fcollate:
|
||||||
self.lineno += 1
|
self.lineno += 1
|
||||||
@ -1789,22 +1789,22 @@ class LoadingSurvex:
|
|||||||
if comment:
|
if comment:
|
||||||
# this catches the ;|*include NEWFILE and ;|*edulcni ENDOFFILE lines too
|
# this catches the ;|*include NEWFILE and ;|*edulcni ENDOFFILE lines too
|
||||||
self.LoadSurvexComment(survexblock, comment)
|
self.LoadSurvexComment(survexblock, comment)
|
||||||
|
else:
|
||||||
|
# detect a merge failure inserted by version control
|
||||||
|
mfail = self.rx_badmerge.match(sline)
|
||||||
|
if mfail:
|
||||||
|
message = f"\n ! - ERROR version control merge failure\n - '{sline}'\n"
|
||||||
|
message = (
|
||||||
|
message + f" - line {self.lineno} in {blkid} in {survexblock}\n - NERD++ needed to fix it"
|
||||||
|
)
|
||||||
|
print(message)
|
||||||
|
print(message, file=sys.stderr)
|
||||||
|
stash_data_issue(parser="survex", message=message)
|
||||||
|
continue # skip this line
|
||||||
|
|
||||||
if not sline:
|
if not sline:
|
||||||
continue # skip blank lines
|
continue # skip blank lines
|
||||||
|
|
||||||
# detect a merge failure inserted by version control
|
|
||||||
mfail = self.rx_badmerge.match(sline)
|
|
||||||
if mfail:
|
|
||||||
message = f"\n ! - ERROR version control merge failure\n - '{sline}'\n"
|
|
||||||
message = (
|
|
||||||
message + f" - line {self.lineno} in {blkid} in {survexblock}\n - NERD++ needed to fix it"
|
|
||||||
)
|
|
||||||
print(message)
|
|
||||||
print(message, file=sys.stderr)
|
|
||||||
stash_data_issue(parser="survex", message=message)
|
|
||||||
continue # skip this line
|
|
||||||
|
|
||||||
# detect a star command
|
# detect a star command
|
||||||
star = self.rx_star.match(sline)
|
star = self.rx_star.match(sline)
|
||||||
if star:
|
if star:
|
||||||
@ -1829,7 +1829,7 @@ class LoadingSurvex:
|
|||||||
mfail = self.rx_badmerge.match(svxline)
|
mfail = self.rx_badmerge.match(svxline)
|
||||||
if mfail:
|
if mfail:
|
||||||
message = f"\n!! - ERROR version control merge failure\n - '{svxline}'\n"
|
message = f"\n!! - ERROR version control merge failure\n - '{svxline}'\n"
|
||||||
message = message + f" - in '{path}' at line {thissvxline}\n"
|
message = message + f" - in '{path}' at line {self.lineno}\n"
|
||||||
message = (
|
message = (
|
||||||
message + f" - line {self.lineno} {survexblock}\n - Parsing aborted. NERD++ needed to fix it"
|
message + f" - line {self.lineno} {survexblock}\n - Parsing aborted. NERD++ needed to fix it"
|
||||||
)
|
)
|
||||||
@ -1963,7 +1963,7 @@ class LoadingSurvex:
|
|||||||
stash_data_issue(parser="survex", message=message, url=None, sb=(path))
|
stash_data_issue(parser="survex", message=message, url=None, sb=(path))
|
||||||
return # skip this survex file and all things *included in it
|
return # skip this survex file and all things *included in it
|
||||||
except:
|
except:
|
||||||
message = f" ! ERROR *include file '{path}' in '{survexblock}' has unexpected error on opening file. OMITTED!"
|
message = f" ! ERROR *include file '{path}' in '{survexblock}' has unexpected error on opening or reading file. OMITTED!"
|
||||||
print(message)
|
print(message)
|
||||||
print(message, file=sys.stderr)
|
print(message, file=sys.stderr)
|
||||||
stash_data_issue(parser="survex", message=message, url=None, sb=(path))
|
stash_data_issue(parser="survex", message=message, url=None, sb=(path))
|
||||||
|
38
templates/renameform.html
Normal file
38
templates/renameform.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}File rename form{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h2>Rename "{{filename}}"</h2>
|
||||||
|
<figure class=onleft>
|
||||||
|
<img src="/expofiles/{{filepath|urlencode}}" width=150px>
|
||||||
|
</figure>
|
||||||
|
<div style = "max-width:95%; margin-left:8%; text-align: left; " >
|
||||||
|
<form method ='post' >
|
||||||
|
{% csrf_token %}
|
||||||
|
<br>
|
||||||
|
<input class="fancybutton2" style="padding: 0.5em 25px; margin-left: 110px "
|
||||||
|
label = "Rename to" name = "renameto" id="renameto"
|
||||||
|
pattern="[A-Za-z][A-Za-z0-9_-\.]*"/>
|
||||||
|
<label
|
||||||
|
style="padding: 0.5em 25px; margin-left: 110px"
|
||||||
|
for="renameto">the new name for this file<br></label>
|
||||||
|
<br><br><br>
|
||||||
|
|
||||||
|
<button class="fancybutton2" style="padding: 0.5em 25px; margin-left: 110px" type = "submit" value = "Upload" >
|
||||||
|
Rename it
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<br /><br /><br />
|
||||||
|
Full urlencoded path for this file <a href="/expofiles/{{filepath|urlencode}}"><em>{{filepath|urlencode}}</em></a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -72,7 +72,7 @@
|
|||||||
{% if not create %}
|
{% if not create %}
|
||||||
<p>
|
<p>
|
||||||
{% for f in files %}
|
{% for f in files %}
|
||||||
<a href="/expofiles/surveyscans/{{year}}/{{wallet|urlencode}}/{{f}}">{{ f}}</a><br />
|
<a href="/expofilerename/surveyscans/{{year}}/{{wallet|urlencode}}/{{f}}">»</a> <a href="/expofiles/surveyscans/{{year}}/{{wallet|urlencode}}/{{f}}">{{ f}}</a><br />
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<p><No files in this wallet. >
|
<p><No files in this wallet. >
|
||||||
|
|
||||||
|
5
urls.py
5
urls.py
@ -24,7 +24,7 @@ from troggle.core.views.other import (controlpanel, exportlogbook, frontpage,
|
|||||||
from troggle.core.views.prospect import prospecting
|
from troggle.core.views.prospect import prospecting
|
||||||
from troggle.core.views.scans import (allscans, cavewallets, scansingle,
|
from troggle.core.views.scans import (allscans, cavewallets, scansingle,
|
||||||
walletslistperson, walletslistyear)
|
walletslistperson, walletslistyear)
|
||||||
from troggle.core.views.uploads import dwgupload, photoupload
|
from troggle.core.views.uploads import dwgupload, photoupload, expofilerename
|
||||||
from troggle.core.views.wallets_edit import walletedit
|
from troggle.core.views.wallets_edit import walletedit
|
||||||
"""This sets the actualurlpatterns[] and urlpatterns[] lists which django uses
|
"""This sets the actualurlpatterns[] and urlpatterns[] lists which django uses
|
||||||
to resolve urls - in both directions as these are declarative.
|
to resolve urls - in both directions as these are declarative.
|
||||||
@ -110,6 +110,9 @@ trogglepatterns = [
|
|||||||
path('dwguploadnogit/', dwgupload, {'gitdisable': 'yes'}, name='dwguploadnogit'), # used in testing
|
path('dwguploadnogit/', dwgupload, {'gitdisable': 'yes'}, name='dwguploadnogit'), # used in testing
|
||||||
path('dwguploadnogit/<path:folder>', dwgupload, {'gitdisable': 'yes'}, name='dwguploadnogit'), # used in testing
|
path('dwguploadnogit/<path:folder>', dwgupload, {'gitdisable': 'yes'}, name='dwguploadnogit'), # used in testing
|
||||||
|
|
||||||
|
# Renaming an uploaded file
|
||||||
|
path('expofilerename/<path:filepath>', expofilerename, name='expofilerename'),
|
||||||
|
|
||||||
# setting LOGIN_URL = '/accounts/login/' is default.
|
# setting LOGIN_URL = '/accounts/login/' is default.
|
||||||
# NB setting url pattern name to 'login' instea dof 'expologin' with override Django, see https://docs.djangoproject.com/en/dev/topics/http/urls/#naming-url-patterns
|
# NB setting url pattern name to 'login' instea dof 'expologin' with override Django, see https://docs.djangoproject.com/en/dev/topics/http/urls/#naming-url-patterns
|
||||||
path('accounts/logout/', expologout, name='expologout'), # same as in django.contrib.auth.urls
|
path('accounts/logout/', expologout, name='expologout'), # same as in django.contrib.auth.urls
|
||||||
|
Loading…
Reference in New Issue
Block a user