Merge branch 'python3-new' of ssh://expo.survex.com/home/expo/troggle into python3-new

This commit is contained in:
Martin Green 2022-08-01 16:05:17 +02:00
commit c1aaf07885
25 changed files with 712 additions and 138 deletions

View File

@ -178,7 +178,7 @@ EXPOWEB = REPOS_ROOT_PATH / "expoweb"
CAVEDESCRIPTIONS = EXPOWEB / "cave_data" CAVEDESCRIPTIONS = EXPOWEB / "cave_data"
ENTRANCEDESCRIPTIONS = EXPOWEB / "entrance_data" ENTRANCEDESCRIPTIONS = EXPOWEB / "entrance_data"
EXPOWEB_URL = '' EXPOWEB_URL = ''
SCANS_URL = '/survey_scans/' # SCANS_URL = '/survey_scans/' # defunct, removed.
# Sanitise these to be strings as all other code is expecting strings # Sanitise these to be strings as all other code is expecting strings
# and we have not made the change to pathlib Path type in the other localsettings-* variants yet. # and we have not made the change to pathlib Path type in the other localsettings-* variants yet.

View File

@ -18,7 +18,7 @@ There are other, simpler, upload forms in view/uploads.py
Some are not used and need renovating or destroying. Some are not used and need renovating or destroying.
''' '''
todo = '''Re-enable TinyMCE todo = '''
''' '''
class CaveForm(ModelForm): class CaveForm(ModelForm):

View File

@ -26,6 +26,14 @@ from django.shortcuts import render
from troggle.core.models.troggle import TroggleModel, Person, Expedition, DataIssue from troggle.core.models.troggle import TroggleModel, Person, Expedition, DataIssue
from troggle.core.models.survex import SurvexStation from troggle.core.models.survex import SurvexStation
from troggle.core.utils import writetrogglefile from troggle.core.utils import writetrogglefile
from troggle.core.utils import TROG
# Us ethe TROG global object to cache teh cave lookup list
Gcavelookup = TROG['caves']['gcavelookup']
Gcave_count = TROG['caves']['gcavecount']
Gcavelookup = None
Gcave_count = None
'''The model declarations for Areas, Caves and Entrances. Also LogBookENtry, QM, PersonTrip '''The model declarations for Areas, Caves and Entrances. Also LogBookENtry, QM, PersonTrip
''' '''
@ -600,8 +608,6 @@ class PersonTrip(TroggleModel):
return f'{self.personexpedition} ({self.logbook_entry.date})' return f'{self.personexpedition} ({self.logbook_entry.date})'
Gcavelookup = None
Gcave_count = None
def GetCaveLookup(): def GetCaveLookup():
"""A very relaxed way of finding probably the right cave given almost any string which might serve to identify it """A very relaxed way of finding probably the right cave given almost any string which might serve to identify it

View File

@ -1,6 +1,10 @@
import os import os
from urllib.parse import urljoin
import re import re
import json
import operator
from urllib.parse import urljoin
from pathlib import Path
from functools import reduce
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
@ -18,7 +22,7 @@ class SurvexDirectory(models.Model):
verbose_name_plural = "Survex directories" verbose_name_plural = "Survex directories"
def __str__(self): def __str__(self):
return "[SurvexDirectory:"+str(self.path) + "-" + str(self.primarysurvexfile.path) + "-" + str(self.cave)+"]" return "[SurvexDirectory:"+str(self.path) + " | Primary svx:" + str(self.primarysurvexfile.path) +".svx ]"
class SurvexFile(models.Model): class SurvexFile(models.Model):
@ -160,6 +164,9 @@ class SurvexPersonRole(models.Model):
return str(self.person) + " - " + str(self.survexblock) return str(self.person) + " - " + str(self.survexblock)
class Wallet(models.Model): class Wallet(models.Model):
'''We do not keep the JSON values in the database, we query them afresh each time,
but we will change this when we need to do a Django query on e.g. personame
'''
fpath = models.CharField(max_length=200) fpath = models.CharField(max_length=200)
walletname = models.CharField(max_length=200) walletname = models.CharField(max_length=200)
@ -169,8 +176,171 @@ class Wallet(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return urljoin(settings.URL_ROOT, reverse('singlewallet', kwargs={"path":re.sub("#", "%23", self.walletname)})) return urljoin(settings.URL_ROOT, reverse('singlewallet', kwargs={"path":re.sub("#", "%23", self.walletname)}))
def get_json(self):
jsonfile = Path(self.fpath, 'contents.json')
if not Path(jsonfile).is_file():
#print(f'{jsonfile} is not a file')
return None
else:
with open(jsonfile) as json_f:
try:
waldata = json.load(json_f)
except:
wurl = f"/scanupload/{self.walletname}" # .replace('#', ':')
message = f"! {str(self.walletname)} Failed to load {jsonfile} JSON file"
#print(message)
raise
return waldata
def year(self):
if self.walletname[4] != "#":
return None
year = int(self.walletname[0:4])
if year < 1976 or year > 2050:
return None
else:
return str(year)
# Yes this is horribly, horribly inefficient, esp. for a page that have date, people and cave in it
def date(self):
if not self.get_json():
return None
jsondata = self.get_json()
return jsondata["date"]
def people(self):
if not self.get_json():
return None
jsondata = self.get_json()
return jsondata["people"]
def cave(self):
if not self.get_json():
return None
jsondata = self.get_json()
return jsondata["cave"]
def name(self):
if not self.get_json():
return None
jsondata = self.get_json()
return jsondata["name"]
def get_fnames(self):
'''Filenames without the suffix, i.e. without the ".jpg"
'''
dirpath = Path(settings.SCANS_ROOT, self.fpath)
files = []
if dirpath.is_dir():
try:
for f in dirpath.iterdir():
if f.is_file():
if f.name != 'contents.json' and f.name != 'walletindex.html':
files.append(Path(f.name).stem)
except FileNotFoundError:
pass
return files
def get_ticks(self):
waldata = self.get_json()
if not waldata:
return {}
ticks = {}
# Initially, are there any required survex files present ?
survexok = "red"
ticks["S"] = "red"
if waldata["survex not required"]:
survexok = "green"
ticks["S"] = "green"
else:
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"]]
ngood = 0
nbad = 0
ticks["S"] = "lightblue"
for svx in waldata["survex file"]:
if svx !="":
if (Path(settings.SURVEX_DATA) / svx).is_file():
ngood += 1
else:
nbad += 1
if nbad == 0 and ngood >= 1:
ticks["S"] = "green"
if nbad >= 1 and ngood >= 1:
ticks["S"] = "orange"
if nbad >= 1 and ngood == 0:
ticks["S"] = "red"
# Cave Description
if waldata["description written"]:
ticks["C"] = "green"
else:
ticks["C"] = survexok
# QMs
if waldata["qms written"]:
ticks["Q"] = "green"
else:
ticks["Q"] = survexok
# Notes, Plan, Elevation; Tunnel
if waldata["electronic survey"]:
ticks["N"] = "green"
ticks["P"] = "green"
ticks["E"] = "green"
ticks["T"] = "green"
else:
files = self.get_fnames()
# Notes required
notes_scanned = reduce(operator.or_, [f.startswith("note") for f in files], False)
notes_scanned = reduce(operator.or_, [f.endswith("notes") for f in files], notes_scanned)
if notes_scanned:
ticks["N"] = "green"
else:
ticks["N"] = "red"
# Plan drawing required
plan_scanned = reduce(operator.or_, [f.startswith("plan") for f in files], False)
plan_scanned = reduce(operator.or_, [f.endswith("plan") for f in files], plan_scanned)
plan_drawing_required = not (plan_scanned or waldata["plan drawn"] or waldata["plan not required"])
if plan_drawing_required:
ticks["P"] = "red"
else:
ticks["P"] = "green"
# Elev drawing required
elev_scanned = reduce(operator.or_, [f.startswith("elev") for f in files], False)
elev_scanned = reduce(operator.or_, [f.endswith("elev") for f in files], elev_scanned)
elev_scanned = reduce(operator.or_, [f.endswith("elevation") for f in files], elev_scanned)
elev_drawing_required = not (elev_scanned or waldata["elev drawn"] or waldata["elev not required"])
if elev_drawing_required:
ticks["E"] = "red"
else:
ticks["E"] = "green"
# Tunnel / Therion
if elev_drawing_required or plan_drawing_required:
ticks["T"] = "red"
else:
ticks["T"] = "green"
# Website
if waldata["website updated"]:
ticks["W"] = "green"
else:
ticks["W"] = "red"
return ticks
def __str__(self): def __str__(self):
return str(self.walletname) + " (Wallet)" return "[" + str(self.walletname) + " (Wallet)]"
class SingleScan(models.Model): class SingleScan(models.Model):
ffile = models.CharField(max_length=200) ffile = models.CharField(max_length=200)
@ -189,7 +359,7 @@ class SingleScan(models.Model):
class DrawingFile(models.Model): class DrawingFile(models.Model):
dwgpath = models.CharField(max_length=200) dwgpath = models.CharField(max_length=200)
dwgname = models.CharField(max_length=200) dwgname = models.CharField(max_length=200)
manywallets = models.ManyToManyField("Wallet") # implicitly links via folders to scans to SVX files dwgwallets = models.ManyToManyField("Wallet") # implicitly links via folders to scans to SVX files
scans = models.ManyToManyField("SingleScan") # implicitly links via scans to SVX files scans = models.ManyToManyField("SingleScan") # implicitly links via scans to SVX files
dwgcontains = models.ManyToManyField("DrawingFile") # case when its a frame type dwgcontains = models.ManyToManyField("DrawingFile") # case when its a frame type
filesize = models.IntegerField(default=0) filesize = models.IntegerField(default=0)

View File

@ -128,7 +128,7 @@ class Person(TroggleModel):
#href = models.CharField(max_length=200) #href = models.CharField(max_length=200)
orderref = models.CharField(max_length=200) # for alphabetic orderref = models.CharField(max_length=200) # for alphabetic
user = models.OneToOneField(User, null=True, blank=True,on_delete=models.CASCADE) user = models.OneToOneField(User, null=True, blank=True,on_delete=models.CASCADE) # not used now
def get_absolute_url(self): def get_absolute_url(self):
return urljoin(settings.URL_ROOT,reverse('person',kwargs={'first_name':self.first_name,'last_name':self.last_name})) return urljoin(settings.URL_ROOT,reverse('person',kwargs={'first_name':self.first_name,'last_name':self.last_name}))

View File

@ -42,6 +42,10 @@ TROG = {
}, },
'issues' : { 'issues' : {
'logdataissues' : {} 'logdataissues' : {}
},
'caves' : {
'gcavelookup' : {},
'gcavecount' : {}
} }
} }

