2
0
mirror of https://expo.survex.com/repositories/troggle/.git synced 2024-11-22 15:21:52 +00:00

QMs now have working url to survexfile & tick description

This commit is contained in:
Philip Sargent 2022-07-20 14:44:56 +03:00
parent 2a7f1506c9
commit 549c1649b4
6 changed files with 91 additions and 54 deletions

View File

@ -4,6 +4,7 @@ import datetime
import re import re
import json import json
import subprocess import subprocess
import operator
from collections import defaultdict from collections import defaultdict
from pathlib import Path from pathlib import Path
@ -149,7 +150,21 @@ class Cave(TroggleModel):
return str(self.slug()) return str(self.slug())
def get_QMs(self): def get_QMs(self):
return QM.objects.filter(cave=self) '''Searches for all QMs that reference this cave. Probably a better Django way to do this
'''
qms = QM.objects.filter(cave=self).order_by('expoyear', 'block__date') # a QuerySet, see https://docs.djangoproject.com/en/4.0/ref/models/querysets/#order-by
return qms # a QuerySet
# undated = []
# dated = []
# qms = QM.objects.filter(cave=self) # a QuerySet
# for q in qms:
# if q.block:
# dated.append(q)
# else:
# undated.append(q)
# sortedqms = sorted(dated, key=operator.attrgetter('block.date')) # sort by date of survexblock the QM was defined in
# orderedqms = sorted(undated, key=operator.attrgetter('expoyear')) # sort by date of expoyear
# return orderedqms + sortedqms # a list, NOT a QuerySet
# def new_QM_number(self, year=datetime.date.today().year): # def new_QM_number(self, year=datetime.date.today().year):
@ -456,8 +471,8 @@ class QM(TroggleModel):
blockname=models.TextField(blank=True,null=True) # NB truncated copy of survexblock name blockname=models.TextField(blank=True,null=True) # NB truncated copy of survexblock name
expoyear = models.CharField(max_length=4,blank=True, null=True) # could change to datetime if logbooks similarly chnaged expoyear = models.CharField(max_length=4,blank=True, null=True) # could change to datetime if logbooks similarly chnaged
found_by = models.ForeignKey(LogbookEntry, related_name='QMs_found',blank=True, null=True,on_delete=models.SET_NULL ) found_by = models.ForeignKey(LogbookEntry, related_name='QMs_found',blank=True, null=True,on_delete=models.SET_NULL )
ticked = models.BooleanField(default=False) # for ticked QMs not atatched to a logbook entry ticked = models.BooleanField(default=False) # for ticked QMs not attached to a logbook entry, should imply completion_description has text
ticked_off_by = models.ForeignKey(LogbookEntry, related_name='QMs_ticked_off',blank=True, null=True,on_delete=models.SET_NULL) ticked_off_by = models.ForeignKey(LogbookEntry, related_name='QMs_ticked_off',blank=True, null=True,on_delete=models.SET_NULL) # unused, ever?!
number = models.IntegerField(help_text="this is the sequential number in the year, only unique for CSV imports", ) number = models.IntegerField(help_text="this is the sequential number in the year, only unique for CSV imports", )
GRADE_CHOICES=( GRADE_CHOICES=(
('A', 'A: Large obvious lead'), ('A', 'A: Large obvious lead'),
@ -494,8 +509,19 @@ class QM(TroggleModel):
#return f'{self.cave.slug()[5:]}-{self.expoyear}-{self.blockname}{self.number}{self.grade}' #return f'{self.cave.slug()[5:]}-{self.expoyear}-{self.blockname}{self.number}{self.grade}'
return f'{cavestr}-{expoyearstr}-{blocknamestr}{self.number}{self.grade}' return f'{cavestr}-{expoyearstr}-{blocknamestr}{self.number}{self.grade}'
def newslug(self): def get_completion_url(self):
'''assumes html file named is in same folder as cave description file
'''
cd = None
if self.completion_description:
try:
dir = Path(self.cave.url).parent
cd = dir / self.completion_description
except:
cd = None
return cd
def newslug(self):
qmslug = f'{str(self.cave)}-{self.expoyear}-{self.blockname}{self.number}{self.grade}' qmslug = f'{str(self.cave)}-{self.expoyear}-{self.blockname}{self.number}{self.grade}'
return qmslug return qmslug

View File

@ -272,13 +272,11 @@ def cavepage(request, karea, subpath):
r = rendercave(request, cave, cave.slug()) r = rendercave(request, cave, cave.slug())
return r return r
except NoReverseMatch: except NoReverseMatch:
raise
except:
raise
message = f'Failed to render cave: {kpath} (it does exist and is unique) because of a Django URL resolution error. Check urls.py.' message = f'Failed to render cave: {kpath} (it does exist and is unique) because of a Django URL resolution error. Check urls.py.'
return render(request,'errors/generic.html', {'message': message}) return render(request,'errors/generic.html', {'message': message})
except:
# return rendercave(request, cave, cave.slug(), cave_id=cave_id) # anything else is a new problem. Add in specific error messages here as we discover new types of error
raise
def caveEntrance(request, slug): def caveEntrance(request, slug):
try: try:
@ -482,6 +480,7 @@ def qm(request,cave_id,qm_id,year,grade=None, blockname=None):
290 has several QMS with the same number, grade, year (2108) and first 8 chars of the survexblock. This crashes things. 290 has several QMS with the same number, grade, year (2108) and first 8 chars of the survexblock. This crashes things.
''' '''
year=int(year) year=int(year)
if blockname == '': if blockname == '':
@ -492,12 +491,13 @@ def qm(request,cave_id,qm_id,year,grade=None, blockname=None):
qm=manyqms.get(number=qm_id,expoyear=year) qm=manyqms.get(number=qm_id,expoyear=year)
return render(request,'qm.html', {'qm': qm}) return render(request,'qm.html', {'qm': qm})
except QM.DoesNotExist: except QM.DoesNotExist:
raise #raise
return render(request,'errors/badslug.html', {'badslug': f'{cave_id=} {year=} {qm_id=} {blockname=}'}) return render(request,'errors/badslug.html', {'badslug': f'{cave_id=} {year=} {qm_id=} {blockname=}'})
else: else:
try: try:
qmslug = f'{cave_id}-{year}-{blockname=}{qm_id}{grade}' qmslug = f'{cave_id}-{year}-{blockname=}{qm_id}{grade}'
print(f'{qmslug=}')
c=getCave(cave_id) c=getCave(cave_id)
manyqms=c.get_QMs() manyqms=c.get_QMs()
qmqs=manyqms.filter(expoyear=year, blockname=blockname, number=qm_id, grade=grade) qmqs=manyqms.filter(expoyear=year, blockname=blockname, number=qm_id, grade=grade)
@ -509,15 +509,16 @@ def qm(request,cave_id,qm_id,year,grade=None, blockname=None):
else: else:
qm=qmqs.get(expoyear=year, blockname=blockname, number=qm_id, grade=grade) qm=qmqs.get(expoyear=year, blockname=blockname, number=qm_id, grade=grade)
if qm: if qm:
print(qm, f'{qmslug=}:{cave_id=} {year=} {qm_id=} {blockname=} {qm.expoyear=}') print(qm, f'{qmslug=}:{cave_id=} {year=} {qm_id=} {blockname=} {qm.expoyear=} {qm.completion_description=}')
return render(request,'qm.html', {'qm': qm}) return render(request,'qm.html', {'qm': qm})
else: else:
raise #raise
return render(request,'errors/badslug.html', {'badslug': f'{cave_id=} {year=} {qm_id=} {blockname=}'}) return render(request,'errors/badslug.html', {'badslug': f'{cave_id=} {year=} {qm_id=} {blockname=}'})
except MultipleObjectsReturned: except MultipleObjectsReturned:
raise message = f'Multiple QMs with the same cave, year, number, grade AND first 8 chars of the survexblock name. (Could be caused by incomplete databasereset). Fix this in the survex file(s). {cave_id=} {year=} {qm_id=} {blockname=}'
return render(request,'errors/generic.html', {'message': message})
except QM.DoesNotExist: except QM.DoesNotExist:
raise #raise
return render(request,'errors/badslug.html', {'badslug': f'{cave_id=} {year=} {qm_id=} {blockname=}'}) return render(request,'errors/badslug.html', {'badslug': f'{cave_id=} {year=} {qm_id=} {blockname=}'})

View File

@ -9,14 +9,16 @@ from troggle.core.models.troggle import DataIssue
from troggle.core.models.caves import QM, Cave, LogbookEntry from troggle.core.models.caves import QM, Cave, LogbookEntry
from troggle.core.utils import save_carefully from troggle.core.utils import save_carefully
'''Reads the CSV files containg QMs for a select few caves''' '''Reads the CSV files containg QMs for a select few caves
See parsers/survex.py for the parser which extracts QMs from the survex files
'''
def deleteQMs(): def deleteQMs():
QM.objects.all().delete() QM.objects.all().delete()
DataIssue.objects.filter(parser='QMs').delete() DataIssue.objects.filter(parser='QMs').delete()
def parseCaveQMs(cave,inputFile): def parseCaveQMs(cave, inputFile, ticked=False):
"""Runs through the CSV file at inputFile (which is a relative path from expoweb) and """Runs through the CSV file at inputFile (which is a relative path from expoweb) and
saves each QM as a QM instance. saves each QM as a QM instance.
This is creating and linking a Placeholder logbookentry dated 1st Jan. in the relevant This is creating and linking a Placeholder logbookentry dated 1st Jan. in the relevant
@ -59,7 +61,7 @@ def parseCaveQMs(cave,inputFile):
message = f' ! - {qmPath} KH is not in the database. Please run cave parser' message = f' ! - {qmPath} KH is not in the database. Please run cave parser'
print(message) print(message)
DataIssue.objects.create(parser='QMs', message=message) DataIssue.objects.create(parser='QMs', message=message)
nqms = parse_KH_QMs(kh, inputFile=inputFile) nqms = parse_KH_QMs(kh, inputFile=inputFile, ticked=ticked)
return nqms return nqms
#qmPath = settings.EXPOWEB+inputFile #qmPath = settings.EXPOWEB+inputFile
@ -77,16 +79,6 @@ def parseCaveQMs(cave,inputFile):
n += 1 n += 1
year=int(line[0][1:5]) year=int(line[0][1:5])
logslug = f'PH_{int(year)}_{int(n):02d}' logslug = f'PH_{int(year)}_{int(n):02d}'
# logbook placeholder code was previously here. No longer needed.
#check if placeholder exists for given year, create it if not
# message = " ! - "+ str(year) + " logbook: placeholder entry for '" + cave + "' created. DUMMY EXPEDITION ID. Should be re-attached to the actual trip."
# if cave=='204-steinBH':
# placeholder, hadToCreate = LogbookEntry.objects.get_or_create(date__year=year, place="204", title="placeholder for QMs in 204", text=message, entry_type="DUMMY", expedition_id=1, defaults={"date": date(year, 1, 1),"cave_slug":str(steinBr), "slug": logslug})
# elif cave=='234-Hauch':
# placeholder, hadToCreate = LogbookEntry.objects.get_or_create(date__year=year, place="234", title="placeholder for QMs in 234", text=message, entry_type="DUMMY", expedition_id=1, defaults={"date": date(year, 1, 1),"cave_slug":str(hauchHl)})
# # if hadToCreate:
# # print(message)
# # DataIssue.objects.create(parser='QMs', message=message)
QMnum=re.match(r".*?-\d*?-X?(?P<numb>\d*)",line[0]).group("numb") QMnum=re.match(r".*?-\d*?-X?(?P<numb>\d*)",line[0]).group("numb")
newQM = QM() newQM = QM()
# newQM.found_by=placeholder # newQM.found_by=placeholder
@ -100,12 +92,13 @@ def parseCaveQMs(cave,inputFile):
newQM.area=line[2] newQM.area=line[2]
newQM.location_description=line[3] newQM.location_description=line[3]
# Troggle will in future (?! - written in 2006) check if QMs are completed by checking if they have a ticked_off_by trip.
# In the table, completion is indicated by the presence of a completion discription. # In the table, completion is indicated by the presence of a completion discription.
newQM.completion_description=line[4] newQM.completion_description=line[4]
newQM.nearest_station_description=line[5] newQM.nearest_station_description=line[5]
# if newQM.completion_description: if newQM.completion_description:
# newQM.ticked_off_by=placeholder newQM.ticked = True
else:
newQM.ticked = False
newQM.comment=line[6] newQM.comment=line[6]
try: try:
@ -134,7 +127,7 @@ def parseCaveQMs(cave,inputFile):
continue continue
return nqms return nqms
def parse_KH_QMs(kh, inputFile): def parse_KH_QMs(kh, inputFile, ticked):
"""import QMs from the 1623-161 (Kaninchenhohle) html pages, different format """import QMs from the 1623-161 (Kaninchenhohle) html pages, different format
""" """
khQMs=open(os.path.join(settings.EXPOWEB, inputFile),'r') khQMs=open(os.path.join(settings.EXPOWEB, inputFile),'r')
@ -161,12 +154,13 @@ def parse_KH_QMs(kh, inputFile):
'grade':res['grade'] 'grade':res['grade']
} }
nonLookupArgs={ nonLookupArgs={
'ticked': ticked,
'nearest_station_name':res['nearest_station'], 'nearest_station_name':res['nearest_station'],
'location_description':res['description'] 'location_description':res['description']
} }
instance, created = save_carefully(QM,lookupArgs,nonLookupArgs) instance, created = save_carefully(QM,lookupArgs,nonLookupArgs)
# if created: # if created:
# message = " ! - "+ instance.code() + " QM entry for '161 KH' created. " # message = f" ! - {instance.code()} QM entry for '161 KH' created. ticked: {ticked}"
# print(message) # print(message)
# DataIssue.objects.create(parser='QMs', message=message) # DataIssue.objects.create(parser='QMs', message=message)
nqms += 1 nqms += 1
@ -177,8 +171,9 @@ def Load_QMs():
deleteQMs() deleteQMs()
n204 = parseCaveQMs(cave='204-steinBH',inputFile=r"1623/204/qm.csv") n204 = parseCaveQMs(cave='204-steinBH',inputFile=r"1623/204/qm.csv")
n234 = parseCaveQMs(cave='234-Hauch',inputFile=r"1623/234/qm.csv") n234 = parseCaveQMs(cave='234-Hauch',inputFile=r"1623/234/qm.csv")
n161 = parseCaveQMs(cave='161-KH', inputFile="1623/161/qmtodo.htm") n161 = parseCaveQMs(cave='161-KH', inputFile="1623/161/qmtodo.htm", ticked=False)
t161 = parseCaveQMs(cave='161-KH', inputFile="1623/161/qmdone.htm", ticked=True)
#parseCaveQMs(cave='balkonhoehle',inputFile=r"1623/264/qm.csv") #parseCaveQMs(cave='balkonhoehle',inputFile=r"1623/264/qm.csv")
print(f" - Imported: {n204} QMs for 204, {n234} QMs for 234, {n161} QMs for 161.") print(f" - Imported: {n204} QMs for 204, {n234} QMs for 234, {t161} QMs for 161 done, {n161} QMs for 161 not done.")
print () print ()

