First code import
This commit is contained in:
parent
5d706a10b8
commit
454d05cb7a
12
Makefile
Normal file
12
Makefile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
DEST=/var/gemini/fosdem/
|
||||||
|
|
||||||
|
all: updateinstall
|
||||||
|
|
||||||
|
updateinstall: update install
|
||||||
|
|
||||||
|
update:
|
||||||
|
wget --quiet --output-document schedule.xml https://fosdem.org/2021/schedule/xml
|
||||||
|
./schedule2gemtext.py
|
||||||
|
|
||||||
|
install:
|
||||||
|
rsync -a -v -p --exclude=Makefile --exclude="*~" *.gmi ${DEST}
|
23
README.md
23
README.md
@ -1,3 +1,24 @@
|
|||||||
# fosdem2gemini
|
# fosdem2gemini
|
||||||
|
|
||||||
Convert the FOSDEM schedule to gemtext (text/gemini) format, to be served with Gemini.
|
Convert the [FOSDEM](https://fosdem.org/) schedule to gemtext
|
||||||
|
(`text/gemini`) format, to be served via
|
||||||
|
[Gemini](https://en.wikipedia.org/wiki/Gemini_(protocol)).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Download the program. Be sure you have the
|
||||||
|
[lxml](https://lxml.de/library. Edit sample-index.gmi to suit your
|
||||||
|
needs and rename it index.gmi. Then run the program periodically, for
|
||||||
|
instance through cron:
|
||||||
|
|
||||||
|
```
|
||||||
|
# FOSDEM
|
||||||
|
26 1,5,9,13,17,21 * * * (cd /path/to/FOSDEM; make all)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical details
|
||||||
|
|
||||||
|
The FOSDEM schedule is publically available (from the schedule
|
||||||
|
management system Pentabarf) as a XML file (whch we can retrieve with,
|
||||||
|
for instance, `wget https://fosdem.org/2021/schedule/xml`). The use of
|
||||||
|
a structured format allows us to convert it easily.
|
||||||
|
14
sample-index.gmi
Normal file
14
sample-index.gmi
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# FOSDEM schedule
|
||||||
|
|
||||||
|
A non-official Gemini version of FOSDEM schedule for the 2021 "COVID edition".
|
||||||
|
|
||||||
|
=> allevents.gmi All events, sorted by date and time
|
||||||
|
=> allrooms.gmi All "rooms", with their events
|
||||||
|
|
||||||
|
=> https://fosdem.org/ FOSDEM official page
|
||||||
|
=> https://fosdem.org/2021/schedule/ FOSDEM official schedule
|
||||||
|
|
||||||
|
The schedule here is automatically produced from the FOSDEM "Pentabarf" files.
|
||||||
|
|
||||||
|
This service is maintained by Stéphane Bortzmeyer <stephane+fosdem@bortzmeyer.org>.
|
||||||
|
|
235
schedule2gemtext.py
Executable file
235
schedule2gemtext.py
Executable file
@ -0,0 +1,235 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# https://fosdem.org/2021/schedule/xml
|
||||||
|
FILE = "schedule.xml"
|
||||||
|
INDEX = "index.gmi"
|
||||||
|
ALLEVENTS = "allevents.gmi"
|
||||||
|
ALLROOMS = "allrooms.gmi"
|
||||||
|
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# https://lxml.de/
|
||||||
|
from lxml.html.clean import Cleaner
|
||||||
|
from lxml.html.soupparser import fromstring
|
||||||
|
|
||||||
|
def html2gemini(html):
|
||||||
|
result = ""
|
||||||
|
for child in html:
|
||||||
|
if child.tag == "p":
|
||||||
|
if child.text != None:
|
||||||
|
result += child.text
|
||||||
|
elif child.tag == "div":
|
||||||
|
result += html2gemini(child)
|
||||||
|
elif child.tag == "hr":
|
||||||
|
result += "\n"
|
||||||
|
elif child.tag == "h1":
|
||||||
|
result += "# %s" % child.text
|
||||||
|
elif child.tag == "h2":
|
||||||
|
result += "## %s" % child.text
|
||||||
|
elif child.tag == "h3":
|
||||||
|
result += "### %s" % child.text
|
||||||
|
elif child.tag == "ul" or child.tag == "ol":
|
||||||
|
result += html2gemini(child)
|
||||||
|
elif child.tag == "li":
|
||||||
|
if child.text != None:
|
||||||
|
result += """
|
||||||
|
* %s
|
||||||
|
""" % child.text
|
||||||
|
elif child.tag == "code":
|
||||||
|
result += child.text
|
||||||
|
elif child.tag == "pre":
|
||||||
|
result += """
|
||||||
|
```
|
||||||
|
%s
|
||||||
|
```
|
||||||
|
""" % html2gemini(child)
|
||||||
|
else:
|
||||||
|
print("Unknown tag %s" % child.tag, file=sys.stderr)
|
||||||
|
if child.tag == "div" or child.tag == "p" or child.tag == "ul" or child.tag == "ol":
|
||||||
|
result += """
|
||||||
|
|
||||||
|
"""
|
||||||
|
return result
|
||||||
|
|
||||||
|
def cleanroom(name):
|
||||||
|
components = name.split(".")
|
||||||
|
first_word = components[1][0].upper() + components[1][1:]
|
||||||
|
if len(components) == 2:
|
||||||
|
return first_word
|
||||||
|
elif len(components) == 3:
|
||||||
|
return first_word + " " + components[2]
|
||||||
|
else:
|
||||||
|
return first_word + " " + " ".join(components[2:])
|
||||||
|
|
||||||
|
def typeroom(name):
|
||||||
|
components = name.split(".")
|
||||||
|
if components[0] == "S":
|
||||||
|
return "Stand"
|
||||||
|
elif components[0] in ("D", "F", "M", "B"):
|
||||||
|
return "Room"
|
||||||
|
elif components[0] == "K":
|
||||||
|
return "Keynote"
|
||||||
|
elif components[0] == "I": # Infodesk
|
||||||
|
return ""
|
||||||
|
elif components[0] == "L": # Lightning talks
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
return "Unknown"
|
||||||
|
|
||||||
|
def matrixroom(name):
|
||||||
|
ptype = ""
|
||||||
|
if typeroom(name) == "Stand":
|
||||||
|
ptype = "-stand"
|
||||||
|
return "#%s%s:fosdem.org" % (cleanroom(name).lower(), ptype)
|
||||||
|
|
||||||
|
def shortroom(name):
|
||||||
|
components = name.split(".")
|
||||||
|
return "".join(components).lower()
|
||||||
|
|
||||||
|
def events_of(room):
|
||||||
|
result = []
|
||||||
|
for event in events:
|
||||||
|
if events[event]["room"] == room:
|
||||||
|
result.append(event)
|
||||||
|
return result
|
||||||
|
|
||||||
|
tree = ET.parse(FILE)
|
||||||
|
root = tree.getroot()
|
||||||
|
days = {}
|
||||||
|
rooms = {}
|
||||||
|
events = {}
|
||||||
|
# https://lxml.de/api/lxml.html.clean.Cleaner-class.html
|
||||||
|
html_cleaner = Cleaner(scripts=True, javascript=True, embedded=True,
|
||||||
|
meta=True, page_structure=True, links=True,
|
||||||
|
remove_unknown_tags=True,
|
||||||
|
frames=True,annoying_tags=True,add_nofollow=True,
|
||||||
|
style=False)
|
||||||
|
for day in root.findall("day"):
|
||||||
|
days[day.attrib["index"]] = {"date": day.attrib["date"]}
|
||||||
|
for room in day.findall("room"):
|
||||||
|
rooms[room.attrib["name"]] = {}
|
||||||
|
for event in room.findall("event"):
|
||||||
|
speakers = ""
|
||||||
|
first = True
|
||||||
|
for speaker in event.findall("persons/person"):
|
||||||
|
if not first:
|
||||||
|
speakers += " and "
|
||||||
|
speakers += speaker.text
|
||||||
|
first = False
|
||||||
|
id = event.attrib["id"]
|
||||||
|
if event.find("abstract").text is None:
|
||||||
|
abstract = None
|
||||||
|
else:
|
||||||
|
abstract = html_cleaner.clean_html(event.find("abstract").text)
|
||||||
|
events[id] = {"room": room.attrib["name"],
|
||||||
|
"day": day.attrib["index"],
|
||||||
|
"start": event.find("start").text,
|
||||||
|
"duration": event.find("duration").text,
|
||||||
|
"title": event.find("title").text,
|
||||||
|
"subtitle": event.find("subtitle").text,
|
||||||
|
"type": event.find("type").text,
|
||||||
|
"abstract": abstract,
|
||||||
|
"speakers": speakers
|
||||||
|
}
|
||||||
|
eventfile = open("event-%s.gmi" % event.attrib["id"], "w")
|
||||||
|
if events[id]["subtitle"] is None:
|
||||||
|
subtitle = ""
|
||||||
|
else:
|
||||||
|
subtitle = """
|
||||||
|
%s
|
||||||
|
""" % events[id]["subtitle"]
|
||||||
|
if events[id]["abstract"] is None:
|
||||||
|
abstract = ""
|
||||||
|
else:
|
||||||
|
abstract_root = fromstring(events[id]["abstract"])
|
||||||
|
abstract = """
|
||||||
|
%s
|
||||||
|
""" % html2gemini(abstract_root)
|
||||||
|
if events[id]["speakers"] == "":
|
||||||
|
speakers = ""
|
||||||
|
else:
|
||||||
|
speakers = events[id]["speakers"]
|
||||||
|
print("""# FOSDEM event "%s"
|
||||||
|
|
||||||
|
%s
|
||||||
|
Type %s
|
||||||
|
%s
|
||||||
|
Starts on day %s (%s) at %s (Brussels time, UTC+1) in room %s (duration %s)
|
||||||
|
Matrix room %s
|
||||||
|
%s
|
||||||
|
|
||||||
|
=> . FOSDEM schedule page
|
||||||
|
""" % (events[id]["title"], speakers, events[id]["type"],
|
||||||
|
subtitle, events[id]["day"], days[events[id]["day"]]["date"],
|
||||||
|
events[id]["start"], cleanroom(events[id]["room"]), events[id]["duration"],
|
||||||
|
matrixroom(events[id]["room"]),
|
||||||
|
abstract),
|
||||||
|
file=eventfile)
|
||||||
|
eventfile.close()
|
||||||
|
sortedevents = sorted(events.keys(),
|
||||||
|
key=lambda x: "%s %s" % (events[x]["day"], events[x]["start"]),
|
||||||
|
reverse=False)
|
||||||
|
allevents = open(ALLEVENTS, "w")
|
||||||
|
print("""# All events at FOSDEM
|
||||||
|
""", file=allevents)
|
||||||
|
day = None
|
||||||
|
for event in sortedevents:
|
||||||
|
if typeroom(events[event]["room"]) not in ["Room", "Keynote"]:
|
||||||
|
continue
|
||||||
|
if events[event]["day"] != day:
|
||||||
|
day = events[event]["day"]
|
||||||
|
print("""## Day %s (%s)
|
||||||
|
|
||||||
|
""" % (events[event]["day"], days[events[event]["day"]]["date"]), file=allevents)
|
||||||
|
if events[event]["subtitle"] is None:
|
||||||
|
subtitle = ""
|
||||||
|
else:
|
||||||
|
subtitle = """%s
|
||||||
|
""" % events[event]["subtitle"]
|
||||||
|
if events[event]["speakers"] == "":
|
||||||
|
speakers = ""
|
||||||
|
else:
|
||||||
|
speakers = "By %s" % events[event]["speakers"]
|
||||||
|
print("""### %s
|
||||||
|
%s
|
||||||
|
%s
|
||||||
|
Starts at %s (Brussels time, UTC+1) in room %s
|
||||||
|
|
||||||
|
=> event-%s.gmi Details on the event
|
||||||
|
|
||||||
|
""" % (events[event]["title"], speakers,
|
||||||
|
subtitle,
|
||||||
|
events[event]["start"], cleanroom(events[event]["room"]), event),
|
||||||
|
file=allevents)
|
||||||
|
print("""
|
||||||
|
|
||||||
|
=> . FOSDEM schedule page
|
||||||
|
""", file=allevents)
|
||||||
|
allevents.close()
|
||||||
|
sortedrooms = sorted(rooms.keys(),
|
||||||
|
key=lambda x: cleanroom(x),
|
||||||
|
reverse=False)
|
||||||
|
allrooms = open(ALLROOMS, "w")
|
||||||
|
print("""# All rooms at FOSDEM
|
||||||
|
""", file=allrooms)
|
||||||
|
day = None
|
||||||
|
for room in sortedrooms:
|
||||||
|
print("""## %s "%s"
|
||||||
|
|
||||||
|
""" % (typeroom(room), cleanroom(room)),
|
||||||
|
file=allrooms)
|
||||||
|
room_events = events_of(room)
|
||||||
|
for event in room_events:
|
||||||
|
print("""=> event-%s.gmi %s""" % (event, events[event]["title"]), file=allrooms)
|
||||||
|
print("""
|
||||||
|
|
||||||
|
Matrix room : %s
|
||||||
|
|
||||||
|
=> https://fosdem.org/2021/schedule/room/%s/ Description of this room on FOSDEM site
|
||||||
|
""" % (matrixroom(room), shortroom(room)), file=allrooms)
|
||||||
|
print("""
|
||||||
|
=> . FOSDEM schedule page
|
||||||
|
""", file=allrooms)
|
||||||
|
allrooms.close()
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user