mirror of
https://expo.survex.com/repositories/troggle/.git
synced 2024-12-18 06:22:18 +00:00
991 lines
40 KiB
Python
991 lines
40 KiB
Python
import datetime
|
|
import json
|
|
import os
|
|
import re
|
|
import socket
|
|
import subprocess
|
|
import urllib
|
|
from pathlib import Path
|
|
|
|
from django import forms
|
|
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
|
|
from django.core.files.storage import FileSystemStorage
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import render
|
|
|
|
import settings
|
|
from troggle.core.models.caves import Cave
|
|
from troggle.core.models.logbooks import LogbookEntry # , PersonLogEntry
|
|
from troggle.core.models.survex import SurvexBlock, SurvexFile, SurvexPersonRole
|
|
from troggle.core.models.troggle import DataIssue, Expedition
|
|
from troggle.core.models.wallets import YEAR_RANGE, Wallet, make_valid_date
|
|
from troggle.core.utils import current_expo, sanitize_name
|
|
from troggle.core.views.auth import login_required_if_public
|
|
from troggle.core.views.caves import get_cave_leniently, getCave
|
|
from troggle.core.views.scans import caveifywallet, oldwallet
|
|
from troggle.core.views.uploads import FilesForm
|
|
from troggle.parsers.scans import contentsjson
|
|
|
|
"""Main wallet editing form, which includes scan file upload into the wallet
|
|
"""
|
|
|
|
todo = """
|
|
- Nasty bug in navigating to 'previous wallet' when we have a 2-year gap in expos
|
|
The xxxx#00 wallet is not getting edited correctly. Something is off by one somewhere..
|
|
|
|
- Register uploaded filenames in the Django db without needing to wait for a reset & bulk file import
|
|
|
|
- Refactor walletedit() as it contains all the wallets 'complaints' code from the pre-2022
|
|
script 'wallets.py'
|
|
|
|
- We should validate uploaded file as being a valid image file, not a dubious script or hack?
|
|
"""
|
|
WALLET_BLANK_JSON = {
|
|
"cave": "",
|
|
"date": "",
|
|
"free text": "",
|
|
# "description url": "1623/NNN",
|
|
"description written": False,
|
|
"electronic survey": False,
|
|
"elev drawn": False,
|
|
"elev not required": False,
|
|
"name": "",
|
|
"people": ["Unknown"],
|
|
"plan drawn": False,
|
|
"plan not required": False,
|
|
"notes not required": False,
|
|
"qms written": False,
|
|
"survex file": [],
|
|
"survex not required": False,
|
|
"website updated": False,
|
|
}
|
|
|
|
class WalletGotoForm(forms.Form): # not a model-form, just a form-form
|
|
walletgoto = forms.CharField(strip=True, required=False)
|
|
|
|
|
|
class WalletForm(forms.Form): # not a model-form, just a form-form
|
|
descriptionw = forms.CharField(strip=True, required=False)
|
|
people = forms.CharField(strip=True, required=False)
|
|
survexnr = forms.CharField(strip=True, required=False)
|
|
qmsw = forms.CharField(strip=True, required=False)
|
|
date = forms.CharField(strip=True, required=True) # the only required field
|
|
websiteupt = forms.CharField(strip=True, required=False)
|
|
elevnr = forms.CharField(strip=True, required=False)
|
|
cave = forms.CharField(strip=True, required=False)
|
|
psg = forms.CharField(strip=True, required=False)
|
|
freetext = forms.CharField(strip=True, required=False)
|
|
plannr = forms.CharField(strip=True, required=False)
|
|
notesnr = forms.CharField(strip=True, required=False)
|
|
electronic = forms.CharField(strip=True, required=False)
|
|
pland = forms.CharField(strip=True, required=False)
|
|
elevd = forms.CharField(strip=True, required=False)
|
|
# url = forms.CharField(strip=True, required=False)
|
|
survex = forms.CharField(strip=True, required=False)
|
|
|
|
|
|
xlate = {
|
|
# "url": "description url",
|
|
"descriptionw": "description written",
|
|
"people": "people",
|
|
"date": "date",
|
|
"cave": "cave",
|
|
"plannr": "plan not required",
|
|
"notesnr": "notes not required",
|
|
"survexnr": "survex not required",
|
|
"qmsw": "qms written",
|
|
"elevnr": "elev not required",
|
|
"websiteupt": "website updated",
|
|
"electronic": "electronic survey",
|
|
"pland": "plan drawn",
|
|
"elevd": "elev drawn",
|
|
"psg": "name", # a name for this wallet
|
|
"freetext": "free text",
|
|
"survex": "survex file",
|
|
}
|
|
|
|
|
|
|
|
def get_complaints(complaints, waldata, svxfiles, files, wallet, wurl):
|
|
"""Taken from old script wallets.py and edited to make more comprehensible
|
|
Loads the survex files names and processes all complaints
|
|
|
|
"""
|
|
# If skipping through the wallets on the upload form, the wallet may not yet exist
|
|
try:
|
|
w = Wallet.objects.get(walletname=wallet)
|
|
except ObjectDoesNotExist:
|
|
return None, None
|
|
|
|
# Date
|
|
if not waldata["date"]:
|
|
complaints.append(
|
|
"A date is mandatory. No data can be updated or edited unless you specify a date. Look in the survex file if there is one."
|
|
)
|
|
|
|
# People
|
|
if (
|
|
not waldata["people"]
|
|
or waldata["people"] == ["NOBODY"]
|
|
or waldata["people"] == ["Unknown"]
|
|
or waldata["people"] == [""]
|
|
):
|
|
complaints.append(
|
|
"Somebody must have done this. Look in the survex file, or in the logbook entries for this date, for the people who created this data."
|
|
)
|
|
|
|
# survex, but get_ticks has already done much of this ??
|
|
survex_complaint = ""
|
|
|
|
if waldata["survex file"]:
|
|
if not type(waldata["survex file"]) == list: # a string also is a sequence type, so do it this way
|
|
waldata["survex file"] = [waldata["survex file"]]
|
|
for sx in waldata["survex file"]:
|
|
# this logic appears in several places, inc get_ticks(). Refactor.
|
|
if sx != "":
|
|
if Path(sx).suffix.lower() != ".svx":
|
|
sx = sx + ".svx"
|
|
svxfiles.append(sx)
|
|
if not (Path(settings.SURVEX_DATA) / sx).is_file():
|
|
file_complaint = f"{wallet} Incorrect survex file name. File {sx} was not found in LOSER repo"
|
|
complaints.append(file_complaint)
|
|
message = f"! {file_complaint}"
|
|
print(message)
|
|
DataIssue.objects.update_or_create(
|
|
parser="wallets", message=message, url=wurl
|
|
) # set URL to this wallet folder
|
|
else:
|
|
try:
|
|
sxpath = str(Path(sx).with_suffix(""))
|
|
SurvexFile.objects.get(path=sxpath)
|
|
except MultipleObjectsReturned:
|
|
# can happen if connecting a wallet to a survex file.. i think..
|
|
QSsvxfiles = SurvexFile.objects.filter(path=sxpath)
|
|
message = f"! {wallet} Urk, multiple SurvexFile objects {sxpath}"
|
|
DataIssue.objects.update_or_create(
|
|
parser="wallets", message=message, url=wurl
|
|
) # set URL to this wallet folder
|
|
for s in QSsvxfiles:
|
|
print(s.path, s.cave, s.primary)
|
|
# QSsvxfiles[0] # dont' know how this happened, fix later..
|
|
except:
|
|
file_complaint = (
|
|
f"{wallet} Survex file {sx} exists, but is not registered in the database {sxpath}. How?.."
|
|
)
|
|
complaints.append(file_complaint)
|
|
message = f"! {file_complaint}"
|
|
print(message)
|
|
DataIssue.objects.update_or_create(
|
|
parser="wallets", message=message, url=wurl
|
|
) # set URL to this wallet folder
|
|
|
|
if waldata["survex not required"] and waldata["survex file"] != [""]:
|
|
survex_complaint = (
|
|
f'Survex is stated as not required and yet there is a survex file! ({waldata["survex file"]})'
|
|
)
|
|
if not waldata["survex not required"] and waldata["survex file"] == [""]:
|
|
survex_complaint = "A survex file is required, but has not been specified!"
|
|
if survex_complaint:
|
|
complaints.append(survex_complaint)
|
|
|
|
ticks = w.get_ticks()
|
|
|
|
# Notes required
|
|
if ticks["N"] != "green":
|
|
complaints.append(
|
|
"The notes needs scanning (or renaming) or tick 'Notes not required' checkbox. No noteNN.jpg or XXnote.jpg file was found. Needed even for an electronic survey."
|
|
)
|
|
|
|
# Plan drawing required
|
|
if ticks["P"] != "green":
|
|
complaints.append(
|
|
"The plan needs drawing (or renaming, or tick 'Plan drawn' checkbox or 'Plan not required' checkbox): no planNN.jpg or XXplan.jpg file found."
|
|
)
|
|
|
|
# Elev drawing required
|
|
if ticks["E"] != "green":
|
|
complaints.append(
|
|
"The elevation needs drawing (or renaming, or tick 'Elev drawn' checkbox or 'Elev not required' checkbox): no elevNN.jpg or XXelev.jpg file found."
|
|
)
|
|
|
|
# Therion
|
|
if ticks["T"] != "green":
|
|
complaints.append(
|
|
"Tunnel or Therion drawing files need drawing, or tick 'Plan/Elev drawn' checkbox or 'Plan/Elev not required' checkboxes"
|
|
)
|
|
|
|
# Description
|
|
if not waldata["description written"]:
|
|
complaints.append(
|
|
"The guidebook description needs writing into the survex file. Tick the 'Cave description written' checkbox when this is done."
|
|
)
|
|
# QMs
|
|
if not waldata["qms written"] and w.year() and int(w.year()) >= 2015:
|
|
complaints.append(
|
|
"The QMs needs writing into the survex file. Tick the 'QMs written' checkbox when this is done."
|
|
)
|
|
|
|
# Website
|
|
if not waldata["website updated"]:
|
|
complaints.append(
|
|
"The cave description webpage is marked as needing updating using the guidebook description from the survex file. Tick the 'Cave Description wepage updated' checkbox when this is done."
|
|
)
|
|
|
|
# Find the cave, if it exists
|
|
# Just for wallets, we are lenient about whether the 1623- prefix has been written down.
|
|
if waldata["cave"]:
|
|
caveobject = None
|
|
try:
|
|
caveid = waldata["cave"]
|
|
if type(caveid) is list:
|
|
for i in caveid:
|
|
i = i.replace("/", "-")
|
|
caveobject = get_cave_leniently(i)
|
|
w.caves.add(caveobject) # new many-to-many field
|
|
#print(w.caves)
|
|
else:
|
|
# either single cave or the square brackets have been removed
|
|
ids = caveid.split(",")
|
|
for i in ids:
|
|
j = i.replace("'","").strip('[] "')
|
|
#print(j)
|
|
try:
|
|
caveobject = get_cave_leniently(j) # may fail if garbage value ,e.g. space, in wallet data
|
|
w.caves.add(caveobject)
|
|
except:
|
|
pass
|
|
# if not caveobject.url == waldata["description url"]:
|
|
# complaints.append(f'The URL of cave description \"{waldata["description url"]}\" does not match the one on record for this cave which is: "{caveobject.url}". If the wallet is not for a cave, put a useful URL here.')
|
|
except Cave.MultipleObjectsReturned:
|
|
complaints.append(f'The cave ID \'{waldata["cave"]}\' is AMBIGUOUS. Please fix it.')
|
|
caveobject = None
|
|
except ObjectDoesNotExist:
|
|
complaints.append(f'The cave ID \'{waldata["cave"]}\' is not recognised. Please fix it.')
|
|
caveobject = None
|
|
else:
|
|
complaints.append(
|
|
'No cave ID is given. If there is no survex file, please give something, even if it is just "1623-000", "surface survey" or "scraps found in hut"'
|
|
)
|
|
caveobject = None
|
|
|
|
return complaints, caveobject
|
|
|
|
|
|
@login_required_if_public
|
|
def walletedit(request, path=None):
|
|
"""Create a new wallet or upload scanned image files into a wallet on /expofiles
|
|
Also display AND EDIT the contents.json data in the wallet.
|
|
|
|
This is the main wallet display and edit page.
|
|
|
|
The Wallet object and the contents.json file are created when the user
|
|
creates the wallet AND THEN SAVES IT WITH A DATE.
|
|
|
|
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,
|
|
as it covers many very different things we do not need. This is simpler,
|
|
or at least, it is all in one place and you can see what it does, inctead of using invisible
|
|
Django defaults that a newcomer can't find.
|
|
(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
|
|
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
|
|
"""
|
|
git = settings.GIT
|
|
filesaved = False
|
|
actual_saved = []
|
|
|
|
def no_surveyscans(year, next):
|
|
"""Detect if a folder of scans exists, even if no wallet Object has been created
|
|
"""
|
|
id = f"{year}#{next:02d}"
|
|
dirpath = Path(settings.SCANS_ROOT, year, id)
|
|
if not dirpath.is_dir():
|
|
return True
|
|
|
|
# if the folder exists, but has nothing in it, we treat it as available
|
|
if len(os.listdir(dirpath)) == 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def get_next_empty():
|
|
"""Gets the next most number for a new wallet just after the most recent one in the
|
|
db. But if it has no date set, then ignore it as it was only just created - is this a race condition?
|
|
|
|
This assumes we are still in the same year as the most recent wallet.
|
|
|
|
This just looks at the wallets in the db initially, then
|
|
checks the sub folders ofexpofiles/surveyscans/[year]/
|
|
"""
|
|
latest = Wallet.objects.filter(walletname__startswith="20",walletdate__isnull=False).latest('walletname') # last VALID wallet
|
|
# print(f"==latest wallet number is {latest}")
|
|
next = int(latest.walletname[5:]) + 1
|
|
year = latest.walletname[:4]
|
|
id = f"{year}:{next:02d}"
|
|
if no_surveyscans(year, next):
|
|
# print(f"==No scanned files found for {year=} # {next=}")
|
|
return id
|
|
else:
|
|
walletname=id.replace(':','#')
|
|
# print(f"==making wallet {walletname}")
|
|
make_wallet(walletname, date=True)
|
|
return get_next_empty() # recursive call
|
|
|
|
def get_last_wallet():
|
|
last = Wallet.objects.all().order_by('walletyear').last()
|
|
# print(f"==last wallet updated {last}")
|
|
return last
|
|
|
|
def are_we_next_year():
|
|
recent_wallet = get_last_wallet()
|
|
recent_year = recent_wallet.walletname[:4]
|
|
current_year = current_expo()
|
|
|
|
return int(current_year) > int(recent_year)
|
|
|
|
def preprocess_path(path):
|
|
if path:
|
|
wpath = urllib.parse.unquote(path)
|
|
else:
|
|
# OK the url is "empty". Now we decide if we want to start the next year.
|
|
if are_we_next_year():
|
|
new_walletname = current_expo() + "#00"
|
|
# print(f"{new_walletname}")
|
|
make_wallet(new_walletname, date=True)
|
|
nx = get_next_empty()
|
|
# print(nx)
|
|
return (None, nx)
|
|
|
|
try:
|
|
year = wpath[:4] # if path too short, exception catches it
|
|
sepr = wpath[4]
|
|
y = int(year) # exception catches non-integer [:4]
|
|
wnumber = int(wpath[5:]) # exception catches nonumeric wallet number
|
|
if sepr != "#" and sepr != ":":
|
|
return (oldwallet(request, path), None)
|
|
except:
|
|
# if nonumeric wpath name for example
|
|
return (oldwallet(request, path), None)
|
|
|
|
if not re.match("(19|20)[0-9][0-9][:#]\d{2,3}", wpath):
|
|
return (None, get_next_empty() )
|
|
|
|
ymin, ymax = YEAR_RANGE
|
|
if int(year) < ymin:
|
|
year = str(ymin+2)
|
|
if int(year) > ymax:
|
|
return (None, get_next_empty() )
|
|
|
|
wallet = f"{year}:{wnumber:02d}"
|
|
return (None, wallet)
|
|
|
|
def identify_most_recent_wallet(wallet, currentyear):
|
|
""" Need to find the last wallet of the previous year
|
|
Has to cope with years when there are no wallets
|
|
Has to cope with weirdly named imported wallets from 1999 & earlier
|
|
Has to cope if the 'current' wallet is one that happens to 'not exist' too.
|
|
|
|
Frankly this has just become too bizarre and we should devise a quite different
|
|
navigation system.
|
|
"""
|
|
current_name = wallet.replace(":","#")
|
|
|
|
try:
|
|
recent_wallet = get_last_wallet()
|
|
allwallets = Wallet.objects.all().order_by('walletyear')
|
|
for w in allwallets:
|
|
if len(w.walletname) < 5:
|
|
continue
|
|
if w.walletname[4] != "#":
|
|
continue
|
|
|
|
if w.walletname == current_name:
|
|
break
|
|
if int(w.walletyear.year) >= int(currentyear):
|
|
break
|
|
recent_wallet = w
|
|
recent_name = recent_wallet.walletname
|
|
except:
|
|
raise
|
|
|
|
recent_year = recent_name[:4]
|
|
recent_number = recent_name[5:]
|
|
|
|
# print(f"---identify_most_recent_wallet: {recent_year=} {recent_number=}")
|
|
return recent_year, recent_number
|
|
|
|
def create_nav_links(wallet):
|
|
"""Find the previous wallet and next wallet and create navigation shortcuts"""
|
|
#xx, yy = identify_most_recent_wallet(wallet, y)
|
|
y = wallet[:4]
|
|
n = wallet[5:]
|
|
|
|
if int(n) == 0:
|
|
recent_year, recent_number = identify_most_recent_wallet(wallet, y)
|
|
prevy = recent_year # same as most recent wallet
|
|
recent_number = f"{int(recent_number):02d}"
|
|
else:
|
|
prevy = f"{int(y)-1}" # the previous year
|
|
recent_year = y # current year
|
|
recent_number = f"{int(n)-1:02d}" # previous number
|
|
|
|
nexty = f"{int(y)+1}"
|
|
next = f"{int(n)+1:02d}"
|
|
return prevy, recent_year, recent_number, y, next, nexty
|
|
|
|
def read_json(contents_path):
|
|
"""Read JSON from the wallet metadata file in the repo
|
|
or fills with blank data if that files can't be read
|
|
|
|
Should sanitise to ensure no spurious backslashes e.g. in windows style paths"""
|
|
waldata = {}
|
|
if contents_path.is_file():
|
|
with open(contents_path) as json_file:
|
|
try:
|
|
waldata = json.load(json_file)
|
|
except:
|
|
message = f"! {wallet} Failed to load {contents_path} JSON file"
|
|
print(message)
|
|
DataIssue.objects.create(parser="scans", message=message, url=wurl) # set URL to this wallet folder
|
|
raise
|
|
else: # no JSON file exists
|
|
print("--- No JSON exists, so using default copy")
|
|
waldata = WALLET_BLANK_JSON.copy()
|
|
if not waldata["survex file"]:
|
|
try:
|
|
w = Wallet.objects.get(walletname=wallet)
|
|
b = SurvexBlock.objects.filter(scanswallet=w)
|
|
waldata["survex file"] = []
|
|
for bsf in b:
|
|
waldata["survex file"].append(bsf.survexfile.path)
|
|
except:
|
|
print(f"--- No wallet {wallet} exists in database")
|
|
return waldata
|
|
|
|
def save_json(jsondict):
|
|
# print(f'--- Wallet directory in :drawings: repo {newfolder=} {jsondict}')
|
|
if not os.path.exists(contents_path.parent):
|
|
print(f"--- No wallet directory in :drawings: repo, so creating it {contents_path.parent}")
|
|
os.makedirs(contents_path.parent)
|
|
|
|
with open(contents_path, "w") as jfile:
|
|
json.dump(jsondict, jfile, indent=1)
|
|
# print(f'--- FINISHED saving to JSON at {contents_path}')
|
|
|
|
def make_wallet(walletname, date=False):
|
|
"""We need a wallet Object so that the django template stuff can find the files
|
|
BUT we must restrict this to logged-in users otherwise spiderbots get at
|
|
the hidden Submit button and create zillions of the buggers"""
|
|
# print(f"== make_wallet() Making new wallet {walletname}")
|
|
try:
|
|
w, created = Wallet.objects.get_or_create(walletname=walletname)
|
|
print(f"--- Wallet string {walletname}, wallet object {w} created new?: {created}")
|
|
if date:
|
|
w.walletdate = datetime.datetime.now()
|
|
w.save()
|
|
if created:
|
|
w.fpath = Path(settings.SCANS_ROOT, walletname[0:4], walletname)
|
|
_ = w.year() # sets the walletyear property as a side-effect
|
|
w.save()
|
|
except:
|
|
print(f"!-- Wallet string {walletname}, FAIL TO GET or create WALLET OBJECT")
|
|
raise
|
|
return w
|
|
|
|
def commit_json(waldata):
|
|
destfolder = contents_path.parent
|
|
dr_add = subprocess.run([git, "add", contentsjson], cwd=destfolder, capture_output=True, text=True)
|
|
if dr_add.returncode != 0:
|
|
msgdata = (
|
|
"Ask a nerd to fix this.\n--"
|
|
+ dr_add.stderr
|
|
+ "\n--"
|
|
+ dr_add.stdout
|
|
+ "\n--return code: "
|
|
+ str(dr_add.returncode)
|
|
)
|
|
message = (
|
|
f"CANNOT git on server for this file {contentsjson}. Edits saved but not added to git.\n\n" + msgdata
|
|
)
|
|
print(message)
|
|
return render(request, "errors/generic.html", {"message": message})
|
|
else:
|
|
|
|
if socket.gethostname() != "expo":
|
|
comment = f"on dev machine '{socket.gethostname()}' "
|
|
else:
|
|
comment = ""
|
|
if "cave" in waldata:
|
|
label = waldata["cave"]
|
|
else:
|
|
if "name" in waldata:
|
|
label = waldata["name"]
|
|
else:
|
|
label = ""
|
|
|
|
dr_commit = subprocess.run(
|
|
[git, "commit", "-m", f"JSON update wallet {wallet} {label} {comment}"],
|
|
cwd=destfolder,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
# This produces return code = 1 if it commits OK
|
|
if dr_commit.returncode != 0:
|
|
msgdata = (
|
|
"Ask a nerd to fix this.\n\nstderr: "
|
|
+ dr_commit.stderr
|
|
+ "\n\nstdout: "
|
|
+ dr_commit.stdout
|
|
+ "\n\nreturn code: "
|
|
+ str(dr_commit.returncode)
|
|
)
|
|
message = (
|
|
f"Error code with git on server for this {contentsjson}. File is added to git, but NOT committed.\n"
|
|
+ msgdata
|
|
)
|
|
print(message)
|
|
return render(request, "errors/generic.html", {"message": message})
|
|
|
|
def get_logbook_trips():
|
|
return None
|
|
|
|
def no_people(team):
|
|
return (team == ["Unknown"]
|
|
or team == [""]
|
|
or team == "")
|
|
|
|
def empty_string(thing):
|
|
return (thing == [""]
|
|
or thing == []
|
|
or thing == ""
|
|
or thing == "[]"
|
|
or thing is None)
|
|
|
|
|
|
|
|
def scan_survexblocks(svxfile):
|
|
"""Scans for *team people attached to all the survex blocks in this svxfile
|
|
This could be rather a lot for some compedious survex files! So would need
|
|
culling manually and only those relevant put in the JSON file"""
|
|
wallet_refs = []
|
|
dates = []
|
|
blocknames = []
|
|
team = []
|
|
try:
|
|
blocks = SurvexBlock.objects.filter(survexfile=svxfile)
|
|
for b in blocks:
|
|
# print(f" - - - - {b=} {b.scanswallet.walletname} {b.date=} ")
|
|
if b.scanswallet:
|
|
wallet_refs.append(b.scanswallet) # other wallets
|
|
#if b.scanswallet.walletname == wallet: # only if we assume *ref all correct!
|
|
if b.date:
|
|
dates.append(b.date)
|
|
if b.name != b.title:
|
|
blocknames.append(str(b.name) + "|" + str(b.title))
|
|
else:
|
|
blocknames.append(str(b.name))
|
|
QSpeople = SurvexPersonRole.objects.filter(survexblock=b)
|
|
# print(f" - - {QSpeople=}")
|
|
for p in QSpeople:
|
|
# print(f" - - {p.personname} ")
|
|
team.append(p.personname)
|
|
except:
|
|
message = " - No associated survex blocks found for this wallet"
|
|
print(message)
|
|
# print(f" - - - ", wallet_refs, dates, blocknames, team)
|
|
return wallet_refs, dates, blocknames, team
|
|
|
|
def scan_survexfiles(survex_paths):
|
|
"""Read data from the list of survex file names attached to the wallet JSON file
|
|
|
|
NEED TO ALSO CHECK survex files which have a *ref to this wallet !
|
|
"""
|
|
cave_refs = []
|
|
wallet_refs = []
|
|
caves = []
|
|
dates = []
|
|
names = []
|
|
team = []
|
|
|
|
if not type(survex_paths) == list: # a string also is a sequence type, so do it this way
|
|
survex_paths = [survex_paths]
|
|
|
|
for svxfl in survex_paths:
|
|
if not svxfl: # not a blank string
|
|
continue
|
|
|
|
# print(f" - - {svxf=} ")
|
|
svxf = Path(svxfl)
|
|
if svxf.suffix.lower() != ".svx":
|
|
svxf = svxf.with_suffix(".svx")
|
|
f = Path(settings.SURVEX_DATA) / svxf
|
|
if not f.is_file():
|
|
message = f"! {wallet} Specified survex file '{svxf}' in wallet not found on disc, probably renamed."
|
|
DataIssue.objects.update_or_create(parser="wallets", message=message, url=wurl) # set URL to this wallet folder
|
|
|
|
print(message)
|
|
continue
|
|
|
|
fpath = svxf.parent / svxf.stem
|
|
# print(f' - {fpath=}')
|
|
try:
|
|
svxfile = SurvexFile.objects.get(path=fpath)
|
|
except:
|
|
message = f"Specified and present survex file '{fpath}'not found in db. Database may be empty. Exception."
|
|
print(message)
|
|
# This failure will also get picked up by the "S" colour code red or orange
|
|
try:
|
|
if svxfile.cave:
|
|
caves.append(svxfile.cave)
|
|
cave_refs.append(svxfile.cave.slug()) # this is a string?!
|
|
|
|
|
|
w, d, n, t = scan_survexblocks(svxfile)
|
|
wallet_refs.extend(w)
|
|
dates.extend(d)
|
|
names.extend(n)
|
|
team.extend(t)
|
|
except:
|
|
message = f"Exception wallet handling for {fpath} '{svxfile.cave}' "
|
|
print(message)
|
|
# This failure will also get picked up by the "S" colour code red or orange
|
|
|
|
caves = list(set(caves))
|
|
if len(caves) == 1:
|
|
caves = caves[0]
|
|
|
|
elif len(caves) > 1:
|
|
print(
|
|
f" - More than one Cave {caves} in this wallet {wallet}. Not managed in this troggle release."
|
|
)
|
|
|
|
if len(names) == 1:
|
|
names = names[0]
|
|
elif len(names) > 1:
|
|
names = f"several, please edit: {names}"
|
|
print(
|
|
f" - More than one block name is relevant {names} in this wallet {wallet}. Not managed in this troggle release."
|
|
)
|
|
|
|
cave_refs = list(set(cave_refs))
|
|
firstdate = None
|
|
if dates:
|
|
firstdate = min(dates).isoformat()
|
|
return firstdate, list(set(team)), caves, cave_refs, wallet_refs, names
|
|
|
|
checkboxes = [
|
|
"description written",
|
|
"survex not required",
|
|
"qms written",
|
|
"website updated",
|
|
"plan not required",
|
|
"plan drawn",
|
|
"elev not required",
|
|
"elev drawn",
|
|
"notes not required",
|
|
"electronic survey",
|
|
]
|
|
|
|
redirect, wallet = preprocess_path(path)
|
|
if redirect:
|
|
return redirect
|
|
prevy, recent_year, recent_number, year, next, nexty = create_nav_links(wallet)
|
|
|
|
wurl = f"/walletedit/{wallet}".replace("#", ":")
|
|
wallet = wallet.replace(":", "#")
|
|
dirpath = Path(settings.SCANS_ROOT, year, wallet)
|
|
contents_path = Path(settings.DRAWINGS_DATA, "walletjson") / year / wallet / contentsjson
|
|
|
|
fresh_wallet = False
|
|
|
|
form = FilesForm()
|
|
|
|
if request.method == "POST":
|
|
# print(f'--- POST processing starts {wallet=} {path=}')
|
|
if "psg" in request.POST: # handle metadata form
|
|
formj = WalletForm(request.POST)
|
|
# Beware. All fields returned as strings. So must re-type them as
|
|
# lists etc. before using or re-saving
|
|
|
|
# Unset checkboxes do not return any value, checked ones return "True".
|
|
# So all need initialising to False
|
|
if formj.is_valid():
|
|
posted = request.POST.copy()
|
|
posted.pop("csrfmiddlewaretoken") # discard this
|
|
wd = WALLET_BLANK_JSON.copy()
|
|
for f in checkboxes:
|
|
wd[f] = False
|
|
# print(f'--- wd ${f}$ - {wd[f]}')
|
|
for f in posted:
|
|
wd[xlate[f]] = posted[f].replace("'", '"')
|
|
print(f"'{f}' -{xlate[f]}- {posted[f]}")
|
|
if posted[f] == "True":
|
|
wd[xlate[f]] = True
|
|
|
|
newdate = make_valid_date("no id yet in form", posted["date"])
|
|
wd["people"] = wd["people"][1:-1].replace('"', "").split(",")
|
|
for i, elem in enumerate(wd["people"]):
|
|
wd["people"][i] = elem.strip()
|
|
if wd["cave"]:
|
|
if wd["cave"][0] == "[":
|
|
wd["cave"] = wd["cave"][1:-1].replace('"', "").split(",")
|
|
for i, elem in enumerate(wd["cave"]):
|
|
wd["cave"][i] = elem.strip()
|
|
|
|
if wd["survex file"]: # allow for no survex file at all
|
|
if wd["survex file"][0] == "[":
|
|
wd["survex file"] = wd["survex file"][1:-1]
|
|
wd["survex file"] = wd["survex file"].replace('"', "").split(",")
|
|
for i, elem in enumerate(wd["survex file"]):
|
|
wd["survex file"][i] = elem.strip()
|
|
|
|
|
|
save_json(wd)
|
|
# walletobject will already exist as creation does not happen here anymore
|
|
walletobject = make_wallet(wallet)
|
|
walletobject.walletdate = newdate # must be valid date
|
|
print(f"---Setting VALID new date to db {walletobject} {walletobject.walletdate}")
|
|
walletobject.save()
|
|
print(f"---Setting VALID new date to db {walletobject} {walletobject.walletdate}")
|
|
commit_json(wd)
|
|
|
|
else:
|
|
print("--- INVALID JSON Update form submitted")
|
|
print(formj.errors)
|
|
return render(request, "errors/generic.html", {"message": formj.errors})
|
|
|
|
elif (
|
|
"walletgoto" in request.POST
|
|
): # not editing wallet data or uploading a file.. going direct to a named wallet
|
|
formg = WalletGotoForm(request.POST, request.FILES)
|
|
if formg.is_valid():
|
|
walletgoto = request.POST["walletgoto"]
|
|
|
|
return HttpResponseRedirect(f'/walletedit/{walletgoto.replace("#",":")}')
|
|
|
|
else: # Creating a wallet .
|
|
# NOT editing wallet data, or uploading a file. Should not overwrite metadata at all.
|
|
if "submitbutton" in request.POST:
|
|
print(f"--- Submit button value {request.POST['submitbutton']}")
|
|
if request.POST['submitbutton']=="Create":
|
|
w = WALLET_BLANK_JSON.copy()
|
|
save_json(w)
|
|
walletobject = make_wallet(wallet) # no date set yet
|
|
commit_json(w)
|
|
|
|
form = FilesForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
# print(f'--- FORM walletedit multiple BUT EMPTY METADATA supposedly {WALLET_BLANK_JSON["date"]=}')
|
|
multiple = request.FILES.getlist("uploadfiles")
|
|
fs = FileSystemStorage(os.path.join(dirpath)) # creates wallet folder if necessary
|
|
|
|
waldata = read_json(contents_path)
|
|
actual_saved = []
|
|
if multiple:
|
|
for f in multiple:
|
|
try: # crashes in Django os.chmod call if on WSL, but does save file!
|
|
saved_filename = fs.save(f.name, content=f)
|
|
except:
|
|
print(f"\n !! Permissions failure ?! on attempting to save scanfile {f.name}")
|
|
if "saved_filename" in locals():
|
|
if saved_filename.is_file():
|
|
actual_saved.append(saved_filename)
|
|
# print(f'! - FORM walletedit multiple {actual_saved}')
|
|
filesaved = True
|
|
# print(f'--- FORM walletedit multiple BUT EMPTY METADATA supposedly {WALLET_BLANK_JSON["date"]=}')
|
|
save_json(waldata)
|
|
walletobject = make_wallet(wallet)
|
|
commit_json(waldata)
|
|
else:
|
|
print("--- Upload files form invalid, which is correct if just created.")
|
|
#
|
|
# Not a POST, so a GET starts here. And also control gets here after a POST is processed.
|
|
#
|
|
files = []
|
|
dirs = []
|
|
# print(f'! - FORM walletedit - start {wallet} {dirpath}')
|
|
if dirpath.is_dir():
|
|
create = False # wallet exists because folder exists, even if nothing in it
|
|
try:
|
|
for f in dirpath.iterdir():
|
|
if f.is_dir():
|
|
for d in f.iterdir():
|
|
dirs.append(f"{f.name}/{d.name}")
|
|
if f.is_file():
|
|
files.append(f.name)
|
|
except FileNotFoundError:
|
|
files.append(
|
|
"(No wallet yet. It would be created if you upload a scan and then save the form with a date.)"
|
|
)
|
|
else:
|
|
# either on GET or on dropping-through after the POST creating a new wallet object:
|
|
if Wallet.objects.filter(walletname=wallet).exists():
|
|
create = False
|
|
else:
|
|
create = True
|
|
|
|
if len(files) > 0:
|
|
files = sorted(files)
|
|
|
|
if dirs:
|
|
dirs = sorted(dirs)
|
|
try:
|
|
waldata = read_json(contents_path)
|
|
except:
|
|
message = f"Nasty failure in parsing wallets metadata in {contents_path}. Probably backslash not forward slash in filename path"
|
|
return render(request, "errors/generic.html", {"message": message})
|
|
|
|
jsonfile = Path(settings.DRAWINGS_DATA, "walletjson") / wallet[0:4] / wallet / "contents.json"
|
|
# print(f'! - FORM walletedit - jsonfile {jsonfile}')
|
|
if not Path(jsonfile).is_file():
|
|
metadataurl = ""
|
|
metadata =""
|
|
else:
|
|
metadataurl = Path("/dwgdataraw", "walletjson") / wallet[0:4] / wallet.replace("#", ":") / "contents.json"
|
|
with open(jsonfile, 'r') as f:
|
|
metadata = f.read()
|
|
psg = ""
|
|
freetext = ""
|
|
chkplannr = ""
|
|
chknotesnr = ""
|
|
chkpland = ""
|
|
svxfiles = []
|
|
trips = []
|
|
checked = {}
|
|
context = {}
|
|
if not waldata: # should always be true as populated by blank data if json file doesn't exist
|
|
message = f" !! No Wallet data initialised or read ! Should not happen."
|
|
print(message)
|
|
return render(request, "errors/generic.html", {"message": message})
|
|
|
|
refs = []
|
|
|
|
if "survex file" in waldata:
|
|
date, team, caves, caverefs, wallet_refs, names = scan_survexfiles(waldata["survex file"])
|
|
# Override the discovered values with those in the JSON file:
|
|
if not waldata["date"]: # either absent or empty string
|
|
waldata["date"] = date
|
|
|
|
if no_people(waldata["people"]):
|
|
people = team
|
|
waldata["people"] = team
|
|
else:
|
|
people = waldata["people"] # text string
|
|
|
|
if empty_string(waldata["cave"]):
|
|
cave = caverefs # a list, potentially
|
|
waldata["cave"] = cave
|
|
else:
|
|
cave = waldata["cave"] # text string
|
|
|
|
if empty_string(waldata["name"]):
|
|
psg = names
|
|
waldata["name"] = names
|
|
else:
|
|
psg = waldata["name"]
|
|
|
|
if "free text" in waldata:
|
|
freetext = waldata["free text"]
|
|
|
|
if 'notes not required' not in waldata: # cope with schema change
|
|
waldata['notes not required'] = False
|
|
|
|
# find trips and survex files of the same date
|
|
walletobject = make_wallet(wallet)
|
|
if waldata["date"]:
|
|
samedate = make_valid_date(psg, waldata["date"])
|
|
walletobject.walletdate = samedate
|
|
walletobject.save()
|
|
|
|
try:
|
|
thisexpo = Expedition.objects.get(year=int(year))
|
|
except: # creating a wallet for an expo that does not exist perhaps
|
|
message = f"Trying to access an Expo for '{year}' which does not exist (yet)."
|
|
message += " See /handbook/computing/newyear.html"
|
|
print(message)
|
|
return render(request, "errors/generic.html", {"message": message})
|
|
if samedate:
|
|
svxothers = SurvexFile.objects.filter(survexblock__date=samedate).distinct()
|
|
trips = LogbookEntry.objects.filter(date=samedate)
|
|
wallets = Wallet.objects.filter(walletdate=samedate)
|
|
else:
|
|
svxothers = None
|
|
trips = None
|
|
wallets = None
|
|
|
|
else:
|
|
svxothers = None
|
|
trips = None
|
|
wallets = None
|
|
|
|
# Survex and survex complaints, comes from json file on disc, not as pre-populated as above
|
|
complaints, caveobject = get_complaints([], waldata, svxfiles, files, wallet, wurl)
|
|
# print(f' - {caveobject=}')
|
|
|
|
for f in checkboxes:
|
|
if waldata[f]:
|
|
checked[f] = "checked"
|
|
|
|
survexsize = str(min(len(str(waldata["survex file"])), 46))
|
|
|
|
try:
|
|
thiswallet = walletobject # Wallet.objects.get(walletname=wallet)
|
|
caveifywallet(thiswallet)
|
|
thiswallet.ticks = thiswallet.get_ticks() # the complaints in colour form
|
|
# fixsurvextick(thiswallet, thiswallet.ticks)
|
|
# print(f"--- {wallet} {thiswallet} walletdate={thiswallet.walletdate} immediately before form render")
|
|
except:
|
|
thiswallet = None
|
|
context = {
|
|
"year": year,
|
|
"recent_year": recent_year,
|
|
"recent_number": recent_number,
|
|
"next": next,
|
|
"prevy": prevy,
|
|
"nexty": nexty,
|
|
"files": files,
|
|
"dirs": dirs,
|
|
"waldata": waldata,
|
|
"svxfiles": svxfiles,
|
|
"survex": waldata["survex file"],
|
|
"survexsize": survexsize,
|
|
"checked": checked,
|
|
"trips": trips,
|
|
"manywallets": [thiswallet],
|
|
"wallets": wallets,
|
|
"svxothers": svxothers,
|
|
"create": create,
|
|
"metadataurl": metadataurl,
|
|
"metadata": metadata,
|
|
"complaints": complaints,
|
|
"caveobject": caveobject,
|
|
"people": people,
|
|
"peoplesize": str(len(str(people))),
|
|
"filesaved": filesaved,
|
|
"actual_saved": actual_saved,
|
|
}
|
|
|
|
return render(
|
|
request,
|
|
"walletform.html",
|
|
{
|
|
"form": form,
|
|
"wallet": wallet,
|
|
**context,
|
|
"date": waldata["date"],
|
|
#'url': waldata["description url"], 'urlsize': str(len(str(waldata["description url"]))),
|
|
"cave": cave,
|
|
"psg": psg,
|
|
"freetext": freetext,
|
|
"psgsize": str(max(12, len(str(psg)))),
|
|
"freetextsize": str(max(60, len(str(freetext)))),
|
|
},
|
|
)
|
|
|