Compare commits

..

No commits in common. "59878c16a72eaa34b8b9c186db3c2c4ff37737ba" and "6a0e76440a8d775a1c80db7ffec3d8c6df32ac0c" have entirely different histories.

7 changed files with 159 additions and 84 deletions

View file

@ -5,8 +5,9 @@ import typing
import sqlalchemy import sqlalchemy
import sqlalchemy.orm.decl_api import sqlalchemy.orm.decl_api
from sqlalchemy import func from sqlalchemy import Index, func, text
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
from sqlalchemy.dialects.postgresql import TSVECTOR
from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.orderinglist import ordering_list from sqlalchemy.ext.orderinglist import ordering_list
@ -171,6 +172,22 @@ class Event(TimeStampedModel):
event_type = Column(String) event_type = Column(String)
url = Column(String) url = Column(String)
cancelled = Column(Boolean) cancelled = Column(Boolean)
search_vector = Column(TSVECTOR)
trigger = text(
"""
CREATE TRIGGER event_vector_update BEFORE INSERT OR UPDATE
ON event FOR EACH ROW EXECUTE FUNCTION
tsvector_update_trigger(search_vector, 'pg_catalog.english', title, abstract, description);
"""
)
Index(
"event_search_vector_idx",
search_vector,
postgresql_using="gin",
postgresql_ops={"abstract_search_vector": "gin_trgm_ops"},
)
conference = relationship("Conference", back_populates="events") conference = relationship("Conference", back_populates="events")

View file

@ -182,8 +182,6 @@ def search_people() -> str:
@app.route("/merge", methods=["GET", "POST"]) @app.route("/merge", methods=["GET", "POST"])
def merge() -> str | Response: def merge() -> str | Response:
assert app.config["ADMIN_MODE"]
if flask.request.method == "POST": if flask.request.method == "POST":
search_for = flask.request.form["q"] search_for = flask.request.form["q"]
@ -409,8 +407,6 @@ def search_everything() -> str:
@app.route("/person/<int:person_id>/delete", methods=["POST"]) @app.route("/person/<int:person_id>/delete", methods=["POST"])
def delete_person(person_id: int) -> str | Response: def delete_person(person_id: int) -> str | Response:
assert app.config["ADMIN_MODE"]
item = model.Person.query.get(person_id) item = model.Person.query.get(person_id)
for cp in item.conferences_association: for cp in item.conferences_association:

View file

