updated page "Termine" and script "refresh_events.py" to include flyer images and color-code event series
This commit is contained in:
parent
b232ccddf8
commit
1fe6cc24f4
BIN
content/images/termine/bls2526.jpg
Normal file
BIN
content/images/termine/bls2526.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 282 KiB |
@ -6,6 +6,9 @@
|
||||
--tr-smallest-width: 350px;
|
||||
--tr-accent-color: #cf0000;
|
||||
|
||||
--tr-events-color-bls: #e66720;
|
||||
--tr-events-color-paw: #a830b3;
|
||||
|
||||
--pico-font-family-sans-serif: "Libertinus Sans", system-ui, "Segoe UI", Roboto,
|
||||
Oxygen, Ubuntu, Cantarell, Helvetica, Arial, "Helvetica Neue", sans-serif,
|
||||
var(--pico-font-family-emoji);
|
||||
@ -388,16 +391,42 @@ article form {
|
||||
color: var(--pico-muted-color);
|
||||
}
|
||||
|
||||
#events-legend {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
span.babelsberger-lesesalon, .babelsberger-lesesalon .event-info {
|
||||
background-color: var(--tr-events-color-bls);
|
||||
}
|
||||
|
||||
span.andere-welten, .andere-welten .event-info {
|
||||
background-color: var(--tr-events-color-paw);
|
||||
}
|
||||
|
||||
/* event list */
|
||||
.event-info {
|
||||
vertical-align: top;
|
||||
max-width: 240px;
|
||||
}
|
||||
|
||||
.event-detail {
|
||||
#content-body .event-detail {
|
||||
vertical-align: top;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: var(--pico-block-spacing-horizontal);
|
||||
|
||||
img.event-flyer {
|
||||
max-height: calc(var(--tr-card-height) * .666);
|
||||
max-width: calc(var(--tr-smallest-width) * .666);
|
||||
min-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* flex settings for layout of cards */
|
||||
.cards {
|
||||
display: flex;
|
||||
|
||||
@ -25,23 +25,34 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="event-info">Wann & Wo</th>
|
||||
<th class="event-detail">Was & Wieso</th>
|
||||
<th col-span="2" class="event-detail">Was & Wieso</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{# loop over all events that have not started yet #}
|
||||
{% for t in page.termine | selectattr("startdate", ">=", DATETIME_NOW) %}
|
||||
<tr>
|
||||
<td class="event-info"><strong>{{ t.startdate | strftime(date_format) }}{% if t.enddate %}–{{t.enddate | strftime(date_format) }}{% elif t.starttime %} {{ t.starttime | strftime(time_format) }}{% endif %}</strong><br>{{ t.location }}</td>
|
||||
<tr{% if "Babelsberger Lesesalon" in t.categories %} class="babelsberger-lesesalon"{% elif "Andere Welten" in t.categories %} class="andere-welten"{% endif %}>
|
||||
<td class="event-info">
|
||||
<strong>{{ t.startdate | strftime(date_format) }}{% if t.enddate %}–{{t.enddate | strftime(date_format) }}{% elif t.starttime %} {{ t.starttime | strftime(time_format) }}{% endif %}</strong><br>{{ t.location }}
|
||||
</td>
|
||||
<td class="event-detail">
|
||||
<div>
|
||||
<p>{{ t.summary }} {% if "Moderation" in t.categories %}(Moderation){%endif%}</p>
|
||||
{% if t.description %}<p>{{ t.description | replace("\n\n", "</p><p>") | replace("\n", "<br>") | replace("</p><br><p>", "</p><p>")}}</p>{% endif %}
|
||||
{% if t.attach %}<p><a href="{{ t.attach }}" target="_blank" title="siehe auch …">Mehr Infos</a></p>{% endif %}
|
||||
{% if t.link %}<p><a href="{{ t.link }}" target="_blank" title="siehe auch …">Mehr Infos</a></p>{% endif %}
|
||||
</div>
|
||||
{% if t.image %}<a href="{{ t.image }}" target="_blank"><img class="event-flyer" src="{{ t.image }}" alt="siehe auch …"></a> {% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="events-legend">
|
||||
<p>Regelmäßige Veranstaltungsreihen:
|
||||
<span class="babelsberger-lesesalon"> Babelsberger Lesesalon </span>
|
||||
<span class="andere-welten"> Andere Welten </span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content_body %}
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import vobject
|
||||
from datetime import datetime, date, time, timedelta
|
||||
import yaml
|
||||
import os, os.path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
# find out where events.ini lives; that's the project root dir
|
||||
@ -58,9 +59,12 @@ cal_prop_datetimes = ["dtend", "due", "dtstart", "duration", "dtstamp", "last-mo
|
||||
# Current time as an aware datetime.datetime object.
|
||||
now = datetime.now().astimezone()
|
||||
|
||||
image_suffixes = ["jpg", "jpeg", "png", "webp", "gif"]
|
||||
internal_hosts = ["", "localhost", "127.0.0.1", "127.0.1.1", "tobias-radloff.de", "www.tobias-radloff.de"]
|
||||
|
||||
|
||||
"""Returns an aware datetime.datetime object based on the given datetime or date. Conversion happens only if necessary, i.e. if d is of type datetime.date or a naive datetime.datetime object."""
|
||||
def fixDatetime(d, t=None, tz=None):
|
||||
def fixDatetime(d: datetime | date, t=None, tz=None):
|
||||
if not tz:
|
||||
tz = now.tzinfo
|
||||
|
||||
@ -84,7 +88,7 @@ def fixDatetime(d, t=None, tz=None):
|
||||
|
||||
|
||||
"""Takes a list of events in vobject representation and converts each event into a dict of Python datatypes."""
|
||||
def extractEventData(events):
|
||||
def extractEventData(events: list) -> list:
|
||||
e_tmp = []
|
||||
|
||||
# iterate over events
|
||||
@ -96,17 +100,59 @@ def extractEventData(events):
|
||||
# we're only interested in a subset of properties
|
||||
if not k in cal_properties:
|
||||
continue
|
||||
# print(f"k is {k} and v is {v}") #DEBUG
|
||||
|
||||
# Usually, v is a list with a single item. We only need the item's value attribute
|
||||
# Usually, v is a list with a single item. We only need the item's value attribute.
|
||||
if len(v) == 1:
|
||||
d[k] = v[0].value
|
||||
# but sometimes the list has more than one item; this only ever happens for the property "categories"
|
||||
# and in this case, each list item's value is itself a one item list (which is stupid but that's how vobject handles categories …)
|
||||
else:
|
||||
continue
|
||||
|
||||
# but sometimes v has more than one item
|
||||
# special case: if k is "categories", each list item's value is itself a one item list (which is stupid but that's how vobject handles categories)
|
||||
if k == "categories":
|
||||
d[k] = [v[i].value[0] for i in range(len(v))]
|
||||
# all other cases: extract each list item's value as is
|
||||
else:
|
||||
d[k] = [v[i].value for i in range(len(v))]
|
||||
e_tmp.append(d)
|
||||
return e_tmp
|
||||
|
||||
|
||||
def handleAttachments(events: list) -> list:
|
||||
for event in events:
|
||||
# skip events without attachment(s)
|
||||
if "attach" not in event.keys():
|
||||
continue
|
||||
|
||||
# turn single attachment into a single element list
|
||||
if type(event["attach"]) == str:
|
||||
event["attach"] = [event["attach"]]
|
||||
|
||||
# loop over attachments
|
||||
for attachment in event["attach"]:
|
||||
parsed_url = urlparse(attachment)
|
||||
isImage = True if parsed_url.path.split(".")[-1] in image_suffixes else False
|
||||
print(f"attachment is {attachment}, parsed_url is {parsed_url} and isImage is {isImage}") #DEBUG
|
||||
|
||||
# handle image link
|
||||
if isImage:
|
||||
# make internal link relative to "/termine"
|
||||
if parsed_url.hostname in internal_hosts:
|
||||
# make sure path starts with a slash
|
||||
if parsed_url.path[0] != "/":
|
||||
parsed_url.path = "/" + parsed_url.path
|
||||
# prefix path with ".." and add it to the event as "image" property
|
||||
event["image"] = ".." + parsed_url.path
|
||||
# use external link as is
|
||||
else:
|
||||
event["image"] = attachment
|
||||
# handle non-image link
|
||||
else:
|
||||
event["link"] = attachment
|
||||
|
||||
print(event)
|
||||
return events
|
||||
|
||||
# create vobject calendar
|
||||
vcal = vobject.newFromBehavior("vcalendar")
|
||||
|
||||
@ -139,6 +185,9 @@ for e in events:
|
||||
# keep only future events
|
||||
events = [e for e in events if e["dtstart"] >= now]
|
||||
|
||||
# sort attached links into page links and image links
|
||||
events = handleAttachments(events)
|
||||
|
||||
# sort by start date
|
||||
events.sort(key=lambda e: e["dtstart"])
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user