troggle-unchained/core/views/logbooks.py

268 lines
11 KiB
Python
Raw Normal View History

import datetime
import os.path
import re
2023-01-19 18:35:56 +00:00
import time
2011-07-11 02:10:22 +01:00
import django.db.models
2023-01-19 18:35:56 +00:00
from django.db.models import Max, Min
from django.http import HttpResponse, HttpResponseRedirect
2021-03-28 03:48:24 +01:00
from django.shortcuts import render
2011-07-11 02:10:22 +01:00
from django.template import Context, loader
from django.template.defaultfilters import slugify
2023-01-19 18:35:56 +00:00
from django.urls import reverse
from django.utils import timezone
from django.views.generic.list import ListView
2011-07-11 02:10:22 +01:00
2023-01-19 18:35:56 +00:00
import troggle.settings as settings
2023-01-30 16:18:19 +00:00
from troggle.core.models.logbooks import LogbookEntry, PersonLogEntry
from troggle.core.models.survex import SurvexBlock
2023-01-19 18:35:56 +00:00
from troggle.core.models.troggle import Expedition, Person, PersonExpedition
2023-01-29 21:45:51 +00:00
from troggle.core.models.wallets import Wallet
2023-01-19 18:35:56 +00:00
from troggle.core.utils import TROG
2023-01-27 23:21:07 +00:00
from troggle.parsers.imports import import_logbook
from troggle.parsers.people import GetPersonExpeditionNameLookup
2023-01-19 18:35:56 +00:00
from .auth import login_required_if_public
"""These views are for logbook items when they appear in an 'expedition' page
2021-04-10 01:07:49 +01:00
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.
"""
2011-07-11 02:10:22 +01:00
2021-04-21 19:08:42 +01:00
def notablepersons(request):
def notabilitykey(person):
return person.notability()
2011-07-11 02:10:22 +01:00
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 = []
2011-07-11 02:10:22 +01:00
ncols = 4
2020-05-31 22:35:36 +01:00
nc = int((len(persons) + ncols - 1) / ncols)
2011-07-11 02:10:22 +01:00
for i in range(ncols):
pcols.append(persons[i * nc : (i + 1) * nc])
2011-07-11 02:10:22 +01:00
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)
2011-07-11 02:10:22 +01:00
return render(
request, "notablepersons.html", {"persons": persons, "pcols": pcols, "notablepersons": notablepersons}
)
2011-07-11 02:10:22 +01:00
def expedition(request, expeditionname):
"""Returns a rendered page for one expedition, specified by the year e.g. '2019'.
2021-04-10 01:07:49 +01:00
If page caching is enabled, it caches the dictionaries used to render the template page.
2023-01-30 15:28:11 +00:00
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.
2023-01-30 15:28:11 +00:00
Remember that 'personexpedition__expedition' is interpreted by Django to mean the
'expedition' object which is connected by a foreign key to the 'personexpedition'
2023-01-30 16:18:19 +00:00
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
2023-01-30 15:28:11 +00:00
does lazy evaluation.
"""
2023-01-30 15:28:11 +00:00
try:
expo = Expedition.objects.get(year=int(expeditionname))
except:
message = (
f"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})
if request.user.is_authenticated:
logged_in = True
2023-01-30 15:28:11 +00:00
if "reload" in request.GET:
expo.logbookentry_set.all().delete()
import_logbook(year=expo.year)
else:
logged_in = False
2021-04-10 01:07:49 +01:00
ts = TROG["pagecache"]["expedition"] # not much use unless single user!
2021-04-10 01:07:49 +01:00
if settings.CACHEDPAGES:
nexpos = len(TROG["pagecache"]["expedition"])
# print(f'! - expo {expeditionname} CACHEDPAGES {nexpos} expo pages in cache.')
2021-04-10 01:07:49 +01:00
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()
2023-01-30 15:28:11 +00:00
blocks = expo.survexblock_set.all()
dateditems = list(entries) + list(blocks) # evaluates the Django query and hits db
2020-05-24 01:57:06 +01:00
dates = sorted(set([item.date for item in dateditems]))
2023-01-30 16:18:19 +00:00
allpersonlogentries = PersonLogEntry.objects.filter(personexpedition__expedition=expo)
personexpodays = []
2023-01-30 15:28:11 +00:00
for personexpedition in expo.personexpedition_set.all():
expotrips = allpersonlogentries.filter(personexpedition=personexpedition) # lazy
2023-01-30 15:28:11 +00:00
expoblocks = blocks.filter(survexpersonrole__personexpedition=personexpedition)
prow = []
2011-07-11 02:10:22 +01:00
for date in dates:
personentries = expotrips.filter(logbook_entry__date=date) # lazy
personblocks = set(expoblocks.filter(date=date)) # not lazy
2023-01-30 15:28:11 +00:00
pcell = {}
pcell["personentries"] = personentries
pcell["survexblocks"] = personblocks
if issunday := (date.weekday() == 6): # WALRUS
2023-01-30 15:28:11 +00:00
pcell["sunday"] = issunday
2011-07-11 02:10:22 +01:00
prow.append(pcell)
personexpodays.append({"personexpedition": personexpedition, "personrow": prow})
ts[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
def person(
request,
first_name="",
last_name="",
):
2021-04-20 22:58:41 +01:00
try:
this_person = Person.objects.get(first_name=first_name, last_name=last_name)
2021-04-20 22:58:41 +01:00
except:
message = f"Person not found '{first_name} {last_name}' - possibly Scottish? (See our <a href=\"/handbook/troggle/namesredesign.html\">Proposal to fix this</a>)"
return render(request, "errors/generic.html", {"message": message})
return render(request, "person.html", {"person": this_person})
2011-07-11 02:10:22 +01:00
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 = {}
2023-01-30 16:27:01 +00:00
for personlogentry in personexpedition.personlogentry_set.all():
a = res.setdefault(personlogentry.logbook_entry.date, {})
a.setdefault("personlogentries", []).append(personlogentry)
2011-07-11 02:10:22 +01:00
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)
2011-07-11 02:10:22 +01:00
# build up the tables
2020-05-24 01:57:06 +01:00
rdates = sorted(list(res.keys()))
res2 = []
2011-07-11 02:10:22 +01:00
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]),
)
)
2011-07-11 02:10:22 +01:00
return res2
def personexpedition(request, first_name="", last_name="", year=""):
person = Person.objects.get(first_name=first_name, last_name=last_name)
this_expedition = Expedition.objects.get(year=year)
personexpedition = person.personexpedition_set.get(expedition=this_expedition)
personchronology = get_person_chronology(personexpedition)
# for pc in personchronology:
# print(pc)
return render(
request, "personexpedition.html", {"personexpedition": personexpedition, "personchronology": personchronology}
)
2011-07-11 02:10:22 +01:00
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)
2021-04-24 01:23:55 +01:00
if this_logbookentry:
if len(this_logbookentry) > 1:
return render(request, "object_list.html", {"object_list": this_logbookentry})
2021-04-24 01:23:55 +01:00
else:
wallets = set()
allwallets = Wallet.objects.all()
refwallets = allwallets.filter(survexblock__date=date)
for r in refwallets:
wallets.add(r)
# Note that w.year() only works for wallets which have a valid JSON file existing
# This is very slow with a big lag as w.date() is a computed field
# Noticably slow with WSL2 and NTFS filesystem, even with caching as walletdate.
jwallets = allwallets.filter(walletdate=date)
for j in jwallets:
wallets.add(j)
2023-01-30 16:42:56 +00:00
2023-01-29 21:45:51 +00:00
svxothers = SurvexBlock.objects.filter(date=date)
this_logbookentry = this_logbookentry[0]
2023-01-30 16:42:56 +00:00
# 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},
)
2011-07-11 02:10:22 +01:00
else:
msg = f' Logbook entry slug:"{slug}" not found in database on date:"{date}" '
2021-04-24 01:23:55 +01:00
print(msg)
return render(request, "errors/generic.html", {"message": msg})
2011-07-11 02:10:22 +01:00
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()]})
2011-07-11 02:10:22 +01:00
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()]}
)