mirror of
https://expo.survex.com/repositories/troggle/.git
synced 2024-12-18 14:32:19 +00:00
352 lines
14 KiB
Python
352 lines
14 KiB
Python
import re
|
|
|
|
from django.db.models import Q
|
|
from django.shortcuts import render
|
|
from django.views.generic.list import ListView
|
|
from django.shortcuts import render, redirect
|
|
|
|
|
|
import troggle.settings as settings
|
|
from troggle.core.models.logbooks import LogbookEntry, PersonLogEntry, QM, writelogbook
|
|
from troggle.core.models.survex import SurvexBlock, SurvexFile
|
|
from troggle.core.models.troggle import Expedition, Person
|
|
from troggle.core.models.wallets import Wallet
|
|
from troggle.core.utils import TROG, current_expo
|
|
from troggle.parsers.imports import import_logbook
|
|
|
|
|
|
"""These views are for logbook items when they appear in an 'expedition' page
|
|
and for persons: their individual pages and their perseonexpedition pages.
|
|
|
|
It uses the global object TROG to hold some cached pages.
|
|
"""
|
|
|
|
todo = """- Fix the get_person_chronology() display bug.
|
|
|
|
- Fix id= value preservation on editing
|
|
"""
|
|
|
|
|
|
def notablepersons(request):
|
|
def notabilitykey(person):
|
|
return person.notability()
|
|
|
|
persons = Person.objects.all()
|
|
# From what I can tell, "persons" seems to be the table rows, while "pcols" is the table columns. - AC 16 Feb 09
|
|
pcols = []
|
|
ncols = 4
|
|
nc = int((len(persons) + ncols - 1) / ncols)
|
|
for i in range(ncols):
|
|
pcols.append(persons[i * nc : (i + 1) * nc])
|
|
|
|
notablepersons = []
|
|
# Needed recoding because of Django CVE-2021-45116
|
|
for person in persons:
|
|
if person.bisnotable():
|
|
notablepersons.append(person)
|
|
notablepersons.sort(key=notabilitykey, reverse=True)
|
|
|
|
return render(
|
|
request, "notablepersons.html", {"persons": persons, "pcols": pcols, "notablepersons": notablepersons}
|
|
)
|
|
|
|
|
|
def expedition(request, expeditionname):
|
|
"""Returns a rendered page for one expedition, specified by the year e.g. '2019'.
|
|
If page caching is enabled, it caches the dictionaries used to render the template page.
|
|
|
|
This is not as difficult to understand as it looks.
|
|
Yes there are many levels of indirection, with multiple trees being traversed at the same time.
|
|
And the Django special syntax makes this hard for normal Python programmers.
|
|
|
|
Remember that 'personexpedition__expedition' is interpreted by Django to mean the
|
|
'expedition' object which is connected by a foreign key to the 'personexpedition'
|
|
object, which is a field of the PersonLogEntry object:
|
|
PersonLogEntry.objects.filter(personexpedition__expedition=expo)
|
|
|
|
Queries are not evaluated to hit the database until a result is actually used. Django
|
|
does lazy evaluation.
|
|
|
|
"""
|
|
current = current_expo() # creates new expo after 31st Dec.
|
|
try:
|
|
expo = Expedition.objects.get(year=int(expeditionname))
|
|
except:
|
|
message = (
|
|
"Expedition not found - database apparently empty, you probably need to do a full re-import of all data."
|
|
)
|
|
return render(request, "errors/generic.html", {"message": message})
|
|
|
|
ts = TROG["pagecache"]["expedition"] # not much use unless single user!
|
|
if request.user.is_authenticated:
|
|
logged_in = True
|
|
if "reload" in request.GET:
|
|
if expeditionname in ts:
|
|
del ts[expeditionname] # clean out cache for page
|
|
expo.logbookentry_set.all().delete()
|
|
import_logbook(year=expo.year)
|
|
else:
|
|
logged_in = False
|
|
|
|
if settings.CACHEDPAGES:
|
|
if expeditionname in ts:
|
|
# print('! - expo {expeditionanme} using cached page')
|
|
return render(request, "expedition.html", {**ts[expeditionname], "logged_in": logged_in})
|
|
|
|
expeditions = Expedition.objects.all() # top menu only, evaluated only when template renders
|
|
|
|
entries = expo.logbookentry_set.all()
|
|
blocks = expo.survexblock_set.all()
|
|
dateditems = list(entries) + list(blocks) # evaluates the Django query and hits db
|
|
dates = sorted(set([item.date for item in dateditems]))
|
|
|
|
allpersonlogentries = PersonLogEntry.objects.filter(personexpedition__expedition=expo)
|
|
|
|
personexpodays = []
|
|
for personexpedition in expo.personexpedition_set.all():
|
|
expotrips = allpersonlogentries.filter(personexpedition=personexpedition) # lazy
|
|
expoblocks = blocks.filter(survexpersonrole__personexpedition=personexpedition)
|
|
|
|
prow = []
|
|
|
|
for date in dates:
|
|
personentries = expotrips.filter(logbook_entry__date=date) # lazy
|
|
personblocks = set(expoblocks.filter(date=date)) # not lazy
|
|
pcell = {}
|
|
pcell["personentries"] = personentries
|
|
pcell["survexblocks"] = personblocks
|
|
if issunday := (date.weekday() == 6): # WALRUS
|
|
pcell["sunday"] = issunday
|
|
prow.append(pcell)
|
|
personexpodays.append({"personexpedition": personexpedition, "personrow": prow, "sortname": personexpedition.person.last_name})
|
|
|
|
ts[expeditionname] = {
|
|
"year": int(expeditionname),
|
|
"expedition": expo,
|
|
"expeditions": expeditions,
|
|
"personexpodays": personexpodays,
|
|
"settings": settings,
|
|
"dateditems": dateditems,
|
|
"dates": dates,
|
|
}
|
|
TROG["pagecache"]["expedition"][expeditionname] = ts[expeditionname]
|
|
|
|
return render(request, "expedition.html", {**ts[expeditionname], "logged_in": logged_in})
|
|
|
|
|
|
class Expeditions_tsvListView(ListView):
|
|
"""This uses the Django built-in shortcut mechanism
|
|
It defaults to use a template with name <app-label>/<model-name>_list.html.
|
|
https://www.agiliq.com/blog/2017/12/when-and-how-use-django-listview/
|
|
https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Generic_views
|
|
Either a queryset variable or set_queryset() function is used, but not needed
|
|
if you want all the obejcts of a particaulr type in which case just set model = <object>
|
|
"""
|
|
|
|
template_name = "core/expeditions_tsv_list.html" # if not present then uses core/expedition_list.html
|
|
# queryset = Expedition.objects.all()
|
|
# context_object_name = 'expedition'
|
|
model = Expedition # equivalent to .objects.all() for a queryset
|
|
|
|
|
|
class Expeditions_jsonListView(ListView):
|
|
template_name = "core/expeditions_json_list.html"
|
|
model = Expedition
|
|
|
|
class QMs_jsonListView(ListView):
|
|
template_name = "core/QMs_json_list.html"
|
|
model = QM
|
|
|
|
def person(request, slug=""):
|
|
"""Now very much simpler with an unambiguous slug
|
|
"""
|
|
try:
|
|
this_person = Person.objects.get(slug=slug)
|
|
except:
|
|
msg = f" Person '{slug=}' not found in database. DATABASE RESET required - ask a nerd."
|
|
print(msg)
|
|
return render(request, "errors/generic.html", {"message": msg})
|
|
|
|
current_year = current_expo()
|
|
return render(request, "person.html", {"person": this_person, "year": current_year})
|
|
|
|
def get_person_chronology(personexpedition):
|
|
"""
|
|
This is just a nasty convoluted way of trying the make the template do more work than it is sensible to ask it to do.
|
|
Rewrite more simply with the login in the python, not in Django template language (you bastard Curtis).
|
|
"""
|
|
res = {}
|
|
for personlogentry in personexpedition.personlogentry_set.all():
|
|
a = res.setdefault(personlogentry.logbook_entry.date, {})
|
|
a.setdefault("personlogentries", []).append(personlogentry)
|
|
|
|
for personrole in personexpedition.survexpersonrole_set.all():
|
|
if personrole.survexblock.date: # avoid bad data from another bug
|
|
a = res.setdefault(personrole.survexblock.date, {})
|
|
a.setdefault("personroles", []).append(personrole.survexblock)
|
|
|
|
# build up the tables
|
|
rdates = sorted(list(res.keys()))
|
|
|
|
res2 = []
|
|
for rdate in rdates:
|
|
personlogentries = res[rdate].get("personlogentries", [])
|
|
personroles = res[rdate].get("personroles", [])
|
|
for n in range(max(len(personlogentries), len(personroles))):
|
|
res2.append(
|
|
(
|
|
(n == 0 and rdate or "--"),
|
|
(n < len(personlogentries) and personlogentries[n]),
|
|
(n < len(personroles) and personroles[n]),
|
|
)
|
|
)
|
|
|
|
return res2
|
|
|
|
|
|
def personexpedition(request, slug="", year=""):
|
|
try:
|
|
person = Person.objects.get(slug=slug)
|
|
this_expedition = Expedition.objects.get(year=year)
|
|
personexpedition = person.personexpedition_set.get(expedition=this_expedition)
|
|
personchronology = get_person_chronology(personexpedition)
|
|
current_year = current_expo()
|
|
|
|
return render(
|
|
request, "personexpedition.html", {"personexpedition": personexpedition, "personchronology": personchronology, "year": current_year}
|
|
)
|
|
except:
|
|
msg = f" Person '{slug=}' or year '{year=}' not found in database. Please report this to a nerd."
|
|
print(msg)
|
|
return render(request, "errors/generic.html", {"message": msg})
|
|
|
|
def logentrydelete(request, year):
|
|
"""This only gets called by a POST from the logreport page
|
|
|
|
This function is dedicated to James Waite who managed to make so many duplicate logbook entries
|
|
that we needed a sopecial mechanism to delete them.
|
|
"""
|
|
for i in request.POST:
|
|
print(f" - '{i}' {request.POST[i]}")
|
|
eslug = request.POST["entry_slug"]
|
|
entry = LogbookEntry.objects.get(slug=eslug)
|
|
# OK we delete it from the db and then re-save logbook.html file
|
|
# to ensure that the permanent record also has the entry deleted.
|
|
entry.delete()
|
|
|
|
print(f"- Rewriting the entire {year} logbook to disc ")
|
|
filename= "logbook.html"
|
|
try:
|
|
writelogbook(year, filename) # uses a template
|
|
except:
|
|
message = f'! - Logbook saving failed - \n!! Permissions failure ?! on attempting to save file "logbook.html"'
|
|
print(message)
|
|
return render(request, "errors/generic.html", {"message": message})
|
|
|
|
|
|
return redirect(f"/logreport/{year}")
|
|
|
|
|
|
def logreport(request, year=1999):
|
|
"""
|
|
Remember that 'personexpedition__expedition' is interpreted by Django to mean the
|
|
'expedition' object which is connected by a foreign key to the 'personexpedition'
|
|
object, which is a field of the PersonLogEntry object:
|
|
PersonLogEntry.objects.filter(personexpedition__expedition=expo)
|
|
|
|
"""
|
|
# print(f"logreport(): begun")
|
|
|
|
expeditions = Expedition.objects.all() # top menu only, evaluated only when template renders
|
|
dates = None
|
|
dateditems = None
|
|
logged_in = False
|
|
|
|
|
|
if request.user.is_superuser: # expoadmin is both .is_staff and ._is_superuser
|
|
logged_in = True
|
|
|
|
|
|
try:
|
|
expo = Expedition.objects.get(year=int(year))
|
|
except:
|
|
message = (
|
|
"Expedition not found - database apparently empty, you probably need to do a full re-import of all data."
|
|
)
|
|
return render(request, "errors/generic.html", {"message": message})
|
|
|
|
entries = expo.logbookentry_set.all() # lazy list
|
|
dateditems = list(entries) # evaluates the Django query and hits db
|
|
dates = sorted(set([item.date for item in dateditems]))
|
|
|
|
try:
|
|
for entry in dateditems:
|
|
|
|
people = PersonLogEntry.objects.filter(logbook_entry=entry)
|
|
entry.who = []
|
|
for p in people:
|
|
if p.is_logbook_entry_author:
|
|
entry.author = p
|
|
else:
|
|
entry.who.append(p)
|
|
|
|
|
|
# print(f"logreport(): trying..")
|
|
context = {
|
|
"year": year,
|
|
"expedition": expo,
|
|
"expeditions": expeditions,
|
|
"settings": settings,
|
|
"dateditems": dateditems,
|
|
"dates": dates,
|
|
"logged_in": logged_in,
|
|
}
|
|
# print(f"logreport(): rendering..")
|
|
return render(request, "logreport.html", context)
|
|
|
|
except Exception as e:
|
|
msg = f' Logbook report for year:"{year}" not implemented yet\n{e}\n {context}'
|
|
print(msg)
|
|
return render(request, "errors/generic.html", {"message": msg})
|
|
|
|
def logbookentry(request, date, slug):
|
|
# start = time.time()
|
|
trips = LogbookEntry.objects.filter(date=date) # all the trips not just this one
|
|
this_logbookentry = trips.filter(date=date, slug=slug)
|
|
year = slug[:4]
|
|
|
|
if this_logbookentry:
|
|
if len(this_logbookentry) > 1:
|
|
# BUG
|
|
return render(request, "object_list.html", {"object_list": this_logbookentry})
|
|
else:
|
|
# https://stackoverflow.com/questions/739776/how-do-i-do-an-or-filter-in-a-django-query
|
|
wallets = Wallet.objects.filter(Q(survexblock__date=date) | Q(walletdate=date)).distinct()
|
|
svxothers = SurvexFile.objects.filter(survexblock__date=date).distinct()
|
|
|
|
this_logbookentry = this_logbookentry[0]
|
|
# This is the only page that uses next_.. and prev_..
|
|
# and it is calculated on the fly in the model
|
|
return render(
|
|
request,
|
|
"logbookentry.html",
|
|
{"logbookentry": this_logbookentry,"trips": trips,
|
|
"svxothers": svxothers, "wallets": wallets, "year": year},
|
|
)
|
|
else:
|
|
msg = f' Logbook entry slug:"{slug}" not found in database on date:"{date}" '
|
|
print(msg)
|
|
return render(request, "errors/generic.html", {"message": msg})
|
|
|
|
|
|
def get_people(request, expeditionslug):
|
|
exp = Expedition.objects.get(year=expeditionslug)
|
|
return render(request, "options.html", {"items": [(pe.slug, pe.name) for pe in exp.personexpedition_set.all()]})
|
|
|
|
|
|
def get_logbook_entries(request, expeditionslug):
|
|
exp = Expedition.objects.get(year=expeditionslug)
|
|
return render(
|
|
request, "options.html", {"items": [(le.slug, f"{le.date} - {le.title}") for le in exp.logbookentry_set.all()]}
|
|
)
|