View File

@ -1,5 +1,6 @@
import os, stat import os, stat
import re import re
import datetime
from pathlib import Path from pathlib import Path
from urllib.parse import urljoin, unquote as urlunquote from urllib.parse import urljoin, unquote as urlunquote
from urllib.request import urlopen from urllib.request import urlopen
@ -8,9 +9,12 @@ from django.conf import settings
from django.shortcuts import render from django.shortcuts import render
from django.http import HttpResponse from django.http import HttpResponse
from troggle.core.models.survex import Wallet, SingleScan from troggle.core.models.survex import Wallet, SingleScan, SurvexBlock
from troggle.core.models.troggle import Person
from troggle.core.models.caves import GetCaveLookup from troggle.core.models.caves import GetCaveLookup
from troggle.core.views.expo import getmimetype from troggle.core.views.expo import getmimetype
#from troggle.parsers.people import GetPersonExpeditionNameLookup
#import parsers.surveys #import parsers.surveys
'''one of these views serves files as binary blobs, and simply set the mime type based on the file extension, '''one of these views serves files as binary blobs, and simply set the mime type based on the file extension,
@ -19,8 +23,148 @@ by looking inside the file before being served.
need to check if inavlid query string is invalid, or produces multiple replies need to check if inavlid query string is invalid, or produces multiple replies
and render a user-friendly error page. and render a user-friendly error page.
Note that datewallet(), caveifywallet() etc do NOT save the object to the db. They are ephemeral, just for the page rendering of the
manywallets dict.
''' '''
def populatewallet(w):
'''Copy survex data here just for display, not permanently
'''
survexpeople = []
blocks = SurvexBlock.objects.filter(scanswallet = w)
for b in blocks:
for personrole in b.survexpersonrole_set.all():
survexpeople.append(personrole.personname)
w.persons = list(set(survexpeople))
def datewallet(w, earliest):
first = earliest
blocks = SurvexBlock.objects.filter(scanswallet = w)
for b in blocks:
if b.date:
if b.date < first:
first = b.date
if first == earliest:
# no date found
w.date = None
else:
w.date = first
def caveifywallet(w):
'''Gets the cave from the list of survex files,
only selects one of them though. Only used for display.
'''
blocks = SurvexBlock.objects.filter(scanswallet = w)
for b in blocks:
# NB b.cave is not populated by parser. Use b.survexfile.cave instead, or we could parse b.survexpath
if b.survexfile.cave:
w.cave = b.survexfile.cave # just gets the last one, randomly. SHould make this a list or many:many ideally
def fillblankpeople(w):
wp = w.people()
if not wp: # an -empty list
populatewallet(w)
else:
if len(wp) == 1:
nobody = wp[0].lower()
if nobody == 'unknown' or nobody == 'nobody' or nobody == ' ':
populatewallet(w)
def fillblankothers(w):
earliest = datetime.datetime.now().date()
if not w.date():
datewallet(w, earliest)
c = w.cave()
if not c:
caveifywallet(w)
def walletslistperson(request, first_name, last_name):
'''Page which displays a list of all the wallets for a specific person
HORRIBLE linear search through everything. Index and do SQL query properly
'''
# This is where we face having to re-do everything to do with names properly, rather than the horrible series of hacks over 20 years..
#GetPersonExpeditionNameLookup
def tickspersonwallet(p):
manywallets = []
wallets = Wallet.objects.all()
for w in wallets:
w.persons = w.people() # ephemeral attribute for web page
fillblankpeople(w)
if w.persons:
if p.fullname in w.persons:
manywallets.append(w)
fillblankothers(w)
w.ticks = w.get_ticks() # the complaints in colour form
return manywallets
try:
if last_name:
p = Person.objects.get(fullname= f'{first_name} {last_name}')
else:
# speciall Wookey-hack
p = Person.objects.get(first_name= f'{first_name}')
except:
#raise
return render(request, 'errors/generic.html', {'message': f'Unrecognised name of a expo person: "{first_name} {last_name}"'})
manywallets = tickspersonwallet(p)
return render(request, 'personwallets.html', { 'manywallets':manywallets, 'settings': settings, 'person': p})
def walletslistyear(request, year):
'''Page which displays a list of all the wallets in a specific year
'''
def ticksyearwallet(year):
manywallets = []
wallets = Wallet.objects.all()
for w in wallets:
if year == w.year():
manywallets.append(w)
fillblankpeople(w)
fillblankothers(w)
w.ticks = w.get_ticks() # the complaints in colour form
else:
continue
return manywallets
if year < 1976 or year > 2050:
return render(request, 'errors/generic.html', {'message': 'Year out of range. Must be between 1976 and 2050'})
else:
year = str(year)
#return render(request, 'errors/generic.html', {'message': 'This page logic not implemented yet'})
manywallets = ticksyearwallet(year)
return render(request, 'yearwallets.html', { 'manywallets':manywallets, 'settings': settings, 'year': year})
def cavewallets(request, caveid):
'''Returns all the wallets for just one cave
'''
Gcavelookup = GetCaveLookup()
if caveid in Gcavelookup:
cave = Gcavelookup[caveid]
else:
return render(request,'errors/badslug.html', {'badslug': caveid})
# remove duplication. SOrting is done in the template
wallets = set(Wallet.objects.filter(survexblock__survexfile__cave=cave)) # NB a filtered set
manywallets = list(wallets)
for w in manywallets:
fillblankpeople(w)
fillblankothers(w)
w.ticks = w.get_ticks() # the complaints in colour form
return render(request, 'cavewallets.html', { 'manywallets':manywallets, 'settings': settings, 'cave': cave})
def oldwallet(request, path): def oldwallet(request, path):
'''Now called only for non-standard wallet structures for pre-2000 wallets '''Now called only for non-standard wallet structures for pre-2000 wallets
''' '''
@ -59,28 +203,13 @@ def scansingle(request, path, file):
return render(request, 'errors/generic.html', {'message': message}) return render(request, 'errors/generic.html', {'message': message})
def allwallets(request): def allscans(request):
'''Returns all the wallets in the system, we would like to use '''Returns all the wallets in the system, we would like to use
the Django queryset SQL optimisation https://docs.djangoproject.com/en/3.2/ref/models/querysets/#prefetch-related the Django queryset SQL optimisation https://docs.djangoproject.com/en/3.2/ref/models/querysets/#prefetch-related
to get the related singlescan and survexblock objects but that requires rewriting this to do the query on those, not on to get the related singlescan and survexblock objects but that requires rewriting this to do the query on those, not on
the wallets the wallets
''' '''
manywallets = Wallet.objects.all() manywallets = Wallet.objects.all() # NB all of them
# manywallets = Wallet.objects.all().prefetch_related('singlescan') fails as the link is defined on 'singlescan' not on 'wallet' # manywallets = Wallet.objects.all().prefetch_related('singlescan') fails as the link is defined on 'singlescan' not on 'wallet'
return render(request, 'manywallets.html', { 'manywallets':manywallets, 'settings': settings }) return render(request, 'manywallets.html', { 'manywallets':manywallets, 'settings': settings })
def cavewallets(request, cave_id):
'''Returns all the wallets for just one cave,
'''
Gcavelookup = GetCaveLookup()
if cave_id in Gcavelookup:
cave = Gcavelookup[cave_id]
else:
return render(request,'errors/badslug.html', {'badslug': cave_id})
# remove duplication. SOrting is done in the template
wallets = set(Wallet.objects.filter(survexblock__survexfile__cave=cave))
manywallets = list(wallets)
return render(request, 'cavewallets.html', { 'manywallets':manywallets, 'settings': settings, 'cave': cave})

