init
This commit is contained in:
169
generate.py
Normal file
169
generate.py
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
import mailbox
|
||||||
|
import jinja2
|
||||||
|
import email.header
|
||||||
|
import email.utils
|
||||||
|
import yaml
|
||||||
|
import datetime
|
||||||
|
import sys
|
||||||
|
import pypandoc
|
||||||
|
import argparse
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
CONFIG_FILE = "generator.conf"
|
||||||
|
|
||||||
|
WEEKDAYS = { 0: "Montag",
|
||||||
|
1: "Dienstag",
|
||||||
|
2: "Mittwoch",
|
||||||
|
3: "Donnerstag",
|
||||||
|
4: "Freitag",
|
||||||
|
5: "Samstag",
|
||||||
|
6: "Sonntag" }
|
||||||
|
|
||||||
|
def decode_header(header):
|
||||||
|
return "".join([ x[0].decode(x[1] or "ascii") if isinstance(x[0], bytes) else x[0] for x in email.header.decode_header(header) ])
|
||||||
|
|
||||||
|
class Top:
|
||||||
|
def __init__(self, title=None, sender=None, body=None, protostub=None, message=None):
|
||||||
|
if message:
|
||||||
|
self.title = decode_header(message["Subject"])[6:]
|
||||||
|
real_name, address = email.utils.parseaddr(message["From"])
|
||||||
|
self.sender = real_name or address
|
||||||
|
self.body = message.get_payload().rpartition("\n--")[0]
|
||||||
|
elif title:
|
||||||
|
self.title = title
|
||||||
|
self.sender = sender
|
||||||
|
self.body = body
|
||||||
|
self.protostub = protostub
|
||||||
|
else:
|
||||||
|
raise ValueError("One of title or message is needed")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<TOP "+self.title+">"
|
||||||
|
|
||||||
|
# from https://stackoverflow.com/questions/6558535/find-the-date-for-the-first-monday-after-a-given-a-date
|
||||||
|
def next_weekday(d, weekday):
|
||||||
|
days_ahead = weekday - d.weekday()
|
||||||
|
if days_ahead < 0: # Target day already happened this week
|
||||||
|
days_ahead += 7
|
||||||
|
return d + datetime.timedelta(days_ahead)
|
||||||
|
|
||||||
|
def last_weekday(d, weekday):
|
||||||
|
days_ahead = weekday - d.weekday()
|
||||||
|
if days_ahead >= 0: # Target day already happened this week
|
||||||
|
days_ahead -= 7
|
||||||
|
return d + datetime.timedelta(days_ahead)
|
||||||
|
|
||||||
|
def wiki2latex(intext):
|
||||||
|
intext = intext.replace(":\n", ":\n\n")
|
||||||
|
return pypandoc.convert_text(intext, 'latex', format='md')
|
||||||
|
|
||||||
|
def j2replace(intext):
|
||||||
|
return j2env.from_string(intext).render(context)
|
||||||
|
|
||||||
|
def date(indate):
|
||||||
|
return indate.strftime("%d.%m.%Y")
|
||||||
|
|
||||||
|
def time(intime):
|
||||||
|
return intime.strftime("%H:%M")
|
||||||
|
|
||||||
|
def weekday(indate):
|
||||||
|
return WEEKDAYS[indate.weekday()]
|
||||||
|
|
||||||
|
def conf2top(top):
|
||||||
|
sender = None
|
||||||
|
body = None
|
||||||
|
protostub = None
|
||||||
|
try:
|
||||||
|
sender = top["sender"]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
body = top["body"]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
protostub = top["protostub"]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return Top(top["title"], sender, body, protostub)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--config", default=CONFIG_FILE)
|
||||||
|
mode = parser.add_mutually_exclusive_group(required=True)
|
||||||
|
mode.add_argument("--invite", action="store_true")
|
||||||
|
mode.add_argument("--presentation", action="store_true")
|
||||||
|
mode.add_argument("--protocol", action="store_true")
|
||||||
|
parser.add_argument("--write-mbox", action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
config = yaml.full_load(open(args.config))
|
||||||
|
|
||||||
|
if args.invite:
|
||||||
|
template_file = config["invite_template_file"]
|
||||||
|
elif args.presentation:
|
||||||
|
template_file = config["presentation_template_file"]
|
||||||
|
elif args.protocol:
|
||||||
|
template_file = config["protocol_template_file"]
|
||||||
|
else:
|
||||||
|
raise Exception("Should never happen")
|
||||||
|
|
||||||
|
j2env = jinja2.Environment()
|
||||||
|
j2env.filters["wiki2latex"] = wiki2latex
|
||||||
|
j2env.filters["j2replace"] = j2replace
|
||||||
|
j2env.filters["date"] = date
|
||||||
|
j2env.filters["time"] = time
|
||||||
|
j2env.filters["weekday"] = weekday
|
||||||
|
template = j2env.from_string(open(template_file).read())
|
||||||
|
|
||||||
|
mbox = mailbox.mbox(config["top_mbox_file"])
|
||||||
|
|
||||||
|
current_date = next_weekday(datetime.date.today(), config["default_weekday"])
|
||||||
|
next_date = next_weekday(current_date, config["default_weekday"])
|
||||||
|
last_date = last_weekday(current_date, config["default_weekday"])
|
||||||
|
|
||||||
|
time = datetime.time.fromisoformat(config["default_time"])
|
||||||
|
|
||||||
|
pre_tops = []
|
||||||
|
post_tops = []
|
||||||
|
|
||||||
|
for top in config["pre_tops"]:
|
||||||
|
pre_tops.append(conf2top(top))
|
||||||
|
|
||||||
|
for top in config["post_tops"]:
|
||||||
|
post_tops.append(conf2top(top))
|
||||||
|
|
||||||
|
email_tops = []
|
||||||
|
|
||||||
|
for message in mbox:
|
||||||
|
top = Top(message=message)
|
||||||
|
email_tops.append(top)
|
||||||
|
|
||||||
|
to = pre_tops + email_tops + post_tops
|
||||||
|
|
||||||
|
context = {"to": to,
|
||||||
|
"redeleitung": config["redeleitung"],
|
||||||
|
"protokoll": config["protokoll"],
|
||||||
|
"date": current_date,
|
||||||
|
"time": time,
|
||||||
|
"place": config["place"],
|
||||||
|
"next_date": next_date,
|
||||||
|
"last_date": last_date,
|
||||||
|
"meeting_link": config["meeting_link"],
|
||||||
|
"email_tops": email_tops,
|
||||||
|
"WEEKDAYS": WEEKDAYS}
|
||||||
|
|
||||||
|
if args.write_mbox:
|
||||||
|
msg = email.message.EmailMessage()
|
||||||
|
msg.set_content(template.render(context))
|
||||||
|
msg["Subject"] = j2env.from_string(config["invite_subject"]).render(context)
|
||||||
|
msg["From"] = config["redeleitung"]["email"]
|
||||||
|
msg["To"] = config["invite_mail"]
|
||||||
|
mbox = mailbox.mbox(config["mbox_out"])
|
||||||
|
mbox.add(msg)
|
||||||
|
mbox.close()
|
||||||
|
print(mbox)
|
||||||
|
else:
|
||||||
|
print(template.render(context))
|
||||||
60
generator.conf.example
Normal file
60
generator.conf.example
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
redeleitung:
|
||||||
|
name: Rede Leitung
|
||||||
|
email: rede.leitung@fsmi.uni-karlsruhe.de
|
||||||
|
|
||||||
|
protokoll:
|
||||||
|
name: Protokoll Ant
|
||||||
|
|
||||||
|
pre_tops:
|
||||||
|
- title: Begrüßung
|
||||||
|
protostub: Der FSR wird begrüßt
|
||||||
|
- title: Feststellung der Beschlussfähigkeit
|
||||||
|
protostub: Der FSR ist beschlussfähig
|
||||||
|
- title: Tagesordnung
|
||||||
|
body: '\tableofcontents'
|
||||||
|
protostub: '{% for top in to %}# {{top.title}}
|
||||||
|
|
||||||
|
{% endfor %}'
|
||||||
|
- title: Unbeantwortete E-Mails
|
||||||
|
protostub: '<intern></intern>Die E-Mails wurden verteilt'
|
||||||
|
- title: Unveröffentlichte Protokolle
|
||||||
|
body: "* FSR-Protokoll vom {{last_date|date}}"
|
||||||
|
- title: Berichte
|
||||||
|
protostub:
|
||||||
|
|
||||||
|
post_tops:
|
||||||
|
- title: Nächster FSR
|
||||||
|
body: 'Wann: {{next_date.strftime("%d.%m.%Y")}} {{time.strftime("%H:%M")}} \linebreak
|
||||||
|
Wo: {{place}} \linebreak
|
||||||
|
Redeleitung: ? \linebreak
|
||||||
|
Protokoll: ?'
|
||||||
|
protostub: '* Termin: {{next_date.strftime("%d.%m.%Y")}}
|
||||||
|
|
||||||
|
* Ort: {{place}}
|
||||||
|
|
||||||
|
* nächste Redeleitung: XXXXXXXX
|
||||||
|
|
||||||
|
* nächstes Protokoll: XXXXXXXX'
|
||||||
|
- title: Termine
|
||||||
|
protostub: '{|
|
||||||
|
{{"{{"}}Termin|was=AAAAAAAAAA|wann=XX.YY.{{"}}"}}
|
||||||
|
|}'
|
||||||
|
- title: Sonstiges
|
||||||
|
|
||||||
|
invite_template_file: templates/fsr_einladung.j2
|
||||||
|
presentation_template_file: templates/fsr_presentation.tex.j2
|
||||||
|
protocol_template_file: templates/fsr_protokoll.j2
|
||||||
|
|
||||||
|
top_mbox_file: fsr_tops.mbox
|
||||||
|
|
||||||
|
default_weekday: 2 #Mittwoch
|
||||||
|
default_time: "17:30"
|
||||||
|
meeting_link: https://meet.vs.kit.edu/b/abc-def-ghi
|
||||||
|
place: BigBlueButton
|
||||||
|
|
||||||
|
invite_mail: alle@fsmi.uni-karlsruhe.de
|
||||||
|
invite_subject: 'Einladung zum Fachschaftsrat am {{date|weekday}}, dem {{date|date}}'
|
||||||
|
|
||||||
|
mbox_out: invitemail.mbox
|
||||||
|
|
||||||
|
# vim: filetype=yaml
|
||||||
41
templates/fsr_einladung.j2
Normal file
41
templates/fsr_einladung.j2
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
Hallo,
|
||||||
|
|
||||||
|
hiermit lade ich euch zum nächsten Fachschaftsrat ein. Er findet statt
|
||||||
|
am:
|
||||||
|
|
||||||
|
{{date|weekday}}, den {{date|date}} um {{time|time}} Uhr
|
||||||
|
|
||||||
|
via Microsoft Teams (App, Chrome, Edge oder Safari; nicht Firefox) [0]
|
||||||
|
Es ist keine Anmeldung über das KIT notwendig,
|
||||||
|
mehr Informationen unter [1]
|
||||||
|
|
||||||
|
Bitte auch die Nettiquette beachten [2]:
|
||||||
|
- Rechtzeitig da sein
|
||||||
|
- Mikro aus machen wenn man nicht redet
|
||||||
|
- Video aus wg. Bandbreite
|
||||||
|
- Wenn möglich Headset verwendet
|
||||||
|
|
||||||
|
Die Antragstexte für die eingereichten TOPs sind unten angehangen.
|
||||||
|
Die vorläufige Tagesordnung lautet:
|
||||||
|
|
||||||
|
{% for top in to %}{{ "%02d" % loop.index}} {{top.title}}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
Bitte sendet vorher die Berichte an das Protokollamt[3] zu.
|
||||||
|
|
||||||
|
[0] {{meeting_link}}
|
||||||
|
[1] https://www.scc.kit.edu/dienste/ms-teams.php
|
||||||
|
[2] Auszug vom SCC https://www.scc.kit.edu/dienste/ms-teams.php
|
||||||
|
[3] protokollant@fsmi.uni-karlsruhe.de
|
||||||
|
|
||||||
|
Viele Grüße,
|
||||||
|
{{redeleitung.name}}
|
||||||
|
|
||||||
|
|
||||||
|
Eingereichte TOPs:
|
||||||
|
{% for top in email_tops %}
|
||||||
|
= {{top.title}} =
|
||||||
|
Eingereicht von: {{top.sender}}
|
||||||
|
|
||||||
|
{{top.body}}
|
||||||
|
{% endfor %}
|
||||||
40
templates/fsr_presentation.tex.j2
Normal file
40
templates/fsr_presentation.tex.j2
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
\documentclass[aspectratio=169, smaller]{beamer}
|
||||||
|
\mode<beamer> {
|
||||||
|
%\setbeameroption{show notes on second screen=right}
|
||||||
|
%\setbeameroption{show only notes}
|
||||||
|
}
|
||||||
|
|
||||||
|
{% raw %}
|
||||||
|
\providecommand{\tightlist}{%
|
||||||
|
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
|
||||||
|
{% endraw %}
|
||||||
|
|
||||||
|
\usepackage{pgfpages}
|
||||||
|
\usepackage[ngerman]{babel}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{fancyvrb}
|
||||||
|
|
||||||
|
\usetheme[width=3cm]{Berkeley}
|
||||||
|
\usecolortheme{sidebartab}
|
||||||
|
\setbeamertemplate{navigation symbols}{}
|
||||||
|
|
||||||
|
\title{Fachschaftsrat Mathe/Info}
|
||||||
|
\author{Redeleitung: {{redeleitung.name}} \\Protokoll: {{protokoll.name}} }
|
||||||
|
\date{ {{date.strftime("%d.%m.%Y")}} }
|
||||||
|
\logo{\includegraphics[width=17mm]{fslogo}}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\begin{frame}
|
||||||
|
\maketitle
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
{% for top in to %}
|
||||||
|
\section{ {{top.title}} }
|
||||||
|
\begin{frame}{% if top.body and top.body|length > 500 %}[allowframebreaks]{%endif%}
|
||||||
|
\frametitle{ {{top.title}} }
|
||||||
|
{{top.body|j2replace|wiki2latex if top.body}}
|
||||||
|
\end{frame}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
26
templates/fsr_protokoll.j2
Normal file
26
templates/fsr_protokoll.j2
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{{ '{{' }}Protokollkopf
|
||||||
|
|was=Fachschaftsrat
|
||||||
|
|datum={{date.strftime("%d.%m.%Y")}}
|
||||||
|
|ort={{place}}
|
||||||
|
|anfang={{time.strftime("%H:%M")}}
|
||||||
|
|ende=XX:YY
|
||||||
|
|redeleitung={{redeleitung.name}}
|
||||||
|
|protokollant={{protokoll.name}}
|
||||||
|
|anwesende=
|
||||||
|
|
||||||
|
* {{redeleitung.name}} (Fak)
|
||||||
|
* {{protokoll.name}} (Fak)
|
||||||
|
* Weitere
|
||||||
|
|
||||||
|
|veröffentlicht=Unveröffentlicht{{ '}}' }}
|
||||||
|
|
||||||
|
{% for top in to %}
|
||||||
|
= {{top.title}} =
|
||||||
|
{%if top.sender%}Eingereicht von: {{top.sender}}
|
||||||
|
|
||||||
|
{%endif%}{%if top.protostub%}{{top.protostub|j2replace}}
|
||||||
|
{% elif top.body %}{{top.body|j2replace}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
[[Kategorie:Protokoll]]
|
||||||
Reference in New Issue
Block a user