View File

@ -545,8 +545,8 @@ class LoadingSurvex():
caveslug = survexblock.survexfile.cave.slug() caveslug = survexblock.survexfile.cave.slug()
place = survexblock.survexfile.cave place = survexblock.survexfile.cave
else: else:
caveslug = "ugh" caveslug = None
place = "oops" place = None
# message = f' ! - logbook dummy "{logslug}" {str(survexblock.date)[:11]} for cave "{caveslug}" created.' # message = f' ! - logbook dummy "{logslug}" {str(survexblock.date)[:11]} for cave "{caveslug}" created.'
@ -568,6 +568,7 @@ class LoadingSurvex():
nearest_station_name=qm_nearest, nearest_station_name=qm_nearest,
grade=qm_grade.upper(), grade=qm_grade.upper(),
location_description=qm_notes, location_description=qm_notes,
block = survexblock, # only set for survex-imported QMs
blockname = blockname, # only set for survex-imported QMs blockname = blockname, # only set for survex-imported QMs
# found_by = placeholder, # found_by = placeholder,
expoyear = str(survexblock.date.year), expoyear = str(survexblock.date.year),

View File

@ -7,8 +7,8 @@
<p>Note that QMs loaded for 1623-161, 1623-204 and 1623-234 are imported from CSV files . <p>Note that QMs loaded for 1623-161, 1623-204 and 1623-234 are imported from CSV files .
<ul> <ul>
<li><a href="{% url 'caveQMs' '1623-161' %}">1623-161 QMs</a> <li><a href="{% url 'caveQMs' '1623-161' %}">1623-161 QMs</a> Maybe OUT OF DATE. See <a href="/1623/161/qmdone.htm">the manually curated list <b>instead</b></a>
<li><a href="{% url 'caveQMs' '1623-204' %}">1623-204 QMs</a> <li><a href="{% url 'caveQMs' '1623-204' %}">1623-204 QMs</a> Maybe OUT OF DATE. See <a href=" /1623/204/qm.html">the manually curated list <b>instead</b></a>
<li><a href="{% url 'caveQMs' '1623-234' %}">1623-234 QMs</a> <li><a href="{% url 'caveQMs' '1623-234' %}">1623-234 QMs</a>
</ul> </ul>
<p>QMs are also loaded directly from the survex files, e.g. see <p>QMs are also loaded directly from the survex files, e.g. see
@ -24,20 +24,29 @@
{% block content %} {% block content %}
<h3>Extant</h3> <h3>Extant</h3>
<p>{% if cave.get_QMs %} <p>{% if cave.get_QMs %}
<ul id="cavelist"> <ul>
{% for QM in cave.get_QMs %} {% for QM in cave.get_QMs %}
{% if QM.ticked_off_by %} {% if QM.ticked %}
{% else %} {% else %}
<li><a href="{{QM.get_absolute_url}}">{{QM}}</a> :: {{QM.nearest_station_description}} {{QM.location_description}} <b>{{QM.grade}}</b></li> <li><a href="{{QM.get_absolute_url}}">{{QM}}</a> :: {{QM.nearest_station_description}} {{QM.location_description}} <b>{{QM.grade}}</b>
{% if QM.block %} <a href="/survexfile/{{QM.block.survexfile.path}}.svx">{{QM.block}}.svx</a> {{QM.block.date}} {% endif %}</li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</ul> </ul>
<h3>Ticked off</h3> <h3>Ticked off</h3>
<ul> <ul>
{% for QM in cave.get_QMs %} {% for QM in cave.get_QMs %}
{% if QM.ticked_off_by %} {% if QM.ticked %}
<li><a href="{{QM.get_absolute_url}}">{{QM}}</a> :: {{QM.nearest_station_description}} {{QM.location_description}} <b>{{QM.grade}}</b></li> <li><a href="{{QM.get_absolute_url}}">{{QM}}</a> :: {{QM.nearest_station_description}} {{QM.location_description}} <b>{{QM.grade}}</b>
{% if QM.block %} <a href="/survexfile/{{QM.block.survexfile.path}}.svx">{{QM.block}}.svx</a> {{QM.block.date}} {% endif %}
{% if QM.completion_description %}
Completion page: <a href="/{{QM.get_completion_url}}">{{QM.completion_description}}</a>
{% endif %} {% endif %}
{% endif %}
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %}</p> {% endif %}</p>