View File

@ -51,7 +51,7 @@ def pathsreport(request):
"SURVEX_DATA" : str( settings.SURVEX_DATA), "SURVEX_DATA" : str( settings.SURVEX_DATA),
"SCANS_ROOT" : str( settings.SCANS_ROOT), "SCANS_ROOT" : str( settings.SCANS_ROOT),
# "SURVEYS" : str( settings.SURVEYS), # "SURVEYS" : str( settings.SURVEYS),
"SCANS_URL" : str( settings.SCANS_URL), # "SCANS_URL" : str( settings.SCANS_URL),
"SURVEXPORT" : str( settings.SURVEXPORT), "SURVEXPORT" : str( settings.SURVEXPORT),
"DRAWINGS_DATA" : str( settings.DRAWINGS_DATA), "DRAWINGS_DATA" : str( settings.DRAWINGS_DATA),
"URL_ROOT" : str( settings.URL_ROOT) "URL_ROOT" : str( settings.URL_ROOT)
@ -88,7 +88,7 @@ def pathsreport(request):
"SURVEX_DATA" : type(settings.SURVEX_DATA), "SURVEX_DATA" : type(settings.SURVEX_DATA),
"SCANS_ROOT" : type(settings.SCANS_ROOT), "SCANS_ROOT" : type(settings.SCANS_ROOT),
# "SURVEYS" : type(settings.SURVEYS), # "SURVEYS" : type(settings.SURVEYS),
"SCANS_URL" : type(settings.SCANS_URL), # "SCANS_URL" : type(settings.SCANS_URL),
"SURVEXPORT" : type(settings.SURVEXPORT), "SURVEXPORT" : type(settings.SURVEXPORT),
"DRAWINGS_DATA" : type(settings.DRAWINGS_DATA), "DRAWINGS_DATA" : type(settings.DRAWINGS_DATA),
"URL_ROOT" : type(settings.URL_ROOT) "URL_ROOT" : type(settings.URL_ROOT)

View File

@ -23,12 +23,12 @@ from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
#from troggle import settings #from troggle import settings
from troggle.parsers.imports import import_caves, import_people, import_surveyscans from troggle.parsers.imports import import_caves, import_people, import_surveyscans
from troggle.parsers.imports import import_logbooks, import_QMs, import_drawingsfiles, import_survex from troggle.parsers.imports import import_logbooks, import_QMs, import_drawingsfiles, import_survex
from troggle.parsers.scans import wallet_blank_json, wallet_blank_html, contentsjson, indexhtml from troggle.parsers.scans import wallet_blank_json, wallet_blank_html, contentsjson, indexhtml, CopyWalletData
# from databaseReset import reinit_db # don't do this. databaseRest runs code *at import time* # from databaseReset import reinit_db # don't do this. databaseRest runs code *at import time*
from troggle.core.models.troggle import DataIssue from troggle.core.models.troggle import DataIssue
from troggle.core.models.troggle import Expedition, Person, PersonExpedition from troggle.core.models.troggle import Expedition, Person, PersonExpedition
from troggle.core.models.caves import LogbookEntry, QM, Cave, PersonTrip from troggle.core.models.caves import LogbookEntry, QM, Cave, PersonTrip
from troggle.core.models.survex import DrawingFile from troggle.core.models.survex import DrawingFile, Wallet
from troggle.core.views.scans import oldwallet, walletindex from troggle.core.views.scans import oldwallet, walletindex
from troggle.core.views.caves import getCave from troggle.core.views.caves import getCave
@ -93,13 +93,16 @@ xlate = {"url": "description url",
"electronic": "electronic survey", "electronic": "electronic survey",
"pland": "plan drawn", "pland": "plan drawn",
"elevd": "elev drawn", "elevd": "elev drawn",
"psg": "name", "psg": "name", # a name for this wallet
"survex": "survex file", "survex": "survex file",
} }
def get_complaints(complaints, waldata, svxfiles, files, wallet, wurl): def get_complaints(complaints, waldata, svxfiles, files, wallet, wurl):
'''Taken from old script wallets.py and edited to make more comprehensible '''Taken from old script wallets.py and edited to make more comprehensible
Loads the survex files names and processes all complaints Loads the survex files names and processes all complaints
All needs to be restructred to use the get_ticks() function on the Wallets class in core/models/survex.py
which does the same thing
''' '''
# Date # Date
if not waldata["date"]: if not waldata["date"]:
@ -115,6 +118,7 @@ def get_complaints(complaints, waldata, svxfiles, files, wallet, wurl):
if not type(waldata["survex file"])==list: # a string also is a sequence type, so do it this way 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"]] waldata["survex file"] = [waldata["survex file"]]
for svx in waldata["survex file"]: for svx in waldata["survex file"]:
if svx !="":
svxfiles.append(svx) svxfiles.append(svx)
if not (Path(settings.SURVEX_DATA) / svx).is_file(): if not (Path(settings.SURVEX_DATA) / svx).is_file():
file_complaint = f"{wallet} Incorrect survex file name in wallet data: {svx} not found in LOSER repo" file_complaint = f"{wallet} Incorrect survex file name in wallet data: {svx} not found in LOSER repo"
@ -133,20 +137,21 @@ def get_complaints(complaints, waldata, svxfiles, files, wallet, wurl):
# Notes required # Notes required
if not waldata["electronic survey"]: if not waldata["electronic survey"]:
notes_scanned = reduce(operator.or_, [f.startswith("note") for f in files], False) notes_scanned = reduce(operator.or_, [f.startswith("note") for f in files], False)
notes_scanned = reduce(operator.or_, [f.endswith("note") for f in files], notes_scanned) notes_scanned = reduce(operator.or_, [Path(f).stem.endswith("notes") for f in files], notes_scanned)
if not notes_scanned: if not notes_scanned:
complaints.append("The notes needs scanning (or renaming): no noteNN.jpg or XXnote.jpg file found; and this is not an electronic survey.") complaints.append("The notes needs scanning (or renaming): no noteNN.jpg or XXnote.jpg file found; and this is not an electronic survey.")
# Plan drawing required # Plan drawing required
plan_scanned = reduce(operator.or_, [f.startswith("plan") for f in files], False) plan_scanned = reduce(operator.or_, [f.startswith("plan") for f in files], False)
plan_scanned = reduce(operator.or_, [f.endswith("plan") for f in files], plan_scanned) plan_scanned = reduce(operator.or_, [Path(f).stem.endswith("plan") for f in files], plan_scanned)
plan_drawing_required = not (plan_scanned or waldata["plan drawn"] or waldata["plan not required"]) plan_drawing_required = not (plan_scanned or waldata["plan drawn"] or waldata["plan not required"])
if plan_drawing_required: if plan_drawing_required:
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.") 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 # Elev drawing required
elev_scanned = reduce(operator.or_, [f.startswith("elev") for f in files], False) elev_scanned = reduce(operator.or_, [f.startswith("elev") for f in files], False)
elev_scanned = reduce(operator.or_, [f.endswith("elev") for f in files], elev_scanned) elev_scanned = reduce(operator.or_, [Path(f).stem.endswith("elev") for f in files], elev_scanned)
elev_scanned = reduce(operator.or_, [Path(f).stem.endswith("elevation") for f in files], elev_scanned)
elev_drawing_required = not (elev_scanned or waldata["elev drawn"] or waldata["elev not required"]) elev_drawing_required = not (elev_scanned or waldata["elev drawn"] or waldata["elev not required"])
if elev_drawing_required: if elev_drawing_required:
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.") 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.")
@ -290,6 +295,21 @@ def scanupload(request, path=None):
with open(contents_path, "w") as jfile: with open(contents_path, "w") as jfile:
json.dump(wd, jfile, indent = 1) json.dump(wd, jfile, indent = 1)
# print(f'--- FINISHED saving to JSON\n') # print(f'--- FINISHED saving to JSON\n')
# This copies the new data to the drawings repo and commit it
# needs the troggle object wallet, not a string
try:
w, created = Wallet.objects.get_or_create(walletname=wallet)
print(f'wallet string {wallet}, wallet object {w} created new?: {created}')
if created:
w.fpath = Path(settings.SCANS_ROOT, wallet[0:4], wallet)
w.save()
CopyWalletData(w)
except:
print(f'wallet string {wallet}, FAIL TO GET WALLET OBJECT, maybe we need to create it ?')
raise
else: else:
print(f'--- INVALID JSON Update form submitted') print(f'--- INVALID JSON Update form submitted')
print(formj.errors) print(formj.errors)

