[ini]
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
/temp/
|
||||||
|
/build/
|
||||||
|
**/__pycache__
|
417
source/data.json
Normal file
|
@ -0,0 +1,417 @@
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"author": "Christian Fraß",
|
||||||
|
"date": "2024-07-11",
|
||||||
|
"title": "Digitale Infrastruktur für DIE LINKE.",
|
||||||
|
"contact": {
|
||||||
|
"email_address": "christian.frass@dielinke-glauchau.de"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"theory": [
|
||||||
|
{
|
||||||
|
"text": "Wie setzt man einen Web-Dienst mit ausgelagerter Anmeldung auf?",
|
||||||
|
"image": "services-0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Zunächst wird eine Datenbank eingerichtet",
|
||||||
|
"image": "services-1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Weiterhin wird ein Web-Server benötigt",
|
||||||
|
"image": "services-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Zudem soll ein Authentifizierungs-Dienst zum Einsatz kommen",
|
||||||
|
"image": "services-3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… dieser benötigt in der Regel eine Datenbank-Anbindung",
|
||||||
|
"image": "services-4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… und soll über den Web-Server von außen erreichbar sein",
|
||||||
|
"image": "services-5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Der eigentliche Dienst …",
|
||||||
|
"image": "services-6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… benötigt ebenfalls eine Datenbank-Anbindung",
|
||||||
|
"image": "services-7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… soll den Authentifizierungs-Dienst nutzen",
|
||||||
|
"image": "services-8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… und über den Web-Server angesprochen werden",
|
||||||
|
"image": "services-9"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"practice": {
|
||||||
|
"example": {
|
||||||
|
"link": "https://linke.sx",
|
||||||
|
"label": "linke.sx"
|
||||||
|
},
|
||||||
|
"technologies": {
|
||||||
|
"pool": {
|
||||||
|
"debian": {
|
||||||
|
"name": "Debian GNU/Linux",
|
||||||
|
"link": "https://www.debian.org/",
|
||||||
|
"icon": null,
|
||||||
|
"desc": "Betriebssystem"
|
||||||
|
},
|
||||||
|
"ansible": {
|
||||||
|
"name": "Ansible",
|
||||||
|
"link": "https://www.ansible.com/",
|
||||||
|
"icon": null,
|
||||||
|
"desc": "IAC-Plattform"
|
||||||
|
},
|
||||||
|
"keepassxc": {
|
||||||
|
"name": "KeePassXC",
|
||||||
|
"link": "https://keepassxc.org/",
|
||||||
|
"icon": null,
|
||||||
|
"desc": "Passwort-Verwaltung"
|
||||||
|
},
|
||||||
|
"letsencrypt": {
|
||||||
|
"name": "Let's Encrypt",
|
||||||
|
"link": "https://letsencrypt.org/",
|
||||||
|
"icon": null,
|
||||||
|
"desc": "TLS-Zertifikats-Erstellung"
|
||||||
|
},
|
||||||
|
"inwx": {
|
||||||
|
"name": "INWX",
|
||||||
|
"link": "https://www.inwx.de/",
|
||||||
|
"icon": null,
|
||||||
|
"desc": "Domänen-Registrar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"order": [
|
||||||
|
"debian",
|
||||||
|
"ansible",
|
||||||
|
"keepassxc",
|
||||||
|
"inwx",
|
||||||
|
"letsencrypt"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"basics": {
|
||||||
|
"pool": {
|
||||||
|
"postgresql": {
|
||||||
|
"name": "PostgreSQL",
|
||||||
|
"link": "https://www.postgresql.org/",
|
||||||
|
"icon": null,
|
||||||
|
"desc": "Datenbank-Server"
|
||||||
|
},
|
||||||
|
"nginx": {
|
||||||
|
"name": "nginx",
|
||||||
|
"link": "https://nginx.org/",
|
||||||
|
"icon": null,
|
||||||
|
"desc": "Web-Server und Lastenverteiler"
|
||||||
|
},
|
||||||
|
"authelia": {
|
||||||
|
"name": "Authelia",
|
||||||
|
"link": "https://www.authelia.com/",
|
||||||
|
"icon": null,
|
||||||
|
"desc": "Auth-Server"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"order": [
|
||||||
|
"postgresql",
|
||||||
|
"nginx",
|
||||||
|
"authelia"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"concrete_present": {
|
||||||
|
"pool": {
|
||||||
|
"synapse": {
|
||||||
|
"name": "Synapse",
|
||||||
|
"link": "https://github.com/element-hq/synapse",
|
||||||
|
"icon": "https://matrix.org/images/matrix-favicon.svg",
|
||||||
|
"desc": "Chat-Server"
|
||||||
|
},
|
||||||
|
"element": {
|
||||||
|
"name": "Element",
|
||||||
|
"link": "https://element.io/",
|
||||||
|
"icon": "https://element.io/images/webclip.png",
|
||||||
|
"desc": "Chat-Client für Browser"
|
||||||
|
},
|
||||||
|
"hedgedoc": {
|
||||||
|
"name": "Hedgedoc",
|
||||||
|
"link": "https://hedgedoc.org/",
|
||||||
|
"icon": "https://informatik-box.de/images/hedgedoc.png",
|
||||||
|
"desc": "Notizen"
|
||||||
|
},
|
||||||
|
"gitlab": {
|
||||||
|
"name": "GitLab",
|
||||||
|
"link": "https://about.gitlab.com/",
|
||||||
|
"icon": "https://about.gitlab.com/nuxt-images/ico/favicon-192x192.png",
|
||||||
|
"desc": "Code-Management und Aufgabenverwaltung",
|
||||||
|
"stat": "present"
|
||||||
|
},
|
||||||
|
"forgejo": {
|
||||||
|
"name": "Forgejo",
|
||||||
|
"link": "https://forgejo.org/",
|
||||||
|
"icon": "https://forgejo.org/favicon.png",
|
||||||
|
"desc": "Code-Management und Aufgabenverwaltung"
|
||||||
|
},
|
||||||
|
"vikunja": {
|
||||||
|
"name": "Vikunja",
|
||||||
|
"link": "https://vikunja.io/",
|
||||||
|
"icon": "https://vikunja.io/favicon.svg",
|
||||||
|
"desc": "Aufgabenverwaltung und Kalender"
|
||||||
|
},
|
||||||
|
"dokuwiki": {
|
||||||
|
"name": "Dokuwiki",
|
||||||
|
"link": "https://www.dokuwiki.org/dokuwiki",
|
||||||
|
"icon": "https://www.dokuwiki.org/lib/tpl/dokuwiki/images/logo.png",
|
||||||
|
"desc": "Wissensspeicher"
|
||||||
|
},
|
||||||
|
"owncloud": {
|
||||||
|
"name": "ownCloud",
|
||||||
|
"link": "https://owncloud.com/de/infinite-scale/",
|
||||||
|
"icon": "https://owncloud.com/wp-content/themes/ownCloud/dist/assets/img/favicon/apple-touch-icon.png",
|
||||||
|
"desc": "Datenablage"
|
||||||
|
},
|
||||||
|
"murmur": {
|
||||||
|
"name": "Murmur",
|
||||||
|
"link": "https://www.mumble.info/",
|
||||||
|
"icon": "https://www.mumble.info/css/mumble.svg",
|
||||||
|
"desc": "Audiokonferenzen (bislang ohne Authentifizierung)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"order": [
|
||||||
|
"synapse",
|
||||||
|
"element",
|
||||||
|
"murmur",
|
||||||
|
"hedgedoc",
|
||||||
|
"dokuwiki",
|
||||||
|
"owncloud",
|
||||||
|
"vikunja",
|
||||||
|
"gitlab",
|
||||||
|
"forgejo"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"concrete_planned": {
|
||||||
|
"pool": {
|
||||||
|
"bigbluebutton": {
|
||||||
|
"name": "BigBlueButton",
|
||||||
|
"link": "https://bigbluebutton.org/",
|
||||||
|
"icon": null,
|
||||||
|
"desc": "Videokonferenzen",
|
||||||
|
"stat": "planned"
|
||||||
|
},
|
||||||
|
"mastodon": {
|
||||||
|
"name": "Mastodon",
|
||||||
|
"link": "https://joinmastodon.org",
|
||||||
|
"icon": "https://creazilla-store.fra1.digitaloceanspaces.com/icons/3204993/logo-mastodon-icon-sm.png",
|
||||||
|
"desc": "Microblogging",
|
||||||
|
"stat": "planned"
|
||||||
|
},
|
||||||
|
"grav": {
|
||||||
|
"name": "grav",
|
||||||
|
"link": "https://getgrav.org/",
|
||||||
|
"icon": "https://getgrav.org/user/themes/planetoid/images/favicon.png",
|
||||||
|
"desc": "Web-Inhaltsverwaltung",
|
||||||
|
"stat": "planned"
|
||||||
|
},
|
||||||
|
"dovecot": {
|
||||||
|
"name": "Dovecot",
|
||||||
|
"link": "https://doc.dovecot.org/",
|
||||||
|
"icon": "https://w2.influxdata.com/wp-content/uploads/dovecot-logo.png",
|
||||||
|
"desc": "E-Mail-Empfang",
|
||||||
|
"stat": "planned"
|
||||||
|
},
|
||||||
|
"postfix": {
|
||||||
|
"name": "Postfix",
|
||||||
|
"link": "https://www.postfix.org/",
|
||||||
|
"icon": "https://webhostinggeeks.com/howto/wp-content/uploads/2012/06/Posfix-Mail.jpg",
|
||||||
|
"desc": "E-Mail-Versand",
|
||||||
|
"stat": "planned"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"order": [
|
||||||
|
"postfix",
|
||||||
|
"dovecot",
|
||||||
|
"mastodon",
|
||||||
|
"grav"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"alternatives": {
|
||||||
|
"list": [
|
||||||
|
"MS Exchange",
|
||||||
|
"Google",
|
||||||
|
"facebook",
|
||||||
|
"WhatsApp/Telegram/Signal",
|
||||||
|
"Dropbox",
|
||||||
|
"Nextcloud",
|
||||||
|
"Humhub",
|
||||||
|
"Zetkin",
|
||||||
|
"…"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"zentralisiert",
|
||||||
|
"proprietär",
|
||||||
|
"datenschutzverletzend",
|
||||||
|
"zwielichtig",
|
||||||
|
"überladen",
|
||||||
|
"unterladen",
|
||||||
|
"kompliziert",
|
||||||
|
"unflexibel",
|
||||||
|
"kaputt"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user_management": {
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"text": "Die Dienste stehen bereit, aber wer darf sie verwenden?",
|
||||||
|
"image": "user_management-0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Espe als Nutzerverwaltung …",
|
||||||
|
"image": "user_management-1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… hat einen typischen technischen Aufbau",
|
||||||
|
"image": "user_management-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… und kann den Authentifizierungs-Dienst füttern",
|
||||||
|
"image": "user_management-3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "MGL …",
|
||||||
|
"image": "user_management-4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… ist über den Browser bedienbar",
|
||||||
|
"image": "user_management-5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… hat anscheinend auch einen typischen technischen Aufbau",
|
||||||
|
"image": "user_management-6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… und kann erweitert werden :)",
|
||||||
|
"image": "user_management-7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… und damit als Quelle für Espe und den Authentifizierungs-Dienst dienen",
|
||||||
|
"image": "user_management-8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"processes": {
|
||||||
|
"entry": [
|
||||||
|
{
|
||||||
|
"text": "Bei Eintritt legt ein Mitgliederbeauftragter einen Datensatz in MGL an",
|
||||||
|
"image": "user_management-9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Datensatz landet in MGL-BE/MGL-DB",
|
||||||
|
"image": "user_management-10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Syncer liest Mitglieder aus und gleicht in Richtung Espe ab",
|
||||||
|
"image": "user_management-11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Espe erzeugt für jeden neuen Nutzerdatensatz ein Passwort und sendet E-Mail",
|
||||||
|
"image": "user_management-12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "… und schickt die neue Nutzerliste an den Authentifizierungs-Dienst",
|
||||||
|
"image": "user_management-13"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"example": {
|
||||||
|
"link": "https://zackeneule.linke.sx/",
|
||||||
|
"label": "zackeneule.linke.sx",
|
||||||
|
"remark": " (keine MGL-Anbindung)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"realization": {
|
||||||
|
"todos_technical": [
|
||||||
|
{
|
||||||
|
"name": "Entwickler gewinnen",
|
||||||
|
"link": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Penetrationstests durchführen",
|
||||||
|
"link": "https://de.m.wikipedia.org/wiki/Penetrationstest_(Informatik)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Lastverteilung vorsehen",
|
||||||
|
"link": "https://de.m.wikipedia.org/wiki/Lastverteilung_(Informatik)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Datensicherung einrichten",
|
||||||
|
"link": "https://de.m.wikipedia.org/wiki/Datensicherung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Überwachung einrichten",
|
||||||
|
"link": "https://de.m.wikipedia.org/wiki/Monitoring"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"todos_social": [
|
||||||
|
{
|
||||||
|
"name": "Kräfte bündeln",
|
||||||
|
"link": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tester gewinnen",
|
||||||
|
"link": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Admins gewinnen",
|
||||||
|
"link": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Überzeugungsarbeit leisten",
|
||||||
|
"link": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"name": "Ansible-Rollen für Dienste",
|
||||||
|
"link": "https://gitlab.die-linke.cloud/misc/ansible-base"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Infrastruktur-Definition für LAG Netzpolitik Sachsen",
|
||||||
|
"link": "https://gitlab.die-linke.cloud/misc/infrastructure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Espe | Datenmodell",
|
||||||
|
"link": "https://gitlab.die-linke.cloud/espe/datamodel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Espe | Backend",
|
||||||
|
"link": "https://gitlab.die-linke.cloud/espe/backend"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Espe | Frontend | Web",
|
||||||
|
"link": "https://gitlab.die-linke.cloud/espe/frontend-zackeneule"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Espe | Frontend | CLI",
|
||||||
|
"link": "https://gitlab.die-linke.cloud/espe/frontend-mondvogel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Espe | Ansible-Rollen",
|
||||||
|
"link": "https://gitlab.die-linke.cloud/espe/infrastructure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MGL-CLI-Client",
|
||||||
|
"link": "https://gitlab.die-linke.cloud/misc/mgl-cli"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MGL-Espe-Syncer",
|
||||||
|
"link": "https://gitlab.die-linke.cloud/misc/mgl-espe-syncer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
619
source/graphs
Executable file
|
@ -0,0 +1,619 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
import sys as _sys
|
||||||
|
import json as _json
|
||||||
|
import argparse as _argparse
|
||||||
|
|
||||||
|
from lib import *
|
||||||
|
|
||||||
|
|
||||||
|
def hue_regular():
|
||||||
|
return 0.4
|
||||||
|
|
||||||
|
|
||||||
|
def hue_highlight():
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def style(level, step):
|
||||||
|
return ("invis" if (step < level) else "filled")
|
||||||
|
|
||||||
|
|
||||||
|
def style_new(predicate):
|
||||||
|
return (
|
||||||
|
"filled"
|
||||||
|
if predicate() else
|
||||||
|
"invis"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def highlight(predicate):
|
||||||
|
return (
|
||||||
|
{
|
||||||
|
"fillcolor": ("%.4f+0.75+0.5" % (hue_highlight(), )),
|
||||||
|
"color": ("%.4f+0.75+0.5" % (hue_highlight(), )),
|
||||||
|
}
|
||||||
|
if predicate() else
|
||||||
|
{
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def render(input_, data, format_, path_output):
|
||||||
|
path_temp = "/tmp/graph.gv"
|
||||||
|
_os.makedirs(_os.path.dirname(path_temp), exist_ok = True)
|
||||||
|
file_write(
|
||||||
|
path_temp,
|
||||||
|
string_coin(
|
||||||
|
input_,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
_os.makedirs(_os.path.dirname(path_output), exist_ok = True)
|
||||||
|
_os.system(
|
||||||
|
string_coin(
|
||||||
|
"cat {{path_source}} | dot -T {{format}} > {{path_output}}",
|
||||||
|
{
|
||||||
|
"path_source": path_temp,
|
||||||
|
"path_output": path_output,
|
||||||
|
"format": format_,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def attributes_subgraph():
|
||||||
|
return {
|
||||||
|
"fontname": "monospace",
|
||||||
|
"fontcolor": ("%.4f+0+1" % (hue_regular(), )),
|
||||||
|
"color": ("%.4f+0.25+0.25" % (hue_regular(), )),
|
||||||
|
"fillcolor": ("%.4f+0.25+0.25" % (hue_regular(), )),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def dot_macro_services(conf):
|
||||||
|
return dot_graph(
|
||||||
|
{
|
||||||
|
"name": "services",
|
||||||
|
"settings": {
|
||||||
|
"graph": {
|
||||||
|
"layout": "dot",
|
||||||
|
"rankdir": "BT",
|
||||||
|
"bgcolor": "0.4+0+0.125",
|
||||||
|
},
|
||||||
|
"node": {
|
||||||
|
"fontname": "monospace",
|
||||||
|
"shape": "hexagon",
|
||||||
|
"style": "filled",
|
||||||
|
"fillcolor": "0.4+0.75+0.5",
|
||||||
|
"color": "0.4+0.75+0.5",
|
||||||
|
"fontcolor": "0.4+0+1",
|
||||||
|
},
|
||||||
|
"edge": {
|
||||||
|
"fontname": "monospace",
|
||||||
|
"color": "0.4+0+0.75",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "node_db",
|
||||||
|
"attributes": {
|
||||||
|
"label": "DB-Server",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 1)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_web",
|
||||||
|
"attributes": {
|
||||||
|
"label": "Web-Server",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 2)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_auth",
|
||||||
|
"attributes": {
|
||||||
|
"label": "Auth-Server",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 3)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_db_for_auth",
|
||||||
|
"attributes": {
|
||||||
|
"label": "DB-Modul\nfür\nAuth-Server",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 4)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_auth_and_web",
|
||||||
|
"attributes": {
|
||||||
|
"label": "Web-Modul\nfür\nAuth-Server",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 5)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_service",
|
||||||
|
"attributes": {
|
||||||
|
"label": "Dienst",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 6)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_db_for_service",
|
||||||
|
"attributes": {
|
||||||
|
"label": "DB-Modul\nfür\nDienst",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 7)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_auth_for_service",
|
||||||
|
"attributes": {
|
||||||
|
"label": "Auth-Modul\nfür\nDienst",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 8)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_service_and_web",
|
||||||
|
"attributes": {
|
||||||
|
"label": "Web-Modul\nfür\nDienst",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 9)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"name_from": "node_db",
|
||||||
|
"name_to": "node_db_for_auth",
|
||||||
|
"attributes": {
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 4)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_db_for_auth",
|
||||||
|
"name_to": "node_auth",
|
||||||
|
"attributes": {
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 4)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"name_from": "node_auth",
|
||||||
|
"name_to": "node_auth_and_web",
|
||||||
|
"attributes": {
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 5)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_web",
|
||||||
|
"name_to": "node_auth_and_web",
|
||||||
|
"attributes": {
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 5)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_db",
|
||||||
|
"name_to": "node_db_for_service",
|
||||||
|
"attributes": {
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 7)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_db_for_service",
|
||||||
|
"name_to": "node_service",
|
||||||
|
"attributes": {
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 7)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_auth",
|
||||||
|
"name_to": "node_auth_for_service",
|
||||||
|
"attributes": {
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 8)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_auth_for_service",
|
||||||
|
"name_to": "node_service",
|
||||||
|
"attributes": {
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 8)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_service",
|
||||||
|
"name_to": "node_service_and_web",
|
||||||
|
"attributes": {
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 9)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_web",
|
||||||
|
"name_to": "node_service_and_web",
|
||||||
|
"attributes": {
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 9)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"subgraphs": [
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dot_macro_user_management(conf):
|
||||||
|
return dot_graph(
|
||||||
|
{
|
||||||
|
"name": "user_management",
|
||||||
|
"settings": {
|
||||||
|
"graph": {
|
||||||
|
"layout": "dot",
|
||||||
|
"rankdir": "BT",
|
||||||
|
"bgcolor": "0.4+0+0.125",
|
||||||
|
},
|
||||||
|
"node": {
|
||||||
|
"fontname": "monospace",
|
||||||
|
"shape": "hexagon",
|
||||||
|
"style": "filled",
|
||||||
|
"fillcolor": "0.4+0.75+0.5",
|
||||||
|
"color": "0.4+0.75+0.5",
|
||||||
|
"fontcolor": "0.4+0+1",
|
||||||
|
},
|
||||||
|
"edge": {
|
||||||
|
"fontname": "monospace",
|
||||||
|
"color": "0.4+0+0.75",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "node_9",
|
||||||
|
"attributes": (
|
||||||
|
{
|
||||||
|
"label": "Syncer",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 8)),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
highlight(lambda: (conf["step"] == 11))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"name_from": "node_6",
|
||||||
|
"name_to": "node_11",
|
||||||
|
"attributes": {
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 3)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_4",
|
||||||
|
"name_to": "node_9",
|
||||||
|
"attributes": {
|
||||||
|
"dir": "forward",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 8)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_8",
|
||||||
|
"name_to": "node_9",
|
||||||
|
"attributes": {
|
||||||
|
"dir": "both",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 8)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"subgraphs": [
|
||||||
|
{
|
||||||
|
"name": "3",
|
||||||
|
"attributes": (
|
||||||
|
attributes_subgraph()
|
||||||
|
|
|
||||||
|
{
|
||||||
|
"label": "MGL",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 4)),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "node_1",
|
||||||
|
"attributes": (
|
||||||
|
{
|
||||||
|
"label": "MGL-DB",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 6)),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
highlight(lambda: (conf["step"] == 10))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_2",
|
||||||
|
"attributes": (
|
||||||
|
{
|
||||||
|
"label": "MGL-BE",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 6)),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
highlight(lambda: (conf["step"] == 10))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_3",
|
||||||
|
"attributes": (
|
||||||
|
{
|
||||||
|
"label": "MGL-FE-Web",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 5)),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
highlight(lambda: (conf["step"] == 9))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_4",
|
||||||
|
"attributes": (
|
||||||
|
{
|
||||||
|
"label": "MGL-FE-CLI",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 7)),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
highlight(lambda: (conf["step"] == 11))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"name_from": "node_1",
|
||||||
|
"name_to": "node_2",
|
||||||
|
"attributes": {
|
||||||
|
"dir": "both",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 6)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_2",
|
||||||
|
"name_to": "node_3",
|
||||||
|
"attributes": {
|
||||||
|
"dir": "both",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 6)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_2",
|
||||||
|
"name_to": "node_4",
|
||||||
|
"attributes": {
|
||||||
|
"dir": "forward",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 7)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2",
|
||||||
|
"attributes": (
|
||||||
|
attributes_subgraph()
|
||||||
|
|
|
||||||
|
{
|
||||||
|
"label": "Espe",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 1)),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "node_5",
|
||||||
|
"attributes": (
|
||||||
|
{
|
||||||
|
"label": "Espe-DB",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 2)),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
highlight(lambda: (conf["step"] == 12))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_6",
|
||||||
|
"attributes": (
|
||||||
|
{
|
||||||
|
"label": "Espe-BE",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 2)),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
highlight(lambda: (conf["step"] == 12))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_8",
|
||||||
|
"attributes": (
|
||||||
|
{
|
||||||
|
"label": "Espe-FE-CLI",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 2)),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
highlight(lambda: (conf["step"] == 11))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_7",
|
||||||
|
"attributes": (
|
||||||
|
{
|
||||||
|
"label": "Espe-FE-Web",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 2)),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"name_from": "node_5",
|
||||||
|
"name_to": "node_6",
|
||||||
|
"attributes": {
|
||||||
|
"dir": "both",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 2)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_6",
|
||||||
|
"name_to": "node_7",
|
||||||
|
"attributes": {
|
||||||
|
"dir": "both",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 2)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_6",
|
||||||
|
"name_to": "node_8",
|
||||||
|
"attributes": {
|
||||||
|
"dir": "both",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 2)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1",
|
||||||
|
"attributes": (
|
||||||
|
attributes_subgraph()
|
||||||
|
|
|
||||||
|
{
|
||||||
|
"label": "Dienste",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 0)),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "node_10",
|
||||||
|
"attributes": {
|
||||||
|
"label": "…",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 0)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_11",
|
||||||
|
"attributes": (
|
||||||
|
{
|
||||||
|
"label": "Auth-Server",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 0)),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
highlight(lambda: (conf["step"] == 13))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_12",
|
||||||
|
"attributes": {
|
||||||
|
"label": "…",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 0)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"name_from": "node_10",
|
||||||
|
"name_to": "node_11",
|
||||||
|
"attributes": {
|
||||||
|
"dir": "both",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 0)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name_from": "node_11",
|
||||||
|
"name_to": "node_12",
|
||||||
|
"attributes": {
|
||||||
|
"dir": "both",
|
||||||
|
"style": style_new(lambda: (conf["step"] >= 0)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
## consts
|
||||||
|
dir_source = "source"
|
||||||
|
|
||||||
|
## args
|
||||||
|
argument_parser = _argparse.ArgumentParser(
|
||||||
|
)
|
||||||
|
argument_parser.add_argument(
|
||||||
|
"-f",
|
||||||
|
"--format",
|
||||||
|
type = str,
|
||||||
|
choices = ["svg","png"],
|
||||||
|
dest = "format",
|
||||||
|
metavar = "<format>",
|
||||||
|
default = "svg"
|
||||||
|
)
|
||||||
|
argument_parser.add_argument(
|
||||||
|
"-n",
|
||||||
|
"--no-extension",
|
||||||
|
action = "store_true",
|
||||||
|
dest = "no_extension",
|
||||||
|
default = False,
|
||||||
|
)
|
||||||
|
argument_parser.add_argument(
|
||||||
|
"-o",
|
||||||
|
"--output-directory",
|
||||||
|
type = str,
|
||||||
|
dest = "output_directory",
|
||||||
|
metavar = "<output-directory>",
|
||||||
|
default = "temp"
|
||||||
|
)
|
||||||
|
args = argument_parser.parse_args()
|
||||||
|
|
||||||
|
## exec
|
||||||
|
### excc:services
|
||||||
|
if True:
|
||||||
|
size = 10
|
||||||
|
for step in range(size):
|
||||||
|
render(
|
||||||
|
dot_macro_services(
|
||||||
|
{
|
||||||
|
"step": step,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
{},
|
||||||
|
args.format,
|
||||||
|
string_coin(
|
||||||
|
"{{directory}}/{{name}}-{{step}}{{extension}}",
|
||||||
|
{
|
||||||
|
"name": "services",
|
||||||
|
"directory": args.output_directory,
|
||||||
|
"step": ("%u" % step),
|
||||||
|
"extension": (
|
||||||
|
""
|
||||||
|
if args.no_extension else
|
||||||
|
("." + args.format)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
### exec:syncing
|
||||||
|
if True:
|
||||||
|
size = 14
|
||||||
|
for step in range(size):
|
||||||
|
render(
|
||||||
|
dot_macro_user_management(
|
||||||
|
{
|
||||||
|
"step": step,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
{},
|
||||||
|
args.format,
|
||||||
|
string_coin(
|
||||||
|
"{{directory}}/{{name}}-{{step}}{{extension}}",
|
||||||
|
{
|
||||||
|
"name": "user_management",
|
||||||
|
"directory": args.output_directory,
|
||||||
|
"step": ("%u" % step),
|
||||||
|
"extension": (
|
||||||
|
""
|
||||||
|
if args.no_extension else
|
||||||
|
("." + args.format)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
199
source/lib.py
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
import os as _os
|
||||||
|
|
||||||
|
|
||||||
|
def convey(x, fs):
|
||||||
|
y = x
|
||||||
|
for f in fs:
|
||||||
|
y = f(y)
|
||||||
|
return y
|
||||||
|
|
||||||
|
|
||||||
|
def string_coin(template, arguments, options = None):
|
||||||
|
options = (
|
||||||
|
{
|
||||||
|
"open": "{{",
|
||||||
|
"close": "}}",
|
||||||
|
}
|
||||||
|
|
|
||||||
|
(options or {})
|
||||||
|
)
|
||||||
|
result = template
|
||||||
|
for (key, value, ) in arguments.items():
|
||||||
|
if (value is None):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
result = result.replace(
|
||||||
|
(
|
||||||
|
"%s%s%s"
|
||||||
|
% (
|
||||||
|
options["open"],
|
||||||
|
key,
|
||||||
|
options["close"],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
value
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def file_read(path):
|
||||||
|
handle = open(path, "r")
|
||||||
|
content = handle.read()
|
||||||
|
handle.close()
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
def file_write(path, content):
|
||||||
|
_os.makedirs(_os.path.dirname(path), exist_ok = True)
|
||||||
|
handle = open(path, "w" if _os.path.exists(path) else "a")
|
||||||
|
handle.write(content)
|
||||||
|
handle.close()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def dot_entry(head, attributes, options = None):
|
||||||
|
options = (
|
||||||
|
{
|
||||||
|
"indentation": 0,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
(options or {})
|
||||||
|
)
|
||||||
|
return string_coin(
|
||||||
|
"{{indentation}}{{head}} [{{attributes}}];\n",
|
||||||
|
{
|
||||||
|
"indentation": ("\t" * options["indentation"]),
|
||||||
|
"head": head,
|
||||||
|
"attributes": convey(
|
||||||
|
attributes,
|
||||||
|
[
|
||||||
|
lambda x: x.items(),
|
||||||
|
lambda x: map(
|
||||||
|
lambda pair: string_coin(
|
||||||
|
"{{key}}=\"{{value}}\"",
|
||||||
|
{
|
||||||
|
"key": pair[0],
|
||||||
|
"value": pair[1]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
", ".join,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dot_node(node, options = None):
|
||||||
|
options = (
|
||||||
|
{
|
||||||
|
"indentation": 0,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
(options or {})
|
||||||
|
)
|
||||||
|
return dot_entry(
|
||||||
|
string_coin("{{name}}", {"name": node["name"]}),
|
||||||
|
node["attributes"],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dot_edge(edge, options = None):
|
||||||
|
options = (
|
||||||
|
{
|
||||||
|
"indentation": 0,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
(options or {})
|
||||||
|
)
|
||||||
|
return dot_entry(
|
||||||
|
string_coin("{{name_from}} -> {{name_to}}", {"name_from": edge["name_from"], "name_to": edge["name_to"]}),
|
||||||
|
edge["attributes"],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dot_subgraph(subgraph, options = None):
|
||||||
|
options = (
|
||||||
|
{
|
||||||
|
"indentation": 0,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
(options or {})
|
||||||
|
)
|
||||||
|
return string_coin(
|
||||||
|
(
|
||||||
|
"{{indentation}}subgraph cluster_{{name}}\n"
|
||||||
|
+
|
||||||
|
"{{indentation}}{\n"
|
||||||
|
+
|
||||||
|
"{{attributes}}\n"
|
||||||
|
+
|
||||||
|
"{{nodes}}\n"
|
||||||
|
+
|
||||||
|
"{{edges}}\n"
|
||||||
|
+
|
||||||
|
"{{indentation}}}\n"
|
||||||
|
),
|
||||||
|
{
|
||||||
|
"indentation": ("\t" * options["indentation"]),
|
||||||
|
"name": subgraph["name"],
|
||||||
|
"attributes": convey(
|
||||||
|
subgraph["attributes"],
|
||||||
|
[
|
||||||
|
lambda x: x.items(),
|
||||||
|
lambda x: map(
|
||||||
|
lambda pair: string_coin(
|
||||||
|
"{{indentation}}{{key}}=\"{{value}}\";\n",
|
||||||
|
{
|
||||||
|
"indentation": ("\t" * (options["indentation"] + 1)),
|
||||||
|
"key": pair[0],
|
||||||
|
"value": pair[1]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
"".join,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"nodes": "".join(map(lambda node: dot_node(node, {"indentation": (options["indentation"] + 1)}), subgraph["nodes"])),
|
||||||
|
"edges": "".join(map(lambda edge: dot_edge(edge, {"indentation": (options["indentation"] + 1)}), subgraph["edges"])),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dot_graph(graph):
|
||||||
|
return string_coin(
|
||||||
|
(
|
||||||
|
"digraph {{name}}\n"
|
||||||
|
+
|
||||||
|
"{\n"
|
||||||
|
+
|
||||||
|
"{{settings_graph}}\n"
|
||||||
|
+
|
||||||
|
"{{settings_node}}\n"
|
||||||
|
+
|
||||||
|
"{{settings_edge}}\n"
|
||||||
|
+
|
||||||
|
"{{subgraphs}}\n"
|
||||||
|
+
|
||||||
|
"{{nodes}}\n"
|
||||||
|
+
|
||||||
|
"{{edges}}\n"
|
||||||
|
+
|
||||||
|
"}\n"
|
||||||
|
),
|
||||||
|
{
|
||||||
|
"name": graph["name"],
|
||||||
|
"settings_graph": dot_entry("graph", graph["settings"]["graph"], {"indentation": 1}),
|
||||||
|
"settings_node": dot_entry("node", graph["settings"]["node"], {"indentation": 1}),
|
||||||
|
"settings_edge": dot_entry("edge", graph["settings"]["edge"], {"indentation": 1}),
|
||||||
|
"nodes": "".join(map(lambda node: dot_node(node, {"indentation": 1}), graph["nodes"])),
|
||||||
|
"edges": "".join(map(lambda edge: dot_edge(edge, {"indentation": 1}), graph["edges"])),
|
||||||
|
"subgraphs": "".join(map(lambda subgraph: dot_subgraph(subgraph, {"indentation": 1}), graph["subgraphs"])),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
BIN
source/media/icons/ansible.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
source/media/icons/authelia.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
source/media/icons/debian.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
source/media/icons/dokuwiki.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
source/media/icons/dovecot.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
source/media/icons/element.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
source/media/icons/forgejo.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
source/media/icons/gitlab.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
source/media/icons/grav.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
source/media/icons/hedgedoc.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
source/media/icons/inwx.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
source/media/icons/keepassxc.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
source/media/icons/letsencrypt.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
source/media/icons/mastodon.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
source/media/icons/murmur.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
source/media/icons/nginx.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
source/media/icons/owncloud.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
source/media/icons/postfix.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
source/media/icons/postgresql.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
source/media/icons/synapse.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
source/media/icons/vikunja.png
Normal file
After Width: | Height: | Size: 17 KiB |
9
source/tex/app.tex.tpl
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
\begin{item}
|
||||||
|
\href{<<link>>}
|
||||||
|
{
|
||||||
|
\includegraphics[width=0.025\textwidth]{media/icons/<<id>>}
|
||||||
|
\
|
||||||
|
\textbf{<<name>>}
|
||||||
|
} — <<desc>>
|
||||||
|
\end{item}
|
||||||
|
|
190
source/tex/master.tex.tpl
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
\documentclass[aspectratio=169]{beamer}
|
||||||
|
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{svg}
|
||||||
|
% \usepackage[ngerman]{babel}
|
||||||
|
|
||||||
|
\date{<<meta-date>>}
|
||||||
|
\author{<<meta-author>>}
|
||||||
|
\title{<<meta-title>>}
|
||||||
|
|
||||||
|
% \setmainlanguage{german}
|
||||||
|
|
||||||
|
\usetheme{Luebeck}
|
||||||
|
% \usetheme{m}
|
||||||
|
\usecolortheme{spruce}
|
||||||
|
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\tableofcontents
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{section}{Dienste}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\sectionpage
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{subsection}{Theorie}
|
||||||
|
|
||||||
|
<<service-theory-frames>>
|
||||||
|
|
||||||
|
\end{subsection}
|
||||||
|
|
||||||
|
\begin{subsection}{Praxis}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Beispiel}
|
||||||
|
|
||||||
|
Siehe \href{<<service-practice-example-link>>}{\texttt{<<service-practice-example-label>>}}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Grundlegende Dientse}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
<<service-practice-basics-items>>
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Konkrete Dienste}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
<<service-practice-existing-items>>
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Geplante Dienste}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
<<service-practice-future-items>>
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Eingesetzte Technik}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
<<service-practice-technologies-items>>
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\end{subsection}
|
||||||
|
|
||||||
|
\begin{subsection}{Alternativen}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{„Nimm doch einfach …“}
|
||||||
|
|
||||||
|
\begin{columns}
|
||||||
|
\pause
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
\begin{itemize}
|
||||||
|
<<services-alternatives-list-items>>
|
||||||
|
\end{itemize}
|
||||||
|
\end{column}
|
||||||
|
\pause
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
\begin{itemize}
|
||||||
|
<<services-alternatives-problems-items>>
|
||||||
|
\end{itemize}
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}<presentation:0>
|
||||||
|
\frametitle{„Aber es gibt doch noch …“}
|
||||||
|
|
||||||
|
\pause
|
||||||
|
Du kennst ein System, was alles Gewünschte halbwegs sauber, sicher und bezahlbar abbildet? \\
|
||||||
|
\pause
|
||||||
|
\vspace{10mm}
|
||||||
|
Na geil! Dann schick mir gerne eine Nachricht: \texttt{christian.frass@dielinke-glauchau.de} \\
|
||||||
|
\pause
|
||||||
|
\vspace{10mm}
|
||||||
|
… aber rechne besser damit, dass ich es rösten oder ganz in der Luft zerreißen werde :)
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\end{subsection}
|
||||||
|
|
||||||
|
\end{section}
|
||||||
|
|
||||||
|
\begin{section}{Nutzerverwaltung}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\sectionpage
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{subsection}{Aufbau}
|
||||||
|
<<user_management-schema-frames>>
|
||||||
|
\end{subsection}
|
||||||
|
|
||||||
|
\begin{subsection}{Ablauf}
|
||||||
|
|
||||||
|
<<user_management-process-entry-frames>>
|
||||||
|
|
||||||
|
\end{subsection}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Vorführung}
|
||||||
|
|
||||||
|
Siehe \href{<<user_management-example-link>>}{\texttt{<<user_management-example-label>>}} <<user_management-example-remark>>
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\end{section}
|
||||||
|
|
||||||
|
\begin{section}{Umsetzung}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Zu tun}
|
||||||
|
|
||||||
|
\begin{columns}
|
||||||
|
\pause
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
\textbf{technisch}
|
||||||
|
\begin{itemize}
|
||||||
|
<<todos-technical-items>>
|
||||||
|
\end{itemize}
|
||||||
|
\end{column}
|
||||||
|
\pause
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
\textbf{menschlich}
|
||||||
|
\begin{itemize}
|
||||||
|
<<todos-social-items>>
|
||||||
|
\end{itemize}
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
<<resources-frame>>
|
||||||
|
|
||||||
|
\end{section}
|
||||||
|
|
||||||
|
\begin{section}{Schluss}
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Lust bekommen?}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item{E-Mail an \href{mailto:<<meta-contact-email_address>>}{\texttt{<<meta-contact-email_address>>}}}
|
||||||
|
\pause
|
||||||
|
\item{Teilnahme in \texttt{\$\{gemeinsamer-kommunikations-kanal\}}?}
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\begin{center}
|
||||||
|
's'war's :)
|
||||||
|
\end{center}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\end{section}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
|
|
6
source/tex/resource-item.tex.tpl
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
\begin{item}
|
||||||
|
\href{<<link>>}
|
||||||
|
{
|
||||||
|
<<name>>
|
||||||
|
}
|
||||||
|
\end{item}
|
8
source/tex/resources-frame.tex.tpl
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Ressourcen}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
<<resource-items>>
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
16
source/tex/service-theory-frame.tex.tpl
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Theorie}
|
||||||
|
\begin{columns}
|
||||||
|
% \begin{column}{0.618\textwidth}
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
\begin{center}
|
||||||
|
\includesvg[inkscapelatex=false,height=0.8\textheight]{graphs/<<image>>}
|
||||||
|
\end{center}
|
||||||
|
\end{column}
|
||||||
|
% \begin{column}{0.382\textwidth}
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
<<text>>
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
\end{frame}
|
||||||
|
|
4
source/tex/todo.tex.tpl
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
\begin{item}
|
||||||
|
\href{<<link>>}{<<name>>}
|
||||||
|
\end{item}
|
||||||
|
|
8
source/tex/user_management-frame.tex.tpl
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{<<title>>}
|
||||||
|
\begin{figure}
|
||||||
|
\includesvg[inkscapelatex=false,width=1.0\textwidth]{graphs/<<image>>}
|
||||||
|
\end{figure}
|
||||||
|
<<text>>
|
||||||
|
\end{frame}
|
||||||
|
|
56
tools/build
Executable file
|
@ -0,0 +1,56 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
import argparse as _argparse
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
## args
|
||||||
|
argument_parser = _argparse.ArgumentParser(
|
||||||
|
)
|
||||||
|
argument_parser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--clear",
|
||||||
|
action = "store_true",
|
||||||
|
dest = "clear",
|
||||||
|
default = False
|
||||||
|
)
|
||||||
|
argument_parser.add_argument(
|
||||||
|
"-r",
|
||||||
|
"--include-resources",
|
||||||
|
action = "store_true",
|
||||||
|
dest = "include_resources",
|
||||||
|
default = False,
|
||||||
|
)
|
||||||
|
argument_parser.add_argument(
|
||||||
|
"-v",
|
||||||
|
"--verbose",
|
||||||
|
action = "store_true",
|
||||||
|
dest = "verbose",
|
||||||
|
default = False,
|
||||||
|
)
|
||||||
|
argument_parser.add_argument(
|
||||||
|
"-o",
|
||||||
|
"--output-directory",
|
||||||
|
type = str,
|
||||||
|
dest = "output_directory",
|
||||||
|
metavar = "<output-directory>",
|
||||||
|
default = "build"
|
||||||
|
)
|
||||||
|
args = argument_parser.parse_args()
|
||||||
|
|
||||||
|
## exec
|
||||||
|
make_args = []
|
||||||
|
make_args.append("dir_build=%s" % args.output_directory)
|
||||||
|
if args.include_resources:
|
||||||
|
make_args.append("coin_args='-r'")
|
||||||
|
if not args.verbose:
|
||||||
|
make_args.append("latex_args='-interaction batchmode'")
|
||||||
|
if args.clear:
|
||||||
|
make_args.append("clear")
|
||||||
|
make_args.append("all")
|
||||||
|
_os.system("make -f tools/makefile %s" % " ".join(make_args))
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
206
tools/coin
Executable file
|
@ -0,0 +1,206 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys as _sys
|
||||||
|
import json as _json
|
||||||
|
import argparse as _argparse
|
||||||
|
|
||||||
|
from lib import *
|
||||||
|
|
||||||
|
|
||||||
|
_template_cache = {}
|
||||||
|
|
||||||
|
|
||||||
|
def coin(template, arguments):
|
||||||
|
return string_coin(
|
||||||
|
template,
|
||||||
|
arguments,
|
||||||
|
{"open": "<<", "close": ">>"}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def render(template_name, arguments):
|
||||||
|
global _template_cache
|
||||||
|
if (not (template_name in _template_cache)):
|
||||||
|
template_content = file_read("source/tex/%s.tex.tpl" % template_name)
|
||||||
|
_template_cache[template_name] = template_content
|
||||||
|
else:
|
||||||
|
template_content = _template_cache[template_name]
|
||||||
|
return coin(
|
||||||
|
template_content,
|
||||||
|
arguments
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def macro_applist(node):
|
||||||
|
return convey(
|
||||||
|
node["order"],
|
||||||
|
[
|
||||||
|
lambda x: map(
|
||||||
|
lambda y: render(
|
||||||
|
"app",
|
||||||
|
{
|
||||||
|
"id": y,
|
||||||
|
"name": node["pool"][y]["name"],
|
||||||
|
"link": node["pool"][y]["link"],
|
||||||
|
"icon": node["pool"][y]["icon"],
|
||||||
|
"desc": node["pool"][y]["desc"],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
"".join,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def macro_todolist(node):
|
||||||
|
return convey(
|
||||||
|
node,
|
||||||
|
[
|
||||||
|
lambda x: map(
|
||||||
|
lambda entry: render(
|
||||||
|
"todo",
|
||||||
|
{
|
||||||
|
"name": entry["name"],
|
||||||
|
"link": entry["link"],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
"".join,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
## args
|
||||||
|
argument_parser = _argparse.ArgumentParser()
|
||||||
|
argument_parser.add_argument(
|
||||||
|
"-d",
|
||||||
|
"--data-path",
|
||||||
|
type = str,
|
||||||
|
dest = "data_path",
|
||||||
|
metavar = "<data-path>",
|
||||||
|
default = "source/data.json",
|
||||||
|
)
|
||||||
|
argument_parser.add_argument(
|
||||||
|
"-r",
|
||||||
|
"--include-resources",
|
||||||
|
action = "store_true",
|
||||||
|
dest = "include_resources",
|
||||||
|
default = False,
|
||||||
|
)
|
||||||
|
args = argument_parser.parse_args()
|
||||||
|
|
||||||
|
## exec
|
||||||
|
data = _json.loads(file_read(args.data_path))
|
||||||
|
_sys.stdout.write(
|
||||||
|
render(
|
||||||
|
"master",
|
||||||
|
{
|
||||||
|
"meta-date": data["meta"]["date"],
|
||||||
|
"meta-author": data["meta"]["author"],
|
||||||
|
"meta-title": data["meta"]["title"],
|
||||||
|
"meta-contact-email_address": data["meta"]["contact"]["email_address"],
|
||||||
|
"service-theory-frames": convey(
|
||||||
|
data["services"]["theory"],
|
||||||
|
[
|
||||||
|
lambda x: map(
|
||||||
|
lambda y: render(
|
||||||
|
"service-theory-frame",
|
||||||
|
y
|
||||||
|
),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
"".join,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"service-practice-example-link": data["services"]["practice"]["example"]["link"],
|
||||||
|
"service-practice-example-label": data["services"]["practice"]["example"]["label"],
|
||||||
|
"service-practice-technologies-items": macro_applist(data["services"]["practice"]["technologies"]),
|
||||||
|
"service-practice-basics-items": macro_applist(data["services"]["practice"]["basics"]),
|
||||||
|
"service-practice-existing-items": macro_applist(data["services"]["practice"]["concrete_present"]),
|
||||||
|
"service-practice-future-items": macro_applist(data["services"]["practice"]["concrete_planned"]),
|
||||||
|
"services-alternatives-list-items": convey(
|
||||||
|
data["services"]["alternatives"]["list"],
|
||||||
|
[
|
||||||
|
lambda x: map(
|
||||||
|
lambda entry: coin("\\item{<<entry>>}\n", {"entry": entry}),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
"".join,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"services-alternatives-problems-items": convey(
|
||||||
|
data["services"]["alternatives"]["problems"],
|
||||||
|
[
|
||||||
|
lambda x: map(
|
||||||
|
lambda entry: coin("\\item{<<entry>>}\n", {"entry": entry}),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
"".join,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"user_management-schema-frames": convey(
|
||||||
|
data["user_management"]["schema"],
|
||||||
|
[
|
||||||
|
lambda x: map(
|
||||||
|
lambda y: render(
|
||||||
|
"user_management-frame",
|
||||||
|
({"title": "Aufbau"} | y)
|
||||||
|
),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
"".join,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"user_management-process-entry-frames": convey(
|
||||||
|
data["user_management"]["processes"]["entry"],
|
||||||
|
[
|
||||||
|
lambda x: map(
|
||||||
|
lambda y: render(
|
||||||
|
"user_management-frame",
|
||||||
|
({"title": "Ablauf"} | y)
|
||||||
|
),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
"".join,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"user_management-example-link": data["user_management"]["example"]["link"],
|
||||||
|
"user_management-example-label": data["user_management"]["example"]["label"],
|
||||||
|
"user_management-example-remark": data["user_management"]["example"]["remark"],
|
||||||
|
"resources-frame": (
|
||||||
|
render(
|
||||||
|
"resources-frame",
|
||||||
|
{
|
||||||
|
"resource-items": convey(
|
||||||
|
data["resources"],
|
||||||
|
[
|
||||||
|
lambda x: map(
|
||||||
|
lambda resource: render(
|
||||||
|
"resource-item",
|
||||||
|
{
|
||||||
|
"name": resource["name"],
|
||||||
|
"link": resource["link"],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
"".join,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if args.include_resources else
|
||||||
|
""
|
||||||
|
),
|
||||||
|
"todos-technical-items": macro_todolist(data["realization"]["todos_technical"]),
|
||||||
|
"todos-social-items": macro_todolist(data["realization"]["todos_social"]),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
8
tools/fetch
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import json as _json
|
||||||
|
|
||||||
|
data = _json.loads(open("source/data.json").read())
|
||||||
|
for pair in data["service"]["practice"]["pool"].items():
|
||||||
|
print("# %s\ncurl -L %s -o source/media/icons/%s\n" % (pair[0], pair[1]["icon"], pair[0]))
|
||||||
|
|
199
tools/lib.py
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
import os as _os
|
||||||
|
|
||||||
|
|
||||||
|
def convey(x, fs):
|
||||||
|
y = x
|
||||||
|
for f in fs:
|
||||||
|
y = f(y)
|
||||||
|
return y
|
||||||
|
|
||||||
|
|
||||||
|
def string_coin(template, arguments, options = None):
|
||||||
|
options = (
|
||||||
|
{
|
||||||
|
"open": "{{",
|
||||||
|
"close": "}}",
|
||||||
|
}
|
||||||
|
|
|
||||||
|
(options or {})
|
||||||
|
)
|
||||||
|
result = template
|
||||||
|
for (key, value, ) in arguments.items():
|
||||||
|
if (value is None):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
result = result.replace(
|
||||||
|
(
|
||||||
|
"%s%s%s"
|
||||||
|
% (
|
||||||
|
options["open"],
|
||||||
|
key,
|
||||||
|
options["close"],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
value
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def file_read(path):
|
||||||
|
handle = open(path, "r")
|
||||||
|
content = handle.read()
|
||||||
|
handle.close()
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
def file_write(path, content):
|
||||||
|
_os.makedirs(_os.path.dirname(path), exist_ok = True)
|
||||||
|
handle = open(path, "w" if _os.path.exists(path) else "a")
|
||||||
|
handle.write(content)
|
||||||
|
handle.close()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def dot_entry(head, attributes, options = None):
|
||||||
|
options = (
|
||||||
|
{
|
||||||
|
"indentation": 0,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
(options or {})
|
||||||
|
)
|
||||||
|
return string_coin(
|
||||||
|
"{{indentation}}{{head}} [{{attributes}}];\n",
|
||||||
|
{
|
||||||
|
"indentation": ("\t" * options["indentation"]),
|
||||||
|
"head": head,
|
||||||
|
"attributes": convey(
|
||||||
|
attributes,
|
||||||
|
[
|
||||||
|
lambda x: x.items(),
|
||||||
|
lambda x: map(
|
||||||
|
lambda pair: string_coin(
|
||||||
|
"{{key}}=\"{{value}}\"",
|
||||||
|
{
|
||||||
|
"key": pair[0],
|
||||||
|
"value": pair[1]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
", ".join,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dot_node(node, options = None):
|
||||||
|
options = (
|
||||||
|
{
|
||||||
|
"indentation": 0,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
(options or {})
|
||||||
|
)
|
||||||
|
return dot_entry(
|
||||||
|
string_coin("{{name}}", {"name": node["name"]}),
|
||||||
|
node["attributes"],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dot_edge(edge, options = None):
|
||||||
|
options = (
|
||||||
|
{
|
||||||
|
"indentation": 0,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
(options or {})
|
||||||
|
)
|
||||||
|
return dot_entry(
|
||||||
|
string_coin("{{name_from}} -> {{name_to}}", {"name_from": edge["name_from"], "name_to": edge["name_to"]}),
|
||||||
|
edge["attributes"],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dot_subgraph(subgraph, options = None):
|
||||||
|
options = (
|
||||||
|
{
|
||||||
|
"indentation": 0,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
(options or {})
|
||||||
|
)
|
||||||
|
return string_coin(
|
||||||
|
(
|
||||||
|
"{{indentation}}subgraph cluster_{{name}}\n"
|
||||||
|
+
|
||||||
|
"{{indentation}}{\n"
|
||||||
|
+
|
||||||
|
"{{attributes}}\n"
|
||||||
|
+
|
||||||
|
"{{nodes}}\n"
|
||||||
|
+
|
||||||
|
"{{edges}}\n"
|
||||||
|
+
|
||||||
|
"{{indentation}}}\n"
|
||||||
|
),
|
||||||
|
{
|
||||||
|
"indentation": ("\t" * options["indentation"]),
|
||||||
|
"name": subgraph["name"],
|
||||||
|
"attributes": convey(
|
||||||
|
subgraph["attributes"],
|
||||||
|
[
|
||||||
|
lambda x: x.items(),
|
||||||
|
lambda x: map(
|
||||||
|
lambda pair: string_coin(
|
||||||
|
"{{indentation}}{{key}}=\"{{value}}\";\n",
|
||||||
|
{
|
||||||
|
"indentation": ("\t" * (options["indentation"] + 1)),
|
||||||
|
"key": pair[0],
|
||||||
|
"value": pair[1]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
x
|
||||||
|
),
|
||||||
|
"".join,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"nodes": "".join(map(lambda node: dot_node(node, {"indentation": (options["indentation"] + 1)}), subgraph["nodes"])),
|
||||||
|
"edges": "".join(map(lambda edge: dot_edge(edge, {"indentation": (options["indentation"] + 1)}), subgraph["edges"])),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dot_graph(graph):
|
||||||
|
return string_coin(
|
||||||
|
(
|
||||||
|
"digraph {{name}}\n"
|
||||||
|
+
|
||||||
|
"{\n"
|
||||||
|
+
|
||||||
|
"{{settings_graph}}\n"
|
||||||
|
+
|
||||||
|
"{{settings_node}}\n"
|
||||||
|
+
|
||||||
|
"{{settings_edge}}\n"
|
||||||
|
+
|
||||||
|
"{{subgraphs}}\n"
|
||||||
|
+
|
||||||
|
"{{nodes}}\n"
|
||||||
|
+
|
||||||
|
"{{edges}}\n"
|
||||||
|
+
|
||||||
|
"}\n"
|
||||||
|
),
|
||||||
|
{
|
||||||
|
"name": graph["name"],
|
||||||
|
"settings_graph": dot_entry("graph", graph["settings"]["graph"], {"indentation": 1}),
|
||||||
|
"settings_node": dot_entry("node", graph["settings"]["node"], {"indentation": 1}),
|
||||||
|
"settings_edge": dot_entry("edge", graph["settings"]["edge"], {"indentation": 1}),
|
||||||
|
"nodes": "".join(map(lambda node: dot_node(node, {"indentation": 1}), graph["nodes"])),
|
||||||
|
"edges": "".join(map(lambda edge: dot_edge(edge, {"indentation": 1}), graph["edges"])),
|
||||||
|
"subgraphs": "".join(map(lambda subgraph: dot_subgraph(subgraph, {"indentation": 1}), graph["subgraphs"])),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
61
tools/makefile
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
## consts/vars
|
||||||
|
|
||||||
|
dir_source := source
|
||||||
|
dir_temp := temp
|
||||||
|
dir_build := build
|
||||||
|
coin_args :=
|
||||||
|
latex_args :=
|
||||||
|
|
||||||
|
|
||||||
|
## commands
|
||||||
|
|
||||||
|
cmd_log := echo "--"
|
||||||
|
cmd_rm := rm --recursive --force
|
||||||
|
cmd_mkdir := mkdir --parents
|
||||||
|
cmd_cp := cp --recursive --update --verbose
|
||||||
|
cmd_latex := xelatex -shell-escape ${latex_args}
|
||||||
|
|
||||||
|
|
||||||
|
## rules
|
||||||
|
|
||||||
|
.PHONY: _default
|
||||||
|
_default: all
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: icons graphs ${dir_build}/infra.pdf
|
||||||
|
|
||||||
|
.PHONY: clear
|
||||||
|
clear:
|
||||||
|
@ ${cmd_log} "clearing …"
|
||||||
|
@ ${cmd_rm} ${dir_temp} ${dir_build}
|
||||||
|
|
||||||
|
.PHONY: graphs
|
||||||
|
graphs: ${dir_source}/graphs
|
||||||
|
@ ${cmd_log} "graphs …"
|
||||||
|
@ ${cmd_mkdir} ${dir_temp}/graphs
|
||||||
|
@ ${dir_source}/graphs --output-directory=${dir_temp}/graphs --format=svg # --no-extension
|
||||||
|
|
||||||
|
.PHONY: icons
|
||||||
|
icons:
|
||||||
|
@ ${cmd_log} "icons …"
|
||||||
|
@ ${cmd_mkdir} ${dir_temp}/media/icons
|
||||||
|
@ ${cmd_cp} ${dir_source}/media/icons/* ${dir_temp}/media/icons/
|
||||||
|
|
||||||
|
${dir_temp}/infra.tex: \
|
||||||
|
$(wildcard ${dir_source}/tex/*) \
|
||||||
|
${dir_source}/data.json \
|
||||||
|
$(wildcard ${dir_temp}/graphs/*) \
|
||||||
|
tools/coin
|
||||||
|
@ ${cmd_log} "coining …"
|
||||||
|
@ tools/coin --data-path=${dir_source}/data.json ${coin_args} > ${dir_temp}/infra.tex
|
||||||
|
|
||||||
|
${dir_temp}/infra.pdf: ${dir_temp}/infra.tex
|
||||||
|
@ ${cmd_log} "compiling …"
|
||||||
|
@ cd ${dir_temp} && ${cmd_latex} infra.tex && cd - > /dev/null
|
||||||
|
@ cd ${dir_temp} && ${cmd_latex} infra.tex && cd - > /dev/null
|
||||||
|
|
||||||
|
${dir_build}/infra.pdf: ${dir_temp}/infra.pdf
|
||||||
|
@ ${cmd_log} "placing …"
|
||||||
|
@ ${cmd_mkdir} ${dir_build}
|
||||||
|
@ ${cmd_cp} ${dir_temp}/infra.pdf ${dir_build}/infra.pdf
|
||||||
|
|