View File

@ -4,8 +4,8 @@
{% block contentheader %} {% block contentheader %}
<h3>QMs available for these caves from CSV import</h3> <h3>QMs available for these caves from CSV import</h3>
<ul> <ul>
<li><a href="{% url 'caveQMs' '1623-161' %}">1623-161 QMs</a> <li><a href="{% url 'caveQMs' '1623-161' %}">1623-161 QMs</a> Maybe OUT OF DATE. See <a href="/1623/161/qmdone.htm">the curated list <b>instead</b></a>
<li><a href="{% url 'caveQMs' '1623-204' %}">1623-204 QMs</a> <li><a href="{% url 'caveQMs' '1623-204' %}">1623-204 QMs</a> Maybe OUT OF DATE. See <a href=" /1623/204/qm.html">the curated list <b>instead</b></a>
<li><a href="{% url 'caveQMs' '1623-234' %}">1623-234 QMs</a> <li><a href="{% url 'caveQMs' '1623-234' %}">1623-234 QMs</a>
</ul> </ul>
<p>QMs are also loaded directly from the survex files, e.g. see <p>QMs are also loaded directly from the survex files, e.g. see
@ -42,6 +42,11 @@
<a href="/{{ qm.cave.url }}">{{ qm.cave|safe }} cave description</a> <a href="/{{ qm.cave.url }}">{{ qm.cave|safe }} cave description</a>
{% if qm.block %}
<h3>Survexfile</h3>
{{qm.block.date}} <a href="/survexfile/{{qm.block.survexfile.path}}.svx">{{qm.block}}.svx</a>
{% endif %}
<h3>Location</h3> <h3>Location</h3>
{{qm.location_description}} {{qm.location_description}}
<br> <br>
@ -56,12 +61,12 @@ Found by <a href="{{qm.found_by.get_absolute_url}}">{{qm.found_by}}</a> on {{qm.
{% endif %} {% endif %}
<h3>Completion</h3> <h3>Completion</h3>
{% if ticked_off_by %} {% if qm.ticked %}
{{qm.completion_description}} Ticked off log entry: <a href="{{qm.ticked_off_by.get_absolute_url}}">{{qm.ticked_off_by}}</a><br />
Ticked off by: <a href="{{qm.ticked_off_by.get_absolute_url}}">{{qm.ticked_off_by}}</a><br /> Ticked off Description Page: <a href="/{{qm.get_completion_url}}">{{qm.get_completion_url}}</a>
Description: {{qm.completion_description}}
{% else %} {% else %}
None yet- STILL EXTANT. No completion description yet- STILL EXTANT.
{% endif %} {% endif %}
<h3>Comment</h3> <h3>Comment</h3>