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

part-way though converting to slugs for people

This commit is contained in:
Philip Sargent 2023-10-01 15:55:28 +03:00
parent 16d3ee9f92
commit 7b8703dadc
9 changed files with 84 additions and 43 deletions

View File

@ -80,14 +80,15 @@ class Person(TroggleModel):
first_name = models.CharField(max_length=100) first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100)
fullname = models.CharField(max_length=200) fullname = models.CharField(max_length=200) # display name, but should not be used for lookups
nickname = models.CharField(max_length=200) nickname = models.CharField(max_length=200)
slug = models.SlugField(max_length=50, unique=True) slug = models.SlugField(max_length=50, blank=True, null=True) # unique, enforced in code not in db
is_vfho = models.BooleanField( is_vfho = models.BooleanField(
help_text="VFHO is the Vereines für Höhlenkunde in Obersteier, a nearby Austrian caving club.", help_text="VFHO is the Vereines für Höhlenkunde in Obersteier, a nearby Austrian caving club.",
default=False, default=False,
) )
is_guest = models.BooleanField(default=False) # This is per-Person, not per-PersonExpedition
mug_shot = models.CharField(max_length=100, blank=True, null=True) mug_shot = models.CharField(max_length=100, blank=True, null=True)
blurb = models.TextField(blank=True, null=True) blurb = models.TextField(blank=True, null=True)
orderref = models.CharField(max_length=200) # for alphabetic orderref = models.CharField(max_length=200) # for alphabetic
@ -101,6 +102,7 @@ class Person(TroggleModel):
ordering = ("orderref",) # "Wookey" makes too complex for: ('last_name', 'first_name') ordering = ("orderref",) # "Wookey" makes too complex for: ('last_name', 'first_name')
def __str__(self): def __str__(self):
return self.slug
if self.last_name: if self.last_name:
return f"{self.first_name} {self.last_name}" return f"{self.first_name} {self.last_name}"
return self.first_name return self.first_name
@ -150,7 +152,7 @@ class PersonExpedition(TroggleModel):
person = models.ForeignKey(Person, on_delete=models.CASCADE) person = models.ForeignKey(Person, on_delete=models.CASCADE)
slugfield = models.SlugField(max_length=50, blank=True, null=True) # 2022 to be used in future slugfield = models.SlugField(max_length=50, blank=True, null=True) # 2022 to be used in future
is_guest = models.BooleanField(default=False) # is_guest = models.BooleanField(default=False) # This is per-Person, not per-PersonExpedition
class Meta: class Meta:
ordering = ("-expedition",) ordering = ("-expedition",)
@ -162,6 +164,16 @@ class PersonExpedition(TroggleModel):
def get_absolute_url(self): def get_absolute_url(self):
# we do not use URL_ROOT any more. # we do not use URL_ROOT any more.
return(f"/personexpedition/{self.person.slug}/{self.expedition.year}")
# why does this hang the system ?
return reverse(
"personexpedition",
kwargs={
"slug": self.slug,
"year": self.expedition.year,
},
)
# old style, no longer used
return reverse( return reverse(
"personexpedition", "personexpedition",
kwargs={ kwargs={

View File

@ -193,6 +193,9 @@ def person(
this_person = Person.objects.get(first_name=first_name, last_name=last_name) this_person = Person.objects.get(first_name=first_name, last_name=last_name)
except: 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>)" message = f"Person not found '{first_name} {last_name}' - possibly Scottish? (See our <a href=\"/handbook/troggle/namesredesign.html\">Proposal to fix this</a>)"
peeps = Person.objects.filter(first_name=first_name, last_name=last_name)
if len(peeps) > 1:
message = f"Multiple people ({len(peeps)}) with this name '{first_name} {last_name}' - (See our <a href=\"/handbook/troggle/namesredesign.html\">Proposal to fix this</a>)"
return render(request, "errors/generic.html", {"message": message}) return render(request, "errors/generic.html", {"message": message})
return render(request, "person.html", {"person": this_person}) return render(request, "person.html", {"person": this_person})
@ -232,8 +235,8 @@ def get_person_chronology(personexpedition):
return res2 return res2
def personexpedition(request, first_name="", last_name="", year=""): def personexpedition(request, slug="", year=""):
person = Person.objects.get(first_name=first_name, last_name=last_name) person = Person.objects.get(slug=slug)
this_expedition = Expedition.objects.get(year=year) this_expedition = Expedition.objects.get(year=year)
personexpedition = person.personexpedition_set.get(expedition=this_expedition) personexpedition = person.personexpedition_set.get(expedition=this_expedition)
personchronology = get_person_chronology(personexpedition) personchronology = get_person_chronology(personexpedition)

View File

@ -142,7 +142,7 @@ def fixsurvextick(w, ticks):
ticks["S"] = w.fixsurvextick(ticks["S"]) ticks["S"] = w.fixsurvextick(ticks["S"])
def walletslistperson(request, first_name, last_name): def walletslistperson(request, slug):
"""Page which displays a list of all the wallets for a specific person """Page which displays a list of all the wallets for a specific person
HORRIBLE linear search through everything. Index and do SQL query properly HORRIBLE linear search through everything. Index and do SQL query properly
""" """
@ -163,20 +163,21 @@ def walletslistperson(request, first_name, last_name):
return manywallets return manywallets
# print("-walletslistperson") # print("-walletslistperson")
p = Person.objects.get(slug=slug)
# try:
try: # if last_name:
if last_name: # p = Person.objects.get(fullname=f"{first_name} {last_name}")
p = Person.objects.get(fullname=f"{first_name} {last_name}") # else:
else: # # special Wookey-hack
# special Wookey-hack # p = Person.objects.get(first_name=f"{first_name}")
p = Person.objects.get(first_name=f"{first_name}") # except:
except: # # raise
# raise # return render(
return render( # request,
request, # "errors/generic.html",
"errors/generic.html", # {"message": f'Unrecognised name of a expo person: "{first_name} {last_name}"'},
{"message": f'Unrecognised name of a expo person: "{first_name} {last_name}"'}, # )
)
manywallets = tickspersonwallet(p) manywallets = tickspersonwallet(p)
expeditions = Expedition.objects.all() expeditions = Expedition.objects.all()

View File

@ -3,9 +3,9 @@ import os
import re import re
from html import unescape from html import unescape
from pathlib import Path from pathlib import Path
from unidecode import unidecode
from django.conf import settings from django.conf import settings
from unidecode import unidecode
from troggle.core.models.troggle import DataIssue, Expedition, Person, PersonExpedition from troggle.core.models.troggle import DataIssue, Expedition, Person, PersonExpedition
@ -17,7 +17,9 @@ or they should use the same code by importing a module.
def parse_blurb(personline, header, person): def parse_blurb(personline, header, person):
"""create mugshot Photo instance""" """create mugshot Photo instance
Would be better if all this was done before the Person object was created in the db, then it would not
need re-saving (which is slow)"""
ms_filename = personline[header["Mugshot"]] ms_filename = personline[header["Mugshot"]]
ms_path = Path(settings.EXPOWEB, "folk", ms_filename) ms_path = Path(settings.EXPOWEB, "folk", ms_filename)
@ -60,6 +62,18 @@ def parse_blurb(personline, header, person):
person.save() person.save()
slug_cache = {}
def troggle_slugify(longname):
"""Uniqueness enforcement too. Yes we have had two "Dave Johnson"s
"""
slug = longname.strip().lower().replace(" ","-")
if len(slug) > 40: # slugfield is 50 chars
slug = slug[:40]
if slug in slug_cache:
slug_cache[slug] += 1
slug = f"{slug}_{slug_cache[slug]}"
slug_cache[slug] = 1
return slug
def load_people_expos(): def load_people_expos():
"""This is where the folk.csv file is parsed to read people's names. """This is where the folk.csv file is parsed to read people's names.
@ -87,7 +101,10 @@ def load_people_expos():
for personline in personreader: for personline in personreader:
name = personline[header["Name"]] name = personline[header["Name"]]
name = re.sub(r"<.*?>", "", name) name = re.sub(r"<.*?>", "", name)
slug = slugify(name)
match = re.match(r"^([^(]*)(\(([^)]*)\))?", name) # removes nickname in brackets
displayname = match.group(1)
slug = troggle_slugify(displayname)
firstname = "" firstname = ""
nick = "" nick = ""
@ -97,34 +114,39 @@ def load_people_expos():
lastname = matchlastname.group(1).strip() lastname = matchlastname.group(1).strip()
splitnick = re.match(r"^([\w&;\s]+)(?:\(([^)]*)\))?", name) splitnick = re.match(r"^([\w&;\s]+)(?:\(([^)]*)\))?", name)
fullname = splitnick.group(1) fullname = splitnick.group(1) # removes Nickname in brackets, but also cuts hyphenated names
nick = splitnick.group(2) or "" nick = splitnick.group(2) or ""
fullname = fullname.strip() fullname = fullname.strip()
names = fullname.split(" ")
names = fullname.split(" ") # This may have more than one, e.g. "Adeleide de Diesback"
firstname = names[0] firstname = names[0]
if len(names) == 1: if len(names) == 1:
lastname = "" lastname = "" # wookey special code
#restore fullname to be the whole string
fullname = displayname
if personline[header["VfHO member"]] == "": if personline[header["VfHO member"]] == "":
vfho = False vfho = False
else: else:
vfho = True vfho = True
coUniqueAttribs = {"first_name": firstname, "last_name": (lastname or "")} # would be better to just create the python object, and only cmmit to db once all done inc blurb
otherAttribs = {"is_vfho": vfho, "fullname": fullname, "nickname": nick} # and better to save all the Persons in a bulk update, then do all the PersonExpeditions
coUniqueAttribs = {"slug": slug}
otherAttribs = {"first_name": firstname, "last_name": (lastname or ""), "is_vfho": vfho, "fullname": fullname, "nickname": nick,"is_guest": (personline[header["Guest"]] == "1")}
person = Person.objects.create(**otherAttribs, **coUniqueAttribs) person = Person.objects.create(**otherAttribs, **coUniqueAttribs)
parse_blurb(personline=personline, header=header, person=person) parse_blurb(personline=personline, header=header, person=person) # saves to db too
# make person expedition from table # make person expedition from table
for year, attended in list(zip(headers, personline))[5:]: for year, attended in list(zip(headers, personline))[5:]:
expedition = Expedition.objects.get(year=year) expedition = Expedition.objects.get(year=year)
if attended == "1" or attended == "-1": if attended == "1" or attended == "-1":
coUniqueAttribs = {"person": person, "expedition": expedition} coUniqueAttribs = {"person": person, "expedition": expedition}
otherAttribs = {"is_guest": (personline[header["Guest"]] == "1")} # otherAttribs = {"is_guest": (personline[header["Guest"]] == "1")}
pe = PersonExpedition.objects.create(**otherAttribs, **coUniqueAttribs) pe = PersonExpedition.objects.create(**coUniqueAttribs)
print("", flush=True) print("", flush=True)

View File

@ -43,7 +43,7 @@ an "<b>S</b>" for a survey trip. The colours of the "<b>T</b>" and "<b>S</b>" a
</tr> </tr>
{% for personexpoday in personexpodays %} {% for personexpoday in personexpodays %}
<tr> <tr>
<td><a href="{{ personexpoday.personexpedition.get_absolute_url }}">{{personexpoday.personexpedition.person|safe}}</a></td> <td><a href="{{ personexpoday.personexpedition.get_absolute_url }}">{{personexpoday.personexpedition.person.fullname|safe}}</a></td>
{% for activities in personexpoday.personrow %} {% for activities in personexpoday.personrow %}
{% if activities.personentries or activities.survexblocks %} {% if activities.personentries or activities.survexblocks %}

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Person {{person}}{% endblock %} {% block title %}Person {{person}}{% endblock %}
{% block contentheader %} {% block contentheader %}
<h2> {{person|safe}} </h2> <h2> {{person.fullname|safe}} </h2>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -15,7 +15,7 @@
{% endif %} {% endif %}
<br class="clearfloat" /> <br class="clearfloat" />
<h3>{{person|safe}} has been on expo in the following years:</h3> <h3>{{person.fullname|safe}} has been on expo in the following years:</h3>
<table> <table>
<th>Expo</th><th>Logbook mentions</th><th>Survex trips</th> <th>Expo</th><th>Logbook mentions</th><th>Survex trips</th>
@ -36,7 +36,7 @@
{% endfor %} {% endfor %}
</table> </table>
<h3>Surveys done</h3> <h3>Surveys done</h3>
Wallets and surveys mentioning <a href="/wallets/person/{{person}}">{{person}}</a><br> Wallets and surveys mentioning <a href="/wallets/person/{{person}}">{{person.fullname}}</a><br>
{% if person.blurb %} {% if person.blurb %}
{{person.blurb|safe}} {{person.blurb|safe}}

View File

@ -3,7 +3,7 @@
{% block content %} {% block content %}
<h1> <h1>
<a href="{{personexpedition.person.get_absolute_url}}">{{personexpedition.person|safe}}</a> : <a href="{{personexpedition.person.get_absolute_url}}">{{personexpedition.person.fullname|safe}}</a> :
<a href="{{personexpedition.expedition.get_absolute_url}}">{{personexpedition.expedition}}</a> <a href="{{personexpedition.expedition.get_absolute_url}}">{{personexpedition.expedition}}</a>
</h1> </h1>
@ -20,7 +20,7 @@
{% endfor %} {% endfor %}
</p> </p>
<p>Status of all wallets for <b> <p>Status of all wallets for <b>
<a href="/wallets/person/{{personexpedition.person.first_name|safe}}{{personexpedition.person.last_name|safe}}">{{personexpedition.person}}</a> <a href="/wallets/person/{{personexpedition.person.first_name|safe}}{{personexpedition.person.last_name|safe}}">{{personexpedition.person.fullname}}</a>
</b> </b>
</p> </p>
<h3>Table of all trips and surveys aligned by date</h3> <h3>Table of all trips and surveys aligned by date</h3>

View File

@ -3,7 +3,7 @@
{% block title %}One Person Survey scans folders (wallets){% endblock %} {% block title %}One Person Survey scans folders (wallets){% endblock %}
{% block content %} {% block content %}
<h3>Wallets for <a href="{{person.get_absolute_url}}">{{person}}</a> </h3> <h3>Wallets for <a href="{{person.get_absolute_url}}">{{person.fullname}}</a> </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

View File

@ -124,7 +124,8 @@ trogglepatterns = [
# Persons - nasty surname recognition logic fails for 19 people! See also Wallets by person below. # Persons - nasty surname recognition logic fails for 19 people! See also Wallets by person below.
# path('person/<str:name>', person, name="person"), # This is much more complex than it looks.. # path('person/<str:name>', person, name="person"), # This is much more complex than it looks..
re_path(r'^person/(?P<first_name>[A-Z]*[a-z\-\'&;]*)[^a-zA-Z]*(?P<last_name>[a-z\-\']*[^a-zA-Z]*[\-]*[A-Z]*[a-zA-Z\-&;]*)/?', person, name="person"), re_path(r'^person/(?P<first_name>[A-Z]*[a-z\-\'&;]*)[^a-zA-Z]*(?P<last_name>[a-z\-\']*[^a-zA-Z]*[\-]*[A-Z]*[a-zA-Z\-&;]*)/?', person, name="person"),
re_path(r'^personexpedition/(?P<first_name>[A-Z]*[a-z&;]*)[^a-zA-Z]*(?P<last_name>[A-Z]*[a-zA-Z&;]*)/(?P<year>\d+)/?$', personexpedition, name="personexpedition"), #re_path(r'^personexpedition/(?P<first_name>[A-Z]*[a-z&;]*)[^a-zA-Z]*(?P<last_name>[A-Z]*[a-zA-Z&;]*)/(?P<year>\d+)/?$', personexpedition, name="personexpedition"),
path('personexpedition/<slug:slug>/<int:year>', personexpedition, name="personexpedition"),
# Expedition master page & API exports # Expedition master page & API exports
re_path(r'^expedition/(\d+)$', expedition, name="expedition"), re_path(r'^expedition/(\d+)$', expedition, name="expedition"),
@ -210,7 +211,9 @@ trogglepatterns = [
# The data about the wallets themselves, not the scans inside tehm # 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' 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"), # 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"),
path('wallets/person/<slug:slug>', walletslistperson, name="walletslistperson"),
# The tunnel and therion drawings files pageswalletslistcave # The tunnel and therion drawings files pageswalletslistcave