View File

@ -13,23 +13,16 @@ from troggle.core.models.caves import Area, Cave, Entrance, CaveSlug, EntranceSl
'''Reads all the cave description data by parsing the xml files (stored as e.g. :EXPOWEB:/cave_data/1623-161.html ) '''Reads all the cave description data by parsing the xml files (stored as e.g. :EXPOWEB:/cave_data/1623-161.html )
and creating the various Cave, Entrance and necessary Area objects. and creating the various Cave, Entrance and necessary Area objects.
This is the first import that happens after the dabase is reinitialised. This is the first import that happens after the database is reinitialised.
So is the first thing that creates tables. So is the first thing that creates tables.
BUT in Django 2.0 and later we cannot do any queries on data we have just entered
because this is all happening inside one transaction. Bummer.
django.db.transaction.TransactionManagementError:
An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.
''' '''
todo='''- db Update does not work when a cave id is in the pending list but a proper cave description file exists todo='''
and is being imported. It should work. But currently Django aborts and he file is not read in.
- Cannot use Edit This Page for pendingcaves.txt_edit as Edit This Page is expecting an html file. - Cannot use Edit This Page for pendingcaves.txt_edit as Edit This Page is expecting an html file.
So we will need a separate file-editing capability just for this configuration file ?! So we will need a separate file-editing capability just for this configuration file ?!
- crashes on MariaDB on server when deleting Caves and complains Area needs a non null parent, But this is not true. - crashes on MariaDB in databasereset.py on server when deleting Caves and complains Area needs a non null parent, But this is not true.
The only solution we have found is to let it crash, then stop and restart MariaDB (requires a logon able to sudo) The only solution we have found is to let it crash, then stop and restart MariaDB (requires a logon able to sudo)
and then restart the databasereset.py again. (status as of July 2022) and then restart the databasereset.py again. (status as of July 2022)
''' '''
@ -92,6 +85,15 @@ def do_pending_cave(k, url, area):
''' '''
slug = k slug = k
g = GetCaveLookup()
if slug in g:
message = f" ! {k} cave listed in pendingcaves.txt already exists."
DataIssue.objects.create(parser='caves', message=message, url=url)
print(message)
return
default_note = f"_Survex file found in loser repo but no description in expoweb <br><br><br>\n" default_note = f"_Survex file found in loser repo but no description in expoweb <br><br><br>\n"
default_note += f"INSTRUCTIONS: First open 'This survex file' (link above the CaveView panel) to find the date and info. Then " default_note += f"INSTRUCTIONS: First open 'This survex file' (link above the CaveView panel) to find the date and info. Then "
default_note += f"<br><br>\n\n - (1) search in the survex file for the *ref to find a " default_note += f"<br><br>\n\n - (1) search in the survex file for the *ref to find a "
@ -118,7 +120,7 @@ def do_pending_cave(k, url, area):
cave = Cave( cave = Cave(
unofficial_number = k, unofficial_number = k,
underground_description = "Pending cave write-up - creating as empty object. No XML file available yet.", underground_description = "Pending cave write-up - creating as empty object. No XML file available yet.",
survex_file = f"caves-{area.short_name}/{k}/{k}.svx", survex_file = f"caves-{area.short_name}/{k[5:]}/{k[5:]}.svx",
url = url, url = url,
notes = default_note) notes = default_note)
if cave: if cave:
@ -465,27 +467,6 @@ def readcaves():
print(" - Saving Area 1626") print(" - Saving Area 1626")
area_1626.save() area_1626.save()
print (" - Setting pending caves")
# Do this first, so that these empty entries are overwritten as they get properly created.
for k in pending:
area = area_1623
areanum = k[0:4]
url = areanum + "/" + k[5:] # Note we are not appending the .htm as we are modern folks now.
if areanum == "1623":
area = area_1623
if areanum == "1624":
area = area_1624
if areanum == "1626":
area = area_1626
try:
do_pending_cave(k[5:], url, area)
except:
message = f" ! Error. Cannot create pending cave and entrance, pending-id:{k} in area {areanum}"
DataIssue.objects.create(parser='caves', message=message)
print(message)
raise
with transaction.atomic(): with transaction.atomic():
print(" - settings.CAVEDESCRIPTIONS: ", CAVEDESCRIPTIONS) print(" - settings.CAVEDESCRIPTIONS: ", CAVEDESCRIPTIONS)
@ -506,3 +487,26 @@ def readcaves():
print (" - Setting up all the variously useful alias names") print (" - Setting up all the variously useful alias names")
mycavelookup = GetCaveLookup() mycavelookup = GetCaveLookup()
print (" - Setting pending caves")
# Do this last, so we can detect if they are created and no longer 'pending'
for k in pending:
area = area_1623
areanum = k[0:4]
url = areanum + "/" + k[5:] # Note we are not appending the .htm as we are modern folks now.
if areanum == "1623":
area = area_1623
if areanum == "1624":
area = area_1624
if areanum == "1626":
area = area_1626
try:
do_pending_cave(k, url, area)
except:
message = f" ! Error. Cannot create pending cave and entrance, pending-id:{k} in area {areanum}"
DataIssue.objects.create(parser='caves', message=message)
print(message)
raise

View File

@ -50,7 +50,7 @@ def find_dwg_file(dwgfile, path):
scansfile = scansfilel[0] scansfile = scansfilel[0]
if wallet: if wallet:
dwgfile.manywallets.add(wallet) dwgfile.dwgwallets.add(wallet)
if scansfile: if scansfile:
dwgfile.scans.add(scansfile) dwgfile.scans.add(scansfile)

View File

@ -1,12 +1,15 @@
import sys import sys
import os import os
import subprocess
import types import types
import stat import stat
import csv import csv
import re import re
import datetime import datetime
import shutil, filecmp
from functools import reduce from functools import reduce
from pathlib import Path
import settings import settings
from troggle.core.models.survex import SingleScan, Wallet, DrawingFile from troggle.core.models.survex import SingleScan, Wallet, DrawingFile
@ -18,7 +21,9 @@ from troggle.core.utils import save_carefully, GetListDir
contentsjson = "contents.json" contentsjson = "contents.json"
indexhtml = "walletindex.html" indexhtml = "walletindex.html"
git = settings.GIT
# to do: create a 'low priority' field, so that any such wallet does not appear in summary reports
wallet_blank_json = { wallet_blank_json = {
"cave": "", "cave": "",
"date": "", "date": "",
@ -55,6 +60,22 @@ wallet_blank_html = '''<html><body><H1>Wallet WALLET</H1>
</body></html> </body></html>
''' '''
def CheckEmptyDate(wallet):
'''If date is not set, get it from a linked survex file. If several, pick the earliest.
Maybe also look at filedates for the scans in expofiles/surveyscans/ , but these can be re-set by copying.
'''
return
def CheckEmptyPeople(wallet):
'''If people list is empty, copy them from the survex files: all of them
To be a Troggle model change; a many:many relationship between wallets and people,
as well as being a list in the JSON file (which is the permanent repository). We want the many:many
relationship so that we can filter wallets based on a person.
'''
return
def LoadListScansFile(wallet): def LoadListScansFile(wallet):
gld = [ ] gld = [ ]
# flatten out any directories in these wallet folders - should not be any # flatten out any directories in these wallet folders - should not be any
@ -73,6 +94,44 @@ def LoadListScansFile(wallet):
if c>=10: if c>=10:
print(".", end='') print(".", end='')
c = 0 c = 0
def CopyWalletData(wallet):
'''Copies all the contents.json to a parallel set of folders in the drawings repo
refreshes everything during a ful import, but it shoudl all be up to date as every time
wallet data gets saved it should also be copied across and committed.
'''
year = wallet.walletname[0:4]
destfolder = Path(settings.DRAWINGS_DATA,'walletjson', year, wallet.walletname)
destjson = destfolder / contentsjson
sourcejson = Path(wallet.fpath, contentsjson)
if not os.path.exists(Path(destfolder)):
try:
os.makedirs(destfolder)
print(f' - created folder {destfolder}..')
except PermissionError:
print(f"CANNOT save this JSON file.\nPERMISSIONS incorrectly set on server for this folder {destfolder}. Ask a nerd to fix this.")
if os.path.isfile(sourcejson):
try:
if not os.path.isfile(destjson) or not filecmp.cmp(sourcejson, destjson):
shutil.copy(sourcejson, destjson)
print(f' - Copied {sourcejson} to {destjson}')
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\n' + dr_add.stderr + '\n\n' + dr_add.stdout + '\n\nreturn 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)
else:
# ideally we would commit many chnages to many wallets just once. But most of the time only a couple of files will change.
dr_commit = subprocess.run([git, "commit", "-m", f'Update of {contentsjson} in wallet'], 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\n' + dr_commit.stderr + '\n\n' + dr_commit.stdout + '\n\nreturn code: ' + str(dr_commit.returncode)
message = f'Error code with git on server for this {contentsjson}. File is copied, added to git, but NOT committed.\n\n' + msgdata
print(message)
except PermissionError:
print(f"CANNOT copy this JSON file.\nPERMISSIONS incorrectly set on server for this file {destjson}. Ask a nerd to fix this.")
# this iterates through the scans directories (either here or on the remote server) # this iterates through the scans directories (either here or on the remote server)
@ -109,17 +168,20 @@ def load_all_scans():
if fisdir: if fisdir:
wallet = Wallet(fpath=fpath, walletname=walletname) wallet = Wallet(fpath=fpath, walletname=walletname)
# this is where we should load the contents.json for people so we can report on them later # this is where we should load the contents.json for people so we can report on them later
# this is where we shoudl record the year explicitly # this is where we should record the year explicitly
# line 347 of view/uploads.py and needs refactoring for loading contentsjson # line 347 of view/uploads.py and needs refactoring for loading contentsjson
wallet.save() wallet.save()
LoadListScansFile(wallet) LoadListScansFile(wallet)
CheckEmptyDate(wallet)
CheckEmptyPeople(wallet)
CopyWalletData(wallet)
# what is this? # what is this?
elif walletname != "thumbs": # elif walletname != "thumbs":
print(f'\n - Wallet {walletname} - {fpath}') # print(f'\n - Wallet {walletname} - {fpath}')
wallet = Wallet(fpath=fpath, walletname=walletname) # wallet = Wallet(fpath=fpath, walletname=walletname)
wallet.save() # wallet.save()
LoadListScansFile(wallet) # LoadListScansFile(wallet)
else: else:
print(f'\n - IGNORE {walletname} - {fpath}') print(f'\n - IGNORE {walletname} - {fpath}')

View File

@ -37,7 +37,6 @@ todo = '''Also walk the entire tree in the :loser: repo looking for unconnected
- LoadSurvexFile() Creates a new current survexfile and valid .survexdirectory - LoadSurvexFile() Creates a new current survexfile and valid .survexdirectory
The survexblock passed-in is not necessarily the parent. FIX THIS. The survexblock passed-in is not necessarily the parent. FIX THIS.
- rx_qm recognises only simple survey point ids. EXTEND to cover more naming formats and test fully for 2023
''' '''
survexblockroot = None survexblockroot = None
ROOTBLOCK = "rootblock" ROOTBLOCK = "rootblock"
@ -131,8 +130,8 @@ class LoadingSurvex():
rx_cave = re.compile(r'(?i)caves-(\d\d\d\d)/([-\d\w]+|\d\d\d\d-?\w+-\d+)') rx_cave = re.compile(r'(?i)caves-(\d\d\d\d)/([-\d\w]+|\d\d\d\d-?\w+-\d+)')
rx_comment = re.compile(r'([^;]*?)\s*(?:;\s*(.*))?\n?$') rx_comment = re.compile(r'([^;]*?)\s*(?:;\s*(.*))?\n?$')
rx_comminc = re.compile(r'(?i)^\*include[\s]*([-\w/]*).*$') # inserted by linear collate ;*include rx_comminc = re.compile(r'(?i)^\|\*include[\s]*([-\w/]*).*$') # inserted by linear collate ;*include
rx_commcni = re.compile(r'(?i)^\*edulcni[\s]*([-\w/]*).*$') # inserted by linear collate ;*edulcni rx_commcni = re.compile(r'(?i)^\|\*edulcni[\s]*([-\w/]*).*$') # inserted by linear collate ;*edulcni
rx_include = re.compile(r'(?i)^\s*(\*include[\s].*)$') rx_include = re.compile(r'(?i)^\s*(\*include[\s].*)$')
rx_commref = re.compile(r'(?i)^\s*ref(?:erence)?[\s.:]*(\d+)\s*#\s*(X)?\s*(\d+)') rx_commref = re.compile(r'(?i)^\s*ref(?:erence)?[\s.:]*(\d+)\s*#\s*(X)?\s*(\d+)')
rx_wallet = re.compile(r'(?i)^\s*wallet[\s.:]*(\d+)\s*#\s*(X)?\s*(\d+)') rx_wallet = re.compile(r'(?i)^\s*wallet[\s.:]*(\d+)\s*#\s*(X)?\s*(\d+)')
@ -178,13 +177,14 @@ class LoadingSurvex():
callcount = 0 callcount = 0
caverncount = 0 caverncount = 0
ignoreprefix = ["surface", "kataster", "fixedpts", "gpx"] ignoreprefix = ["surface", "kataster", "fixedpts", "gpx"]
ignorenoncave = ["caves-1623", "caves-1623/2007-neu"] ignorenoncave = ["caves-1623", "caves-1626", "caves-1623/2007-neu"]
includedfilename ="" includedfilename =""
currentsurvexblock = None currentsurvexblock = None
currentsurvexfile = None currentsurvexfile = None
currentcave = None currentcave = None
caverndate = None caverndate = None
currentpersonexped = [] currentpersonexped = []
pending = []
def __init__(self): def __init__(self):
self.caveslist = GetCaveLookup() self.caveslist = GetCaveLookup()
@ -690,9 +690,7 @@ class LoadingSurvex():
def IdentifyCave(self, cavepath): def IdentifyCave(self, cavepath):
if cavepath.lower() in self.caveslist: if cavepath.lower() in self.caveslist:
return self.caveslist[cavepath.lower()] return self.caveslist[cavepath.lower()]
# TO DO - some of this is already done in generating self.caveslist so simplify this # TO DO - this predates the big revision to Gcavelookup so look at this again carefully
# esp. as it is in a loop.
# TO DO recognise cave if different name, e.g. gruenstein == 281
path_match = self.rx_cave.search(cavepath) path_match = self.rx_cave.search(cavepath)
if path_match: if path_match:
sluggy = '{}-{}'.format(path_match.group(1), path_match.group(2)) sluggy = '{}-{}'.format(path_match.group(1), path_match.group(2))
@ -724,8 +722,17 @@ class LoadingSurvex():
def ReportNonCaveIncludes(self, headpath, includelabel, depth): def ReportNonCaveIncludes(self, headpath, includelabel, depth):
"""Ignore surface, kataser and gpx *include survex files """Ignore surface, kataser and gpx *include survex files
""" """
if not self.pending:
self.pending = set()
fpending = Path(settings.CAVEDESCRIPTIONS, "pendingcaves.txt")
if fpending.is_file():
with open(fpending, "r") as fo:
cids = fo.readlines()
for cid in cids:
self.pending.add(cid.rstrip('\n').upper())
if headpath in self.ignorenoncave: if headpath in self.ignorenoncave:
#message = f" - {headpath} is <ignorenoncave> (while creating '{includelabel}' sfile & sdirectory)" message = f" - {headpath} is <ignorenoncave> (while creating '{includelabel}' sfile & sdirectory)"
#print("\n"+message) #print("\n"+message)
#print("\n"+message,file=sys.stderr) #print("\n"+message,file=sys.stderr)
return return
@ -735,20 +742,26 @@ class LoadingSurvex():
# print("\n"+message) # print("\n"+message)
# print("\n"+message,file=sys.stderr) # print("\n"+message,file=sys.stderr)
return return
message = f" ! Error: FAILURE '{headpath}' while creating '{includelabel}' at depth:[{depth}]. Not a cave or in the ignore list:'{self.ignoreprefix}'" caveid = f'{headpath[6:10]}-{headpath[11:]}'.upper()
# getting this triggered for gpx/2018 (cavern error) but not for gpx/2017 (no content). if caveid in self.pending:
# Yes we didn't find this cave, but we know it is a pending one. So not an error.
# print(f'! ALREADY PENDING {caveid}',file=sys.stderr)
return
message = f" ! Error: not a cave nor ignorable. headpath:'{headpath}' while parsing '{includelabel=}.svx' at depth:[{len(depth)}]. ignore prefix list:'{self.ignoreprefix}'"
print("\n"+message) print("\n"+message)
print("\n"+message,file=sys.stderr) print("\n"+message,file=sys.stderr)
DataIssue.objects.create(parser='survex', message=message, url=get_offending_filename(headpath)) DataIssue.objects.create(parser='survex', message=message, url=get_offending_filename(headpath))
print(f' # datastack in LoadSurvexFile:{includelabel} type:', end="",file=sys.stderr) print(f' # datastack in LoadSurvexFile:{includelabel} type:', end="",file=sys.stderr)
for dict in self.datastack: for dict in self.datastack:
print(f'{dict["type"].upper()} ', end="",file=sys.stderr) print(f'<{dict["type"].upper()} >', end="",file=sys.stderr)
def LoadSurvexFile(self, svxid): def LoadSurvexFile(self, svxid):
"""Creates SurvexFile in the database, and SurvexDirectory if needed """Creates SurvexFile in the database, and SurvexDirectory if needed
with links to 'cave' with links to 'cave'
Creates a new current survexfile and valid .survexdirectory Creates a new current survexfile and valid .survexdirectory
Inspects the parent folder of the survexfile and uses that to decide if this is a cave we know
The survexblock passed-in is not necessarily the parent. FIX THIS. The survexblock passed-in is not necessarily the parent. FIX THIS.
""" """
if debugprint: if debugprint:
@ -780,7 +793,7 @@ class LoadingSurvex():
if cave: if cave:
newdirectory.cave = cave newdirectory.cave = cave
newfile.cave = cave newfile.cave = cave
# print(f"\n - New directory {newdirectory} for cave {newdirectory.cave}",file=sys.stderr) # print(f"\n - New directory '{newdirectory}' for cave '{cave}'",file=sys.stderr)
else: # probably a surface survey, or a cave in a new area e.g. 1624 not previously managed, and not in the pending list else: # probably a surface survey, or a cave in a new area e.g. 1624 not previously managed, and not in the pending list
self.ReportNonCaveIncludes(headpath, svxid, depth) self.ReportNonCaveIncludes(headpath, svxid, depth)
@ -862,6 +875,7 @@ class LoadingSurvex():
included = self.rx_comminc.match(comment) included = self.rx_comminc.match(comment)
# ;*include means 'we have been included'; whereas *include means 'proceed to include' # ;*include means 'we have been included'; whereas *include means 'proceed to include'
# bug, If the original survex file contians the line ;*include then we pick it up ! So fix our special code to be ;|*include
if included: if included:
self.ProcessIncludeLine(included) self.ProcessIncludeLine(included)
@ -1211,7 +1225,7 @@ class LoadingSurvex():
#-------------------------------------------------------- #--------------------------------------------------------
self.depthinclude += 1 self.depthinclude += 1
fininclude = open(fullpath,'r') fininclude = open(fullpath,'r')
fcollate.write(";*include {}\n".format(includepath)) fcollate.write(";|*include {}\n".format(includepath))
flinear.write("{:2} {} *include {}\n".format(self.depthinclude, indent, includepath)) flinear.write("{:2} {} *include {}\n".format(self.depthinclude, indent, includepath))
push = includepath.lower() push = includepath.lower()
self.includestack.append(push) self.includestack.append(push)
@ -1226,7 +1240,7 @@ class LoadingSurvex():
print(message,file=sys.stderr) print(message,file=sys.stderr)
DataIssue.objects.create(parser='survex', message=message, url=get_offending_filename(path)) DataIssue.objects.create(parser='survex', message=message, url=get_offending_filename(path))
flinear.write("{:2} {} *edulcni {}\n".format(self.depthinclude, indent, pop)) flinear.write("{:2} {} *edulcni {}\n".format(self.depthinclude, indent, pop))
fcollate.write(";*edulcni {}\n".format(pop)) fcollate.write(";|*edulcni {}\n".format(pop))
fininclude.close() fininclude.close()
self.depthinclude -= 1 self.depthinclude -= 1
#-------------------------------------------------------- #--------------------------------------------------------

View File

@ -31,13 +31,13 @@
<a href="{% url "survexcavessingle" "359" %}">359</a> | <a href="{% url "survexcavessingle" "359" %}">359</a> |
<a href="/survexfile/">Survex</a> | <a href="/survexfile/">Survex</a> |
<a href="{% url "survexcaveslist" %}">All Survex</a> | <a href="{% url "survexcaveslist" %}">All Survex</a> |
<a href="{% url "allwallets" %}">Scans</a> | <a href="{% url "allscans" %}">Scans</a> |
<a href="{% url "scanupload" '2022:01' %}">Upload Scans</a> | <a href="{% url "scanupload" '2022:01' %}">Upload Scans</a> |
<a href="{% url "dwgallfiles" %}">Drawings</a> | <a href="{% url "dwgallfiles" %}">Drawings</a> |
<a href="{% url "dwgupload" %}">Upload Drawings</a> | <a href="{% url "dwgupload" %}">Upload Drawings</a> |
<a href="{% url "photoupload" %}">Upload Photos</a> | <a href="{% url "photoupload" %}">Upload Photos</a> |
<a href="/1623/290/290.html">290 (FGH)</a> | <a href="/1623/290/290">290 (FGH)</a> |
<a href="/1626/359/359.html">359 (Homecoming)</a> | <a href="/1626/359/359">359 (Homecoming)</a> |
<br> <br>
<a href="{% url "dataissues" %}">Data Issues</a> | <a href="{% url "dataissues" %}">Data Issues</a> |
@ -48,15 +48,15 @@
<a id="folklink" href="/folk">expoers</a> | <a id="folklink" href="/folk">expoers</a> |
<a id="caversLink" href="{% url "notablepersons" %}">survey lengths</a> | <a id="caversLink" href="{% url "notablepersons" %}">survey lengths</a> |
<a href="{% url "stats" %}">statistics</a> | <a href="{% url "stats" %}">statistics</a> |
<a href="{% url "expedition" 2018 %}">Expo2018</a> | <a href="/wallets/year/2019">Wallets(2019)</a> |
<a href="{% url "expedition" 2019 %}">Expo2019</a> | <a href="{% url "expedition" 2019 %}">Expo(2019)</a> |
<a href="{% url "controlpanel" %}">import/export</a> | <a href="{% url "controlpanel" %}">import/export</a> |
<a href="/admin/">Django admin</a> <a href="/admin/">Django admin</a>
</div> </div>
<div id="nav"> <div id="nav">
{% block nav %} {% block nav %}
<!-- Use id="nav" for the left side menu --> <!-- Not used any more? -->
{% endblock %} {% endblock %}
</div> </div>
@ -67,7 +67,6 @@
<div id="related"> <div id="related">
{% block related %} {% block related %}
{% endblock %} {% endblock %}
</div> </div>
{% block content %} {% block content %}

View File

@ -4,29 +4,41 @@
{% block content %} {% block content %}
<h3>Survey scans folders (wallets) for <a href="/{{cave.url}}">{{cave}}</a></h3> <h3>Wallets for <a href="/{{cave.url}}">{{cave}}</a> {{cave.official_name|safe}}</h3>
<p>Each wallet contains the scanned original in-cave survey notes and sketches of <p>Each wallet contains the scanned original in-cave survey notes and sketches of
plans and elevations. It also contains scans of centre-line survex output on which plans and elevations. It also contains scans of centre-line survex output on which
hand-drawn passage sections are drawn. These hand-drawn passages will eventually be hand-drawn passage sections are drawn. These hand-drawn passages will eventually be
traced to produce Tunnel or Therion drawings and eventually the final complete cave survey. traced to produce Tunnel or Therion drawings and eventually the final complete cave survey.
<p>This lists all the files in a wallet, some of which may not be for this specific cave.
<p>See also wallets
<ul>
<li>per year, e.g. <a href="/wallets/year/2019">2019</a>
<li>per person, e.g. <a href="/wallets/person/MichaelSargent">Michael Sargent</a>
</ul>
{% include 'wallet_table.html' %}
<br />
<table width=95%> <table width=95%>
<tr><th>Scans folder</th><th>Files</th><th>Survex blocks</th><th>Cave</th></tr> <tr><th>Wallet</th><th width=8%>Wallet Date</th><th>Wallet Name</th><th>People</th><th>Scans</th><th>Survex blocks</th><th>Drawings using these scans</th></tr>
{% for scanswallet in manywallets|dictsort:"walletname" %} {% for wallet in manywallets|dictsort:"walletname" %}
<tr> <tr>
<td style="padding:2px"><a href="{{scanswallet.get_absolute_url}}">{{scanswallet.walletname}}</a></td> <td style="padding:2px"><a href="{{wallet.get_absolute_url}}">{{wallet.walletname}}</a></td>
<td align="right" style="padding:2px">{{scanswallet.singlescan_set.all|length}}</td>
<td style="padding:2px">{{wallet.date}}</td>
<td style="padding:2px">{{wallet.name}}</td>
<td style="padding:2px">{{wallet.persons}}</td>
<td align="center" style="padding:2px"><a href="{{wallet.get_absolute_url}}">{{wallet.singlescan_set.all|length}}</a></td>
<td style="padding:2px"> <td style="padding:2px">
{% for survexblock in scanswallet.survexblock_set.all %} {% for survexblock in wallet.survexblock_set.all %}
<a href="{% url "svx" survexblock.survexfile.path %}">{{survexblock}}</a> <a href="{% url "svx" survexblock.survexfile.path %}">{{survexblock}}</a>
{% endfor %} {% endfor %}
</td> </td>
<td style="padding:2px">
{% for survexblock in scanswallet.survexblock_set.all %}
{% ifchanged survexblock.survexfile.cave %}
<a href="/{{survexblock.survexfile.cave.url}}">/{{survexblock.survexfile.cave.slug}}</a>
{% endifchanged %}
<td style="padding:2px; font-size: 70%;">
{% for drawing in wallet.drawingfile_set.all %}
<a href="{% url "dwgfilesingle" drawing.dwgpath %}">{{drawing.dwgpath}}</a><br>
{% empty %}
(no Tunnel drawings found: but there might be Therion drawings)
{% endfor %} {% endfor %}
</td> </td>
</tr> </tr>

View File

@ -6,7 +6,7 @@
<h1>Loading data from files: Issues arising that need attention</h1> <h1>Loading data from files: Issues arising that need attention</h1>
<p> <p>
This is work in progress (June 2022).The URL links to the offending objects are enabled on only some types of fault as yet. This is work in progress.The URL links to the offending objects are enabled on only some types of fault as yet.
<p> <p>
See the See the
<a href="/handbook/computing/todo-data.html">Data Management To Do list</a> as well as these import/parsing issues. <a href="/handbook/computing/todo-data.html">Data Management To Do list</a> as well as these import/parsing issues.

View File

@ -13,7 +13,7 @@
<td align="right" style="padding:2px">{{dwgfile.npaths}}</td> <td align="right" style="padding:2px">{{dwgfile.npaths}}</td>
<td style="padding:2px"> <td style="padding:2px">
{% for scanswallet in dwgfile.manywallets.all %} {% for scanswallet in dwgfile.dwgwallets.all %}
<a href="{{scanswallet.get_absolute_url}}">{{scanswallet.walletname}}</a> <a href="{{scanswallet.get_absolute_url}}">{{scanswallet.walletname}}</a>
{% endfor %} {% endfor %}
</td> </td>

View File

@ -9,6 +9,12 @@
plans and elevations. It also contains scans of centre-line survex output on which plans and elevations. It also contains scans of centre-line survex output on which
hand-drawn passage sections are drawn. These hand-drawn passages will eventually be hand-drawn passage sections are drawn. These hand-drawn passages will eventually be
traced to produce Tunnel or Therion drawings and eventually the final complete cave survey. traced to produce Tunnel or Therion drawings and eventually the final complete cave survey.
<p>See also wallets
<ul>
<li>per year, e.g. <a href="/wallets/year/2019">2019</a>
<li>per cave, e.g. <a href="/cave/scans/1623-204">1623/204</a>
<li>per person, e.g. <a href="/wallets/person/MichaelSargent">Michael Sargent</a>
</ul>
<!-- This should all be restructured to use .prefetch_related() and .select_related() <!-- This should all be restructured to use .prefetch_related() and .select_related()
see https://docs.djangoproject.com/en/3.2/ref/models/querysets/#prefetch-related see https://docs.djangoproject.com/en/3.2/ref/models/querysets/#prefetch-related

View File

@ -27,6 +27,9 @@
</ul> </ul>
</p> </p>
<h3>Surveys done</h3>
Wallets and surveys mentioning <a href="/wallets/person/{{person}}">{{person}}</a>
{% if person.blurb %} {% if person.blurb %}
{{person.blurb|safe}} {{person.blurb|safe}}
{% else %} {% else %}

View File

@ -0,0 +1,50 @@
{% extends "base.html" %}
{% block title %}One Person Survey scans folders (wallets){% endblock %}
{% block content %}
<h3>Wallets for <a href="{{person.get_absolute_url}}">{{person}}</a> </h3>
<p>Each wallet contains the scanned original in-cave survey notes and sketches of
plans and elevations. It also contains scans of centre-line survex output on which
hand-drawn passage sections are drawn. These hand-drawn passages will eventually be
traced to produce Tunnel or Therion drawings and eventually the final complete cave survey.
<p>See also wallets
<ul>
<li>per year, e.g. <a href="/wallets/year/2019">2019</a>
<li>per cave, e.g. <a href="/cave/scans/1623-161">1623/161</a>
</ul>
{% include 'wallet_table.html' %}
<br />
<table width=95%>
<tr><th>Wallet</th><th width=8%>Wallet Date</th><th>Wallet Name</th><th width=15%>Other People</th><th>Cave</th><th>Scans</th><th>Survex blocks</th><th>Drawings using these scans</th></tr>
{% for wallet in manywallets|dictsort:"walletname" %}
<tr>
<td style="padding:2px"><a href="{{wallet.get_absolute_url}}">{{wallet.walletname}}</a></td>
<td style="padding:2px" >{{wallet.date}}</td>
<td style="padding:2px">{{wallet.name}}</td>
<td style="padding:2px">{{wallet.persons}}</td>
<td style="padding:2px">{{wallet.cave}}</td>
<td align="center" style="padding:2px"><a href="{{wallet.get_absolute_url}}">{{wallet.singlescan_set.all|length}}</a></td>
<td style="padding:2px">
{% for survexblock in wallet.survexblock_set.all %}
<a href="{% url "svx" survexblock.survexfile.path %}">{{survexblock}}</a>
{% endfor %}
</td>
<td style="padding:2px; font-size: 70%;">
{% for drawing in wallet.drawingfile_set.all %}
<a href="{% url "dwgfilesingle" drawing.dwgpath %}">{{drawing.dwgpath}}</a><br>
{% empty %}
(no Tunnel drawings found: but there might be Therion drawings)
{% endfor %}
</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -10,6 +10,7 @@
All the processing to extract the survex subdriectories and survex files is done in this template --> All the processing to extract the survex subdriectories and survex files is done in this template -->
<p>Cave description: <a href="/{{cave.url}}">{{cave.url}}</a> <p>Cave description: <a href="/{{cave.url}}">{{cave.url}}</a>
<p>Wallets: <a href="/cave/scans/{{cave|safe}}">{{cave|safe}}</a>
</p> </p>
<p> <p>
{% for survexdirectory in cave.survexdirectory_set.all %} {% for survexdirectory in cave.survexdirectory_set.all %}

View File

@ -0,0 +1,40 @@
<table width=95%>
<tr><th>Wallet</th><th width=8%>Wallet Date</th><th>Cave</th><th>Wallet Name</th>
<!-- survex file-->
<th style="font-family: monospace; font-size: 150%;" title="Survex data">S</th>
<th style="font-family: monospace; font-size: 150%;" title="Survex Cave Description">C</th>
<th style="font-family: monospace; font-size: 150%;" title="Survex QMs">Q</th>
<!-- scanned-->
<th style="font-family: monospace; font-size: 150%;" title="Notes">N</th>
<th style="font-family: monospace; font-size: 150%;" title="Plan">P</th>
<th style="font-family: monospace; font-size: 150%;" title="Elevation">E</th>
<th style="font-family: monospace; font-size: 150%;" title="Tunnel or Therion">T</th>
<th style="font-family: monospace; font-size: 150%;" title="Website updated">W</th>
</tr>
{% for wallet in manywallets|dictsort:"walletname" %}
<tr>
<td style="padding:2px"><a href="{{wallet.get_absolute_url}}">{{wallet.walletname}}</a></td>
<td style="padding:2px" >{{wallet.date}}</td>
<td style="padding:2px">{{wallet.cave}}</td>
<td style="padding:2px">{{wallet.name}}</td>
<td style="padding:1px; background-color:{{wallet.ticks.S}}">&nbsp;</td>
<td style="padding:1px; background-color:{{wallet.ticks.C}}">&nbsp;</td>
<td style="padding:1px; background-color:{{wallet.ticks.Q}}">&nbsp;</td>
<td style="padding:1px; background-color:{{wallet.ticks.N}}">&nbsp;</td>
<td style="padding:1px; background-color:{{wallet.ticks.P}}">&nbsp;</td>
<td style="padding:1px; background-color:{{wallet.ticks.E}}">&nbsp;</td>
<td style="padding:1px; background-color:{{wallet.ticks.T}}">&nbsp;</td>
<td style="padding:1px; background-color:{{wallet.ticks.W}}">&nbsp;</td>
</tr>
{% endfor %}
</table>

View File

@ -129,7 +129,7 @@
title="Date of the trip in ISO format: 2020-08-17" title="Date of the trip in ISO format: 2020-08-17"
placeholder="{{date}}" value="{{date}}" required /> placeholder="{{date}}" value="{{date}}" required />
<br> <br>
<label for="cave">Cave ID</label> <label for="cave">Cave ID (only needed if no survex file yet)</label>
<input <input
label = "Cave" name = "cave" size="12" label = "Cave" name = "cave" size="12"
title="Cave id e.g. 2017-DM-01 or 1623/256" title="Cave id e.g. 2017-DM-01 or 1623/256"
@ -156,10 +156,10 @@
<label for="elevd">Elevation drawn ?</label> <label for="elevd">Elevation drawn ?</label>
<input type="checkbox" name="elevd" id="elevd" value="True" {% if "elev drawn" in checked %}checked{% endif %}> <input type="checkbox" name="elevd" id="elevd" value="True" {% if "elev drawn" in checked %}checked{% endif %}>
<br> <br>
<label for="descriptionw">Cave description written ?</label> <label for="descriptionw">Cave description written (or nothing recorded) ?</label>
<input type="checkbox" name="descriptionw" id="descriptionw" value="True" {% if "description written" in checked %}checked{% endif %}> <input type="checkbox" name="descriptionw" id="descriptionw" value="True" {% if "description written" in checked %}checked{% endif %}>
<br> <br>
<label for="qmsw">QMs written ?</label> <label for="qmsw">QMs written (or none seen) ?</label>
<input type="checkbox" name="qmsw" id="qmsw" value="True" {% if "qms written" in checked %}checked{% endif %}> <input type="checkbox" name="qmsw" id="qmsw" value="True" {% if "qms written" in checked %}checked{% endif %}>
<br> <br>
<label for="websiteupt">Website updated ?</label> <label for="websiteupt">Website updated ?</label>
@ -174,7 +174,7 @@
title="List of people on the survey trip" title="List of people on the survey trip"
placeholder="{{people}}" value="{{people}}" /> placeholder="{{people}}" value="{{people}}" />
<br> <br>
<label for="url">URL of cave description</label> <label for="url">URL of survey area (only needed if not a cave)</label>
<input <input
label = "URL" name = "url" size ="{{urlsize}}" label = "URL" name = "url" size ="{{urlsize}}"
title="URL of cave description, e.g. /1623/264/264.html" title="URL of cave description, e.g. /1623/264/264.html"

View File

@ -0,0 +1,49 @@
{% extends "base.html" %}
{% block title %}One Year Survey scans folders (wallets){% endblock %}
{% block content %}
<h3>Wallets for {{year}} </h3>
<p>Each wallet contains the scanned original in-cave survey notes and sketches of
plans and elevations. It also contains scans of centre-line survex output on which
hand-drawn passage sections are drawn. These hand-drawn passages will eventually be
traced to produce Tunnel or Therion drawings and eventually the final complete cave survey.
<p>See also wallets
<ul>
<li>per cave, e.g. <a href="/cave/scans/1623-161">1623/161</a>
<li>per person, e.g. <a href="/wallets/person/MichaelSargent">Michael Sargent</a>
</ul>
{% include 'wallet_table.html' %}
<br />
<table width=95%>
<tr><th>Wallet</th><th width=8%>Wallet Date</th><th>Wallet Name</th><th>People</th><th>Cave</th><th>Scans</th><th>Survex blocks</th><th>Drawings using these scans</th></tr>
{% for wallet in manywallets|dictsort:"walletname" %}
<tr>
<td style="padding:2px"><a href="{{wallet.get_absolute_url}}">{{wallet.walletname}}</a></td>
<td style="padding:2px">{{wallet.date}}</td>
<td style="padding:2px">{{wallet.name}}</td>
<td style="padding:2px">{{wallet.persons}}</td>
<td style="padding:2px">{{wallet.cave}}</td>
<td align="center" style="padding:2px"><a href="{{wallet.get_absolute_url}}">{{wallet.singlescan_set.all|length}}</a></td>
<td style="padding:2px">
{% for survexblock in wallet.survexblock_set.all %}
<a href="{% url "svx" survexblock.survexfile.path %}">{{survexblock}}</a>
{% endfor %}
</td>
<td style="padding:2px; font-size: 70%;">
{% for drawing in wallet.drawingfile_set.all %}
<a href="{% url "dwgfilesingle" drawing.dwgpath %}">{{drawing.dwgpath}}</a><br>
{% empty %}
(no Tunnel drawings found: but there might be Therion drawings)
{% endfor %}
</td>
</tr>
{% endfor %}
</table>
{% endblock %}

15
urls.py
View File

@ -8,7 +8,7 @@ from django.contrib import auth
from django.urls import path, reverse, resolve from django.urls import path, reverse, resolve
from troggle.core.views import statistics, survex from troggle.core.views import statistics, survex
from troggle.core.views.scans import scansingle, allwallets, cavewallets from troggle.core.views.scans import scansingle, allscans, cavewallets, walletslistyear, walletslistperson
from troggle.core.views.drawings import dwgallfiles, dwgfilesingle from troggle.core.views.drawings import dwgallfiles, dwgfilesingle
from troggle.core.views.uploads import dwgupload, scanupload, photoupload from troggle.core.views.uploads import dwgupload, scanupload, photoupload
from troggle.core.views.other import troggle404, frontpage, todos, controlpanel, frontpage from troggle.core.views.other import troggle404, frontpage, todos, controlpanel, frontpage
@ -168,13 +168,18 @@ trogglepatterns = [
path('survexfile/<path:survex_cave>', survex.survexcavesingle, name="survexcavessingle"), path('survexfile/<path:survex_cave>', survex.survexcavesingle, name="survexcavessingle"),
# The survey scans in the wallets. This short-cuts SCANS_URL which is not actually used anywhere! # The survey scans in the wallets. This short-cuts SCANS_URL which is not used anymore and is defunct
path('survey_scans/', allwallets, name="allwallets"), path('survey_scans/', allscans, name="allscans"), # all the scans in all wallets
path('survey_scans/<path:path>/', scanupload, name="singlewallet"), # replaced singlewallet() path('survey_scans/<path:path>/', scanupload, name="singlewallet"), # replaced singlewallet()
path('survey_scans/<path:path>/<file>', scansingle, name="scansingle"), # works, but html href goes direct to /expofiles/ too path('survey_scans/<path:path>/<file>', scansingle, name="scansingle"), # works, but html href goes direct to /expofiles/ too
re_path(r'^cave/scans/(?P<cave_id>[^/]+)$', cavewallets, name="cavewallets"), # like allwallets, but for just one cave path('cave/scans/<slug:caveid>', cavewallets, name="cavewallets"), # like allscans, but for just one cave
# The tunnel and therion drawings files pages # The data about the wallets themselves, not the scans inside tehm
path('wallets/year/<int:year>', walletslistyear, name="walletslistyear"), # wallets that are for a specific year, as an integer '1985'
re_path('wallets/person/(?P<first_name>[A-Z]*[a-z\-\'&;]*)[^a-zA-Z]*(?P<last_name>[a-z\-\']*[^a-zA-Z]*[\-]*[A-Z]*[a-zA-Z\-&;]*)/?', walletslistperson, name="walletslistperson"),
# The tunnel and therion drawings files pageswalletslistcave
path('dwgfiles', dwgallfiles, name="dwgallfiles"), path('dwgfiles', dwgallfiles, name="dwgallfiles"),
path('dwgfiles/', dwgallfiles, name="dwgallfiles"), path('dwgfiles/', dwgallfiles, name="dwgallfiles"),
path('dwgdataraw/<path:path>', dwgfilesingle, name="dwgfilesingle"), path('dwgdataraw/<path:path>', dwgfilesingle, name="dwgfilesingle"),