Improvements

This commit is contained in:
Edward Betts 2023-09-21 04:59:17 +01:00
parent 77f2baea38
commit 9f3a7995a1
9 changed files with 532 additions and 110 deletions

View file

@ -9,7 +9,7 @@ from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.orderinglist import ordering_list from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.schema import Column, ForeignKey from sqlalchemy.schema import Column, ForeignKey
from sqlalchemy.types import Date, DateTime, Integer, String from sqlalchemy.types import Boolean, Date, DateTime, Integer, String
from .database import session from .database import session
@ -25,6 +25,18 @@ class TimeStampedModel(Base):
modified = Column(DateTime, default=func.now(), onupdate=func.now()) modified = Column(DateTime, default=func.now(), onupdate=func.now())
class Series(TimeStampedModel):
"""Conference series."""
__tablename__ = "series"
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
slug = Column(String, unique=True)
wikidata_qid = Column(String, unique=True)
conferences = relationship("Conference", back_populates="series")
class Conference(TimeStampedModel): class Conference(TimeStampedModel):
"""Conference.""" """Conference."""
@ -42,6 +54,9 @@ class Conference(TimeStampedModel):
schedule_xml_url = Column(String) schedule_xml_url = Column(String)
short_name = Column(String, unique=True) short_name = Column(String, unique=True)
venue_id = Column(Integer, ForeignKey("venue.id")) venue_id = Column(Integer, ForeignKey("venue.id"))
online = Column(Boolean)
wikidata_qid = Column(String, unique=True)
series_id = Column(Integer, ForeignKey("series.id"))
people_detail = relationship( people_detail = relationship(
"ConferencePerson", lazy="dynamic", back_populates="conference" "ConferencePerson", lazy="dynamic", back_populates="conference"
@ -56,6 +71,7 @@ class Conference(TimeStampedModel):
) )
venue = relationship("Venue", back_populates="conferences") venue = relationship("Venue", back_populates="conferences")
series = relationship("Series", back_populates="conferences")
class City(TimeStampedModel): class City(TimeStampedModel):
@ -95,6 +111,17 @@ class Country(TimeStampedModel):
cities = relationship("City", back_populates="country") cities = relationship("City", back_populates="country")
@property
def flag(self) -> str:
a = ord("A")
flag_a = 0x1F1E6
char1, char2 = (
flag_a + ord(self.alpha2[0]) - a,
flag_a + ord(self.alpha2[1]) - a,
)
return chr(char1) + chr(char2)
class ConferencePerson(Base): class ConferencePerson(Base):
__tablename__ = "conference_person" __tablename__ = "conference_person"
@ -108,6 +135,16 @@ class ConferencePerson(Base):
person = relationship("Person", back_populates="conferences_association") person = relationship("Person", back_populates="conferences_association")
conference = relationship("Conference", back_populates="people_detail") conference = relationship("Conference", back_populates="people_detail")
@property
def events(self):
return (
Event.query.join(EventPerson)
.filter(
Event.conference == self.conference, EventPerson.person == self.person
)
.order_by(Event.event_date.desc())
)
class Event(TimeStampedModel): class Event(TimeStampedModel):
"""Event.""" """Event."""
@ -140,7 +177,7 @@ class Event(TimeStampedModel):
people = association_proxy( people = association_proxy(
"people_detail", "people_detail",
"person", "person",
creator=lambda i: EventPerson(person=i[0], named_as=i[1]), creator=lambda i: EventPerson(person=i),
) )
@ -162,7 +199,23 @@ class Person(TimeStampedModel):
events = association_proxy("events_association", "event") events = association_proxy("events_association", "event")
conferences_association = relationship("ConferencePerson", back_populates="person") conferences_association = relationship("ConferencePerson", back_populates="person")
conferences = association_proxy("conference_association", "conference") conferences = association_proxy("conferences_association", "conference")
@property
def conference_count(self):
return ConferencePerson.query.filter_by(person_id=self.id).count()
@property
def event_count(self):
return EventPerson.query.filter_by(person_id=self.id).count()
def active_years(self):
q = (
session.query(func.min(Event.event_date), func.max(Event.event_date))
.join(EventPerson)
.filter_by(person_id=self.id)
)
return q.one()
# photos = relationship("PersonPhoto", back_populates="person") # photos = relationship("PersonPhoto", back_populates="person")
@ -176,6 +229,16 @@ class Person(TimeStampedModel):
return q return q
def conference_by_time(self):
q = (
session.query(ConferencePerson)
.join(Conference)
.filter(ConferencePerson.person == self)
.order_by(Conference.start.desc())
)
return q
# class PersonPhoto(TimeStampedModel): # class PersonPhoto(TimeStampedModel):
# """Person photo.""" # """Person photo."""

100
main.py
View file

@ -10,7 +10,7 @@ from typing import cast
import flask import flask
import requests import requests
import sqlalchemy import sqlalchemy
from sqlalchemy import func, update from sqlalchemy import func, or_, update
from werkzeug.wrappers import Response from werkzeug.wrappers import Response
from confarchive import database, model from confarchive import database, model
@ -50,7 +50,7 @@ def wikidata_search(q: str) -> list[dict[str, typing.Any]]:
data = r.json() data = r.json()
time.sleep(1) time.sleep(1)
return cast(dict[str, typing.Any], data["query"]["search"]) return cast(list[dict[str, typing.Any]], data["query"]["search"])
def wikidata_get_item(qid: str) -> typing.Any: def wikidata_get_item(qid: str) -> typing.Any:
@ -58,6 +58,7 @@ def wikidata_get_item(qid: str) -> typing.Any:
if os.path.exists(cache_filename): if os.path.exists(cache_filename):
item = json.load(open(cache_filename)) item = json.load(open(cache_filename))
else: else:
print(qid)
params: dict[str, str | int] = { params: dict[str, str | int] = {
"action": "wbgetentities", "action": "wbgetentities",
"ids": qid, "ids": qid,
@ -78,7 +79,7 @@ def top_speakers() -> sqlalchemy.orm.query.Query:
.join(model.ConferencePerson) .join(model.ConferencePerson)
.group_by(model.Person) .group_by(model.Person)
.order_by(func.count().desc()) .order_by(func.count().desc())
.having(func.count() > 5) .having(func.count() > 3)
) )
return q return q
@ -118,7 +119,9 @@ def person(person_id: int) -> str | Response:
flask.url_for(flask.request.endpoint, person_id=person_id) flask.url_for(flask.request.endpoint, person_id=person_id)
) )
return flask.render_template("person.html", item=item, Event=model.Event) return flask.render_template(
"person.html", item=item, Event=model.Event, plural=plural
)
@app.route("/event/<int:event_id>") @app.route("/event/<int:event_id>")
@ -132,7 +135,9 @@ def conference_page(short_name: str) -> str:
item = model.Conference.query.filter_by(short_name=short_name).one_or_none() item = model.Conference.query.filter_by(short_name=short_name).one_or_none()
if item is None: if item is None:
flask.abort(404) flask.abort(404)
return flask.render_template("conference.html", item=item) return flask.render_template(
"conference.html", item=item, person_image_filename=person_image_filename
)
@app.route("/people") @app.route("/people")
@ -157,9 +162,16 @@ def merge() -> str | Response:
merge_to_id = min(item_ids) merge_to_id = min(item_ids)
other_ids = [i for i in item_ids if i != merge_to_id] other_ids = [i for i in item_ids if i != merge_to_id]
name_from_person_id = flask.request.form["name"]
print(other_ids, "->", merge_to_id) print(other_ids, "->", merge_to_id)
with database.session.begin(): with database.session.begin():
if merge_to_id != name_from_person_id:
merge_to = model.Person.query.get(merge_to_id)
name_from_person = model.Person.query.get(name_from_person_id)
merge_to.name = name_from_person.name
print("update ConferencePerson") print("update ConferencePerson")
database.session.execute( database.session.execute(
update(model.ConferencePerson) update(model.ConferencePerson)
@ -224,15 +236,37 @@ def index() -> str:
"conference": model.Conference.query.count(), "conference": model.Conference.query.count(),
"event": model.Event.query.count(), "event": model.Event.query.count(),
"person": model.Person.query.count(), "person": model.Person.query.count(),
"country": model.Country.query.count(),
"venue": model.Venue.query.count(),
} }
return flask.render_template("index.html", items=q, count=count) return flask.render_template("index.html", items=q, count=count)
def plural(num: int, label: str) -> str:
return f'{num:,d} {label}{"s" if num != 1 else ""}'
def speaker_counts():
sql = """
select num, count(*)
from (select person_id, count(*) as num from conference_person group by person_id) a
group by num
order by num
"""
return database.session.execute(sql)
@app.route("/speakers") @app.route("/speakers")
def top_speakers_page() -> str: def top_speakers_page() -> str:
"""Top speakers page.""" """Top speakers page."""
return flask.render_template("top_speakers.html", top_speakers=top_speakers()) return flask.render_template(
"top_speakers.html",
top_speakers=top_speakers(),
speaker_counts=speaker_counts(),
plural=plural,
)
@app.route("/country") @app.route("/country")
@ -264,11 +298,11 @@ def link_to_wikidata() -> str:
for person, num in top_speakers2(): for person, num in top_speakers2():
if person.wikidata_qid: if person.wikidata_qid:
continue continue
search_hits = wikidata_search(f'"{person.name}"') search_hits = wikidata_search(person.name)
if not search_hits: if not search_hits:
continue continue
if len(search_hits) > 10: if len(search_hits) > 14:
continue continue
hits = [] hits = []
@ -299,5 +333,55 @@ def link_to_wikidata() -> str:
return flask.render_template("wikidata.html", items=items) return flask.render_template("wikidata.html", items=items)
@app.route("/search")
def search_everything() -> str:
search_for = flask.request.args["q"]
if not search_for:
return flask.render_template("search_everything.html")
search_for = search_for.strip()
like = f"%{search_for}%"
people = model.Person.query.filter(model.Person.name.ilike(like)).order_by(
model.Person.name
)
events = model.Event.query.filter(
or_(model.Event.abstract.ilike(like), model.Event.description.ilike(like))
).order_by(model.Event.event_date)
return flask.render_template(
"search_everything.html", people=people, events=events, search_for=search_for
)
@app.route("/person/<int:person_id>/delete", methods=["POST"])
def delete_person(person_id: int) -> str | Response:
item = model.Person.query.get(person_id)
for cp in item.conferences_association:
database.session.delete(cp)
for ep in item.events_association:
database.session.delete(ep)
database.session.delete(item)
database.session.commit()
return flask.redirect(flask.url_for("index"))
def person_image_filename(person_id):
person = model.Person.query.get(person_id)
return os.path.join("wikidata_photo", "thumb", person.wikidata_photo[0])
for filename in person.wikidata_photo:
face_crop = "face_1_" + filename
full = os.path.join("static", "wikidata_photo", "face_cropped", face_crop)
if os.path.exists(full):
return os.path.join("wikidata_photo", "face_cropped", face_crop)
return os.path.join("wikidata_photo", "thumb", person.wikidata_photo[0])
if __name__ == "__main__": if __name__ == "__main__":
app.run(host="0.0.0.0", port=5002) app.run(host="0.0.0.0", port=5002)

View file

@ -1,5 +1,28 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block style %}
<style>
.image-container {
width: 200px; /* Adjust this to your desired square size */
height: 240px; /* Same as width for a square */
display: inline-flex; /* Use inline-flex to display containers horizontally */
margin-right: 10px; /* Add some spacing between images (adjust as needed) */
justify-content: center; /* Horizontally center the content */
align-items: center; /* Vertically center the content */
overflow: hidden; /* Hide overflowing image parts */
}
.image-container img {
max-width: 100%;
max-height: 100%;
object-fit: cover; /* Crop and scale the image to fit the container */
object-position: center; /* Center the cropping horizontally */
}
</style>
{% endblock %}
{% set show_images = True %}
{% block title %}{{ item.title }}{% endblock %} {% block title %}{{ item.title }}{% endblock %}
{% block content %} {% block content %}
@ -8,36 +31,84 @@
<h1>{{ item.title }}</h1> <h1>{{ item.title }}</h1>
<p><a href="{{ url_for("index") }}">home</a></p> <p><a href="{{ url_for("index") }}">home</a></p>
<ul> <div>
<li>start: {{ item.start }}</li> <div>series: {{ item.series.name }}
<li>end: {{ item.end }}</li> {% if item.series.wikidata_qid %}
{% if days %} <a href="https://www.wikidata.org/wiki/{{ item.series.wikidata_qid }}">Wikidata</a>
<li>days: {{ item.days }}</li> {% endif %}
</div>
<div>start: {{ item.start }}</div>
<div>end: {{ item.end }}</div>
{% if days %}
<div>days: {{ item.days }}</div>
{% endif %}
{# <div>short name: {{ item.short_name }}</div> #}
{% if item.venue %}
{% set country = item.venue.city.country %}
<div>
venue: {{ item.venue.name }}
{% if item.venue.wikidata_qid %}
<a href="https://www.wikidata.org/wiki/{{ item.venue.wikidata_qid }}">Wikidata</a>
{% endif %}
</div>
<div>
city: {{ item.venue.city.name }}
{% if item.venue.city.wikidata_qid %}
<a href="https://www.wikidata.org/wiki/{{ item.venue.city.wikidata_qid }}">Wikidata</a>
{% endif %}
</div>
<div>country: {{ country.name }} {{ country.flag }}</div>
{% endif %}
{% if item.wikidata_qid %}
<div>wikidata: <a href="https://www.wikidata.org/wiki/{{ item.wikidata_qid }}">{{ item.wikidata_qid }}</a></div>
{% endif %}
</div>
{% if show_images %}
<div>
{% for person in item.people %}
{% if person.wikidata_photo %}
<span class="image-container">
<a href="{{ url_for("person", person_id=person.id) }}">
<img src="{{ url_for("static", filename=person_image_filename(person.id)) }}" alt="{{ person.name}}" title="{{ person.name}}">
</a>
</span>
{% endif %}
{% endfor %}
</div>
{% endif %} {% endif %}
<li>short name: {{ item.short_name }}</li>
<li>country: {{ item.country or "n/a" }}</li>
</ul>
<h3>Talks</h3> <h3>Talks</h3>
<p>{{ item.events.count() }} talks</p> <p>{{ item.events.count() }} talks</p>
{% for event in item.events %} {% for event in item.events %}
<div class="card my-2"> <div>
<div class="card-body"> <div>
<h5 class="card-title"> <p>
<a href="{{ url_for("event_page", event_id=event.id) }}">{{ event.title }}</a> 🎤
</h5> <a href="{{ url_for("event_page", event_id=event.id) }}">{{ event.title }}</a><br>
<h6 class="card-subtitle mb-2 text-body-secondary">
Speakers:
{% for p in event.people %}
👤
<a href="{{ url_for("person", person_id=p.id) }}">{{ p.name }}</a>
{% endfor %}<br>
{% if event.event_date %} {% if event.event_date %}
{{ event.event_date.strftime("%d %b %Y at %H:%M") }} 📅 {{ event.event_date.strftime("%a, %d %b %Y at %H:%M") }}
{% else %} {% else %}
event date missing event date missing
{% endif %} {% endif %}
</h6>
<p class="card-text"> <a class="event-detail-toggle" href="#">show details</a><br>
</p>
<div class="event-detail" id="event_{{event.id }}" style="display:none">
{% if event.url %} {% if event.url %}
<a href="{{ event.url }}">talk on conference website</a> <p><a href="{{ event.url }}">talk on conference website</a></p>
{% endif %} {% endif %}
{% if event.abstract %} {% if event.abstract %}
@ -60,12 +131,7 @@
</p> </p>
{% endif %} {% endif %}
<p class="card-text"> </div>
Speakers:
{% for p in event.people %}
<a href="{{ url_for("person", person_id=p.id) }}">{{ p.name }}</a>
{% endfor %}
</p>
</div> </div>
</div> </div>
@ -74,3 +140,32 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %}
<script>
// Get all elements with the class "event-detail-toggle"
var toggleLinks = document.querySelectorAll(".event-detail-toggle");
// Loop through each toggle link and attach a click event handler
toggleLinks.forEach(function(link) {
link.addEventListener("click", function(e) {
e.preventDefault(); // Prevent the default link behavior
// Find the parent div of the clicked link
var parentDiv = this.closest("div");
// Find the element with class "event-detail" inside the parent div
var detailElement = parentDiv.querySelector(".event-detail");
// Toggle the display of the detail element
if (detailElement.style.display === "none" || detailElement.style.display === "") {
detailElement.style.display = "block";
this.textContent = "hide detail"; // Change the link text
} else {
detailElement.style.display = "none";
this.textContent = "show detail"; // Change the link text
}
});
});
</script>
{% endblock %}

View file

@ -33,6 +33,7 @@
{% endif %} {% endif %}
{% if item.description %}
<p class="card-text"> <p class="card-text">
{% if "<" in item.description %} {% if "<" in item.description %}
{{ item.description | safe }} {{ item.description | safe }}
@ -40,6 +41,7 @@
{{ item.description }} {{ item.description }}
{% endif %} {% endif %}
</p> </p>
{% endif %}
<p class="card-text"> <p class="card-text">
Speakers: Speakers:
{% for p in item.people %} {% for p in item.people %}

View file

@ -15,27 +15,49 @@
<button type="submit" class="btn btn-primary">Search</button> <button type="submit" class="btn btn-primary">Search</button>
</form> </form>
<div style="margin-bottom:1rem"> <div style="margin-bottom:1rem">
👥
{{ "{:,d}".format(count.conference) }} conferences<br/> {{ "{:,d}".format(count.conference) }} conferences<br/>
🌍
{{ "{:,d}".format(count.country) }} countries<br/>
📍
{{ "{:,d}".format(count.venue) }} venues<br/>
🎤
{{ "{:,d}".format(count.event) }} talks - {{ "{:,d}".format(count.event) }} talks -
<a href="{{ url_for("events_page") }}">most common titles</a><br/> <a href="{{ url_for("events_page") }}">most common titles</a><br/>
👤
{{ "{:,d}".format(count.person) }} speakers {{ "{:,d}".format(count.person) }} speakers
<a href="{{ url_for("top_speakers_page") }}">top speakers</a><br/> <a href="{{ url_for("top_speakers_page") }}">top speakers</a><br/>
</div> </div>
<h2>Conferences</h2>
{% for item in items %} {% for item in items %}
{% if loop.first or item.start.year != loop.previtem.start.year %}
<h3>{{ item.start.year }}</h3>
{% endif %}
<div style="margin-bottom:1.5rem"> <div style="margin-bottom:1.5rem">
👥
<a href="{{ url_for("conference_page", short_name=item.short_name) }}">{{ item.title }}</a> <a href="{{ url_for("conference_page", short_name=item.short_name) }}">{{ item.title }}</a>
📅
{{ item.start.strftime("%d %b %Y") }} {{ item.start.strftime("%d %b %Y") }}
<br/> <br/>
{% if item.venue %} {% if item.venue %}
📍
{{ item.venue.name }} {{ item.venue.name }}
&ndash; &ndash;
{{ item.venue.city.name }}, {{ item.venue.city.name }},
{{ item.venue.city.country.name }} {{ item.venue.city.country.name }}
{{ item.venue.city.country.flag }}
<br/> <br/>
{% endif %} {% endif %}
{#
{% if item.series %}
📃 Series: {{ item.series.name }}
<br/>
{% endif %}
#}
{{ (item.end - item.start).days + 1 }} days, {{ (item.end - item.start).days + 1 }} days,
{{ item.events.count() }} talks, {{ item.events.count() }} talks,

View file

@ -25,7 +25,6 @@
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" name="person_id" value="{{ item.id }}" id="person{{ item.id }}"> <input class="form-check-input" type="checkbox" name="person_id" value="{{ item.id }}" id="person{{ item.id }}">
<label class="form-check-label" for="person{{ item.id }}"> <label class="form-check-label" for="person{{ item.id }}">
{{ item.id }}
<a href="{{ url_for("person", person_id=item.id) }}">{{ item.name }}</a> <a href="{{ url_for("person", person_id=item.id) }}">{{ item.name }}</a>
{% if item.wikidata_qid %} {% if item.wikidata_qid %}
@ -33,6 +32,10 @@
<a href="https://www.wikidata.org/wiki/{{ item.wikidata_qid }}">{{ item.wikidata_qid }} on Wikidata</a> <a href="https://www.wikidata.org/wiki/{{ item.wikidata_qid }}">{{ item.wikidata_qid }} on Wikidata</a>
{% endif %} {% endif %}
</label> </label>
<input class="form-check-input" type="radio" name="name" value="{{ item.id }}" id="name{{ item.id }}">
<label class="form-check-label" for="name{{ item.id }}">use this name</label><br>
{% for conf in item.conferences %} 👥{{ conf.title }}{% endfor %}
</div> </div>
{% endfor %} {% endfor %}

View file

@ -6,25 +6,24 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<h1>{{ item.name }}</h1> <h1>{{ item.name }}</h1>
<p><a href="{{ url_for("index") }}">home</a></p>
<p>
<h3>Conferences</h3> 👥 {{ plural(item.conference_count, "conference") }}<br/>
{% for apperance in item.conferences_association %} 🎤 {{ plural(item.event_count, "talk") }}<br/>
{% set conf = apperance.conference %} {% set start, end = item.active_years() %}
📅 Years active: {{ start.year }} to {{end.year }}
<div class="card my-2"> {% if item.wikidata_qid %}
<div class="card-body"> <br/>
<h5 class="card-title">{{ conf.id }}: {{ conf.title }}</h5> 📊 Wikidata: <a href="https://www.wikidata.org/wiki/{{ item.wikidata_qid }}">{{ item.wikidata_qid }}</a>
<p class="card-text"> {% endif %}
{% if apperance.bio %}{{ apperance.bio | safe }}{% else %}No speaker biography.{% endif %}
</p> </p>
</div>
</div>
{% endfor %}
{% set search_for = '"' + item.name + '" ' + " haswbstatement:P31=Q5" %} {% if item.wikidata_photo %}
<img src="{{ url_for("static", filename="wikidata_photo/thumb/" + item.wikidata_photo.0) }}">
{% endif %}
{% set search_for = item.name + ' ' + " haswbstatement:P31=Q5" %}
<p><a href="https://www.wikidata.org/w/index.php?search={{ search_for | urlencode }}&title=Special%3ASearch&ns0=1&ns120=1">Search for {{ item.name }} on Wikidata</a></p> <p><a href="https://www.wikidata.org/w/index.php?search={{ search_for | urlencode }}&title=Special%3ASearch&ns0=1&ns120=1">Search for {{ item.name }} on Wikidata</a></p>
<form method="POST"> <form method="POST">
@ -39,60 +38,111 @@
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary">Submit</button>
</form> </form>
<h3>Talks</h3> <form method="POST" action="{{ url_for("delete_person", person_id=item.id) }}">
<p>Has {{ item.events_association.count() }} events</p> <button type="submit" class="btn btn-primary">delete</button>
{% for event in item.events_by_time() %} </form>
<div class="card my-2">
<div class="card-body">
<h5 class="card-title">
{% for apperance in item.conference_by_time() %}
{% set conf = apperance.conference %}
<div>
<h3>👥 {{ conf.title }}
<small>📅 {{ conf.start.strftime("%d %b %Y") }}</small>
</h3>
{% if apperance.bio %}<p>Biography: {{ apperance.bio | safe }}</p>{% endif %}
</div>
{% for event in apperance.events %}
<div>
<h4>
🎤
<a href="{{ url_for("event_page", event_id=event.id) }}">{{ event.title }}</a> <a href="{{ url_for("event_page", event_id=event.id) }}">{{ event.title }}</a>
</h5> <small>
<h6 class="card-subtitle mb-2 text-body-secondary">
{{ event.conference.title }}
&mdash;
{% if event.event_date %} {% if event.event_date %}
{{ event.event_date.strftime("%d %b %Y") }} {{ event.event_date.strftime("%d %b %Y") }}
{% else %} {% else %}
event date missing event date missing
{% endif %} {% endif %}
</h6> <a class="event-detail-toggle" href="#">show details</a>
<p class="card-text"> </small>
</h4>
<div class="event-detail" id="event_{{event.id }}" style="display:none">
<p>
{% if event.url %} {% if event.url %}
<a href="{{ event.url }}">{{ event.title }} on conference website</a> <a href="{{ event.url }}">talk on conference website</a>
{% endif %} {% endif %}
<p>
{% if event.abstract %} {% if event.abstract %}
<p class="card-text"> <div>
{% if "<" in event.abstract %} {% if "<" in event.abstract %}
{{ event.abstract | safe }} {{ event.abstract | safe }}
{% else %} {% else %}
{{ event.abstract }} {{ event.abstract }}
{% endif %} {% endif %}
</p> </div>
{% endif %} {% endif %}
{% if event.description %} {% if event.description %}
<p class="card-text"> <div>
{% if "<" in event.description %} {% if "<" in event.description %}
{{ event.description | safe }} {{ event.description | safe }}
{% else %} {% else %}
{{ event.description }} {{ event.description }}
{% endif %} {% endif %}
</p> </div>
{% endif %} {% endif %}
<p class="card-text"> <div>
{% for p in event.people %} {% for p in event.people %}
{% if p.id != item.id %} {% if p.id != item.id %}
<a href="{{ url_for(request.endpoint, person_id=p.id) }}">{{ p.name }}</a> <a href="{{ url_for(request.endpoint, person_id=p.id) }}">{{ p.name }}</a>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</p>
</div> </div>
</div> </div>
</div>
{% endfor %} {% endfor %}
{% endfor %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %}
<script>
// Get all elements with the class "event-detail-toggle"
var toggleLinks = document.querySelectorAll(".event-detail-toggle");
// Loop through each toggle link and attach a click event handler
toggleLinks.forEach(function(link) {
link.addEventListener("click", function(e) {
e.preventDefault(); // Prevent the default link behavior
// Find the parent div of the clicked link
var parentDiv = this.closest("div");
// Find the element with class "event-detail" inside the parent div
var detailElement = parentDiv.querySelector(".event-detail");
// Toggle the display of the detail element
if (detailElement.style.display === "none" || detailElement.style.display === "") {
detailElement.style.display = "block";
this.textContent = "hide detail"; // Change the link text
} else {
detailElement.style.display = "none";
this.textContent = "show detail"; // Change the link text
}
});
});
</script>
{% endblock %}

View file

@ -0,0 +1,97 @@
{% extends "base.html" %}
{% block title %}Conference archive{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<h1>Conference archive</h1>
<p><a href="{{ url_for("index") }}">home</a></p>
<form action="{{ url_for("search_everything") }}">
<div class="mb-3">
<input type="text" class="form-control" name="q" id="q" value="{{ search_for }}">
</div>
<button type="submit" class="btn btn-primary">Search</button>
</form>
{% if search_for %}
<h3>Talks</h3>
<p>Found {{ events.count() }} events matching '{{ search_for }}'</p>
{% for event in events %}
<div>
<p>
🎤
<a href="{{ url_for("event_page", event_id=event.id) }}">{{ event.title }}</a><br>
Speakers:
{% for p in event.people %}
👤
<a href="{{ url_for("person", person_id=p.id) }}">{{ p.name }}</a>
{% endfor %}<br>
👥 <a href="{{ url_for("conference_page", short_name=event.conference.short_name) }}">{{ event.conference.title }}</a><br>
{% if event.event_date %}
📅 {{ event.event_date.strftime("%a, %d %b %Y at %H:%M") }}
{% else %}
event date missing
{% endif %}
</p>
{% if event.abstract %}
<p class="card-text">
{% if "<" in event.abstract %}
{{ event.abstract | safe }}
{% else %}
{% for line in event.abstract.splitlines() %}
{{ line }}<br>
{% endfor %}
{% endif %}
</p>
{% endif %}
{% if event.description and event.description != event.abstract %}
<p class="card-text">
{% if "<" in event.description %}
{{ event.description | safe }}
{% else %}
{% for line in event.description.splitlines() %}
{{ line }}<br>
{% endfor %}
{% endif %}
</p>
{% endif %}
</div>
{% endfor %}
<h3>People</h3>
<p>
Found {{ people.count() }} people matching '{{ search_for }}'
</p>
<ul>
{% for item in people %}
<li>
<a href="{{ url_for("person", person_id=item.id) }}">{{ item.name }}</a>
{% if item.wikidata_qid %}
&mdash;
<a href="https://www.wikidata.org/wiki/{{ item.wikidata_qid }}">{{ item.wikidata_qid }} on Wikidata</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -3,8 +3,6 @@
{% block title %}Conference archive{% endblock %} {% block title %}Conference archive{% endblock %}
{% block content %} {% block content %}
<div class="container">
<div class="row">
<h1>Conference archive</h1> <h1>Conference archive</h1>
<form action="{{ url_for("search_people") }}"> <form action="{{ url_for("search_people") }}">
@ -15,23 +13,31 @@
<button type="submit" class="btn btn-primary">Search</button> <button type="submit" class="btn btn-primary">Search</button>
</form> </form>
<h3>Speaker/conference frequency distribution</h3>
<p>Distribution of speakers by conference count.</p>
{% for conf_count, speaker_count in speaker_counts %}
<div>
{{ plural(conf_count, "conference") }}:
{{ plural(speaker_count, "speaker") }}
</div>
{% endfor %}
<h3>Top speakers</h3> <h3>Top speakers</h3>
<ul> <ul>
{% for person, count in top_speakers %} {% for person, count in top_speakers %}
<li> <div>
👤
<a href="{{ url_for("person", person_id=person.id) }}">{{ person.name }}</a> <a href="{{ url_for("person", person_id=person.id) }}">{{ person.name }}</a>
({{ count }}) ({{ count }} conferences, {{ person.event_count }} talks)
{% if person.wikidata_photo %}📷{% endif %}
{% if person.wikidata_qid %} {% if person.wikidata_qid %}
&mdash; <a href="https://www.wikidata.org/wiki/{{ person.wikidata_qid }}">Wikidata</a>
<a href="https://www.wikidata.org/wiki/{{ person.wikidata_qid }}">{{ person.wikidata_qid }} on Wikidata</a>
{% endif %} {% endif %}
</li> </div>
{% endfor %} {% endfor %}
</ul>
</div>
</div>
{% endblock %} {% endblock %}