@ -2,58 +2,74 @@
{% block title %}Conference archive{% endblock %} {% block title %}Conference archive{% endblock %}
{% block content %} {% block content %}
<div> <div class="container">
<h1>Conference archive</h1> <div class="row">
<h1>Conference archive</h1>
<div style="margin-bottom:1rem">
👥
{{ "{:,d}".format(count.conference) }} conferences<br/>
🌍
{{ "{:,d}".format(count.country) }} countries<br/>
📍
{{ "{:,d}".format(count.venue) }} venues<br/>
🎤
{{ "{:,d}".format(count.event) }} talks<br/>
👤
{{ "{:,d}".format(count.person) }} speakers
<a href="{{ url_for("top_speakers_page") }}">top speakers</a><br/>
</div>
<h2>Conferences</h2>
{% 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">
👥
<a href="{{ url_for("conference_page", short_name=item.short_name) }}">{{ item.title }}</a>
📅
{{ item.start.strftime("%d %b %Y") }}
<br/>
{% if item.venue %}
📍
{{ item.venue.name }}
&ndash;
{{ item.venue.city.name }},
{{ item.venue.city.country.name }}
{{ item.venue.city.country.flag }}
<br/>
{% endif %}
{# {#
{% if item.series %} <form action="{{ url_for("search_people") }}">
📃 Series: {{ item.series.name }} <div class="mb-3">
<br/> <label for="q" class="form-label">speaker name</label>
{% endif %} <input type="text" class="form-control" name="q" id="q">
</div>
<button type="submit" class="btn btn-primary">Search</button>
</form>
#} #}
{{ (item.end - item.start).days + 1 }} days, <div style="margin-bottom:1rem">
{{ item.events.count() }} talks, 👥
{{ item.people_detail.count() }} speakers<br/> {{ "{:,d}".format(count.conference) }} conferences<br/>
</div> 🌍
{% endfor %} {{ "{:,d}".format(count.country) }} countries<br/>
📍
{{ "{:,d}".format(count.venue) }} venues<br/>
🎤
{{ "{:,d}".format(count.event) }} talks -
<a href="{{ url_for("events_page") }}">most common titles</a><br/>
👤
{{ "{:,d}".format(count.person) }} speakers
<a href="{{ url_for("top_speakers_page") }}">top speakers</a><br/>
</div>
</div> <h2>Conferences</h2>
{% 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">
👥
<a href="{{ url_for("conference_page", short_name=item.short_name) }}">{{ item.title }}</a>
📅
{{ item.start.strftime("%d %b %Y") }}
<br/>
{% if item.venue %}
📍
{{ item.venue.name }}
&ndash;
{{ item.venue.city.name }},
{{ item.venue.city.country.name }}
{{ item.venue.city.country.flag }}
<br/>
{% endif %}
{#
{% if item.series %}
📃 Series: {{ item.series.name }}
<br/>
{% endif %}
#}
{{ (item.end - item.start).days + 1 }} days,
{{ item.events.count() }} talks,
{{ item.people_detail.count() }} speakers<br/>
</div>
{% endfor %}
</table>
</div>
</div>
{% endblock %} {% endblock %}

View file

@ -1,6 +1,7 @@
<p> <p>
<form action="{{ url_for("search_people") }}"> <form action="{{ url_for("search_people") }}">
<a href="{{ url_for("index") }}">conferences</a> <a href="{{ url_for("index") }}">home</a>
| <a href="{{ url_for("events_page") }}">events</a>
| <a href="{{ url_for("top_speakers_page") }}">speakers</a> | <a href="{{ url_for("top_speakers_page") }}">speakers</a>
<input type="text" class="form-control" placeholder="speaker name" name="q" id="q"> <input type="text" class="form-control" placeholder="speaker name" name="q" id="q">

View file

@ -21,8 +21,8 @@
{% block content %} {% block content %}
<div> <div class="container">
<div> <div class="row">
{% set photo = item.photo_filename() %} {% set photo = item.photo_filename() %}
{% if photo %} {% if photo %}
@ -45,7 +45,7 @@
{% endif %} {% endif %}
</p> </p>
{% if config.ADMIN_MODE %}
{% set search_for = item.name + ' ' + " haswbstatement:P31=Q5" %} {% 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>
@ -80,7 +80,6 @@
<p>No similar names found on Wikidata</p> <p>No similar names found on Wikidata</p>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endif %}
{% set bio_source = item.bio_source() %} {% set bio_source = item.bio_source() %}
{% if bio_source %} {% if bio_source %}

View file

@ -17,12 +17,9 @@
</form> </form>
<p> <p>
{% set count = q.count() %} Found {{ q.count() }} people matching '{{ search_for }}'
Found {{ count }} people matching '{{ search_for }}'
{% if config.ADMIN_MODE and count %}
<a href="{{ url_for("merge", q=search_for) }}">merge</a> <a href="{{ url_for("merge", q=search_for) }}">merge</a>
{% endif %}
</p> </p>
<ul> <ul>

View file

@ -1,34 +1,40 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block style %} {% block style %}
<script src="{{ url_for("static", filename="leader-line.min.js") }}"></script>
<style> <style>
.person { .right-images {
display: flex; position: absolute;
align-items: top; right: -280px;
margin-bottom: 10px; top: 0;
width: 120px;
} }
.info { .left-images {
flex: 1; /* Allow text to take remaining space */ position: absolute;
padding-right: 10px; /* Add spacing between text and image */ right: -140px;
top: 0;
width: 120px;
} }
img.photo {
max-width: 120px; /* Set max width for images */
height: auto; /* Maintain image aspect ratio */ .image {
max-width: 100%;
} }
</style>
.container {
position: relative;
}
</style> </style>
{% endblock %} {% endblock %}
{% set show_images = False %} {% set show_images = True %}
{% block title %}Conference archive{% endblock %} {% block title %}Conference archive{% endblock %}
{% block content %} {% block content %}
<div> <div class="container">
{% if show_images %} {% if show_images %}
@ -82,22 +88,65 @@
{% if loop.first or loop.previtem[1] != count %} {% if loop.first or loop.previtem[1] != count %}
<h4>{{ count }} conferences</h4> <h4>{{ count }} conferences</h4>
{% endif %} {% endif %}
<div class="person"> <div>
{% set photo = person.photo_filename() %} <span id="person-{{ person.id }}">
<span class="info" id="person-{{ person.id }}">
👤 👤
<a href="{{ url_for("person", person_id=person.id) }}">{{ person.name }}</a><br> <a href="{{ url_for("person", person_id=person.id) }}">{{ person.name }}</a>
({{ count }} conferences, {{ person.event_count }} talks) ({{ count }} conferences, {{ person.event_count }} talks)
{% if person.photo_filename() %}📷{% endif %} {% if person.photo_filename() %}📷{% endif %}
{% if person.wikidata_qid %} {% if person.wikidata_qid %}
<a href="https://www.wikidata.org/wiki/{{ person.wikidata_qid }}">Wikidata</a> <a href="https://www.wikidata.org/wiki/{{ person.wikidata_qid }}">Wikidata</a>
{% endif %} {% endif %}
</span> </span>
{% if photo %}
<img class="photo" src="{{ url_for("static", filename=photo) }}" alt="{{ person.name }}">
{% endif %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% endblock %} {% endblock %}
{% block script %}
{% if show_images %}
<script>
{# var person_ids = {{ photo_person_ids | tojson }}; #}
var left = {{ left | tojson }};
var right = {{ right | tojson }};
var lines = {};
window.addEventListener('load', function() {
for(var i =0; i < left.length; i++) {
var id = left[i];
var person = document.getElementById('person-' + id);
var image = document.getElementById('image-' + id);
var line = new LeaderLine(LeaderLine.mouseHoverAnchor(person, 'draw'), image);
line.setOptions({startSocket: 'right', endSocket: 'left', path: 'fluid'});
var line2 = new LeaderLine(LeaderLine.mouseHoverAnchor(image, 'draw'), person);
line2.setOptions({startSocket: 'left', endSocket: 'right', path: 'fluid'});
// lines[id] = line;
}
for(var i =0; i < right.length; i++) {
var id = right[i];
var person = document.getElementById('person-' + id);
var image = document.getElementById('image-' + id);
var line = new LeaderLine(LeaderLine.mouseHoverAnchor(person, 'draw'), image);
line.setOptions({startSocket: 'right', endSocket: 'left', path: 'fluid'});
var line2 = new LeaderLine(LeaderLine.mouseHoverAnchor(image, 'draw'), person);
line2.setOptions({startSocket: 'left', endSocket: 'right', path: 'fluid'});
// lines[id] = line;
}
});
</script>
{% endif %}
{% endblock %}