From 436a00d8ce03ac446159ee4fda09d8c6f87f810e Mon Sep 17 00:00:00 2001
From: User <>
Date: Wed, 13 Mar 2019 19:38:21 +0100
Subject: [PATCH] generate svg with XML
---
convert-logs.py | 62 +++++++++++++++++++++++++++++++++++++
generate-svg.py | 82 +++++++++++++++++++++++++++++++++++++++++++++++++
index.html | 11 +++++++
style.css | 30 ++++++++++++++++++
4 files changed, 185 insertions(+)
create mode 100644 convert-logs.py
create mode 100644 generate-svg.py
create mode 100644 index.html
create mode 100644 style.css
diff --git a/convert-logs.py b/convert-logs.py
new file mode 100644
index 0000000..90c8b61
--- /dev/null
+++ b/convert-logs.py
@@ -0,0 +1,62 @@
+from json import loads, dumps
+
+with open('all-time.txt', 'r') as fin:
+ (last_t, last_ev, last_evt, last_ps) = (0, 'error', 0, [])
+
+ with open('events.json', 'w') as fout:
+ cur_t = 0
+ cur_ps = []
+
+ for line in fin:
+ current = loads(line)
+ cur_t = current['time']
+ cur_ps = current['players']
+
+ if last_t == 0:
+ (last_t, last_ev, last_evt, last_ps) = (cur_t, 'online', cur_t, cur_ps)
+ else:
+ if cur_t - last_t > 70:
+ if last_ev == 'online':
+ fout.write(dumps({'period': [last_evt, last_t], 'status': last_ev, 'players': last_ps})+'\n')
+ (last_ev, last_evt, last_ps) = ('unreachable', last_t, cur_ps)
+ else:
+ if last_ev != 'online':
+ fout.write(dumps({'period': [last_evt, last_t], 'status': last_ev})+'\n')
+ (last_ev, last_evt, last_ps) = ('online', last_t, last_ps)
+
+ if last_ps != cur_ps:
+ fout.write(dumps({'period': [last_evt, cur_t], 'status': last_ev, 'players': last_ps})+'\n')
+ (last_ev, last_evt, last_ps) = ('online', cur_t, cur_ps)
+
+ last_t = cur_t
+
+ with open('current-state.json', 'w') as fout:
+ fout.write(dumps({'period': [last_evt, last_t], 'status': 'online', 'players': last_ps}))
+
+
+
+
+# scale down coordinates to dx=1 per day to prevent SVG coordinate overflows
+scale = 1/60/60/24
+
+def get_date(dtime):
+ return datetime.fromtimestamp(dtime).strftime('%Y-%m-%d %H:%M')
+
+def make_line(event):
+ evstart = event['period'][0]
+ evstop = event['period'][1]
+ return '{text} ({start_date} — {stop_date})'.format(
+ type = '' if event['status'] == 'online' else ' class="error"',
+ start = scale*evstart,
+ stop = scale*evstop,
+ start_date = get_date(evstart),
+ stop_date = get_date(evstop),
+ height = len(event['players']) if 'players' in event else 0,
+ text = ('no one' if len(event['players']) == 0 else ', '.join(event['players'])) if event['status'] == 'online' else event['status']
+ )
+
+def events_to_lines():
+ with open('events.json', 'r') as fin:
+ with open('events.log', 'w') as fout:
+ for l in fin:
+ fout.write(make_line(loads(l))+'\n')
diff --git a/generate-svg.py b/generate-svg.py
new file mode 100644
index 0000000..2aaea9b
--- /dev/null
+++ b/generate-svg.py
@@ -0,0 +1,82 @@
+from json import loads
+from xml.etree.ElementTree import Element, tostring
+from datetime import datetime
+
+def readEvents():
+ events = []
+ with open('events.json', 'r') as f:
+ for line in f:
+ events.append(loads(line))
+
+ with open('current-state.json', 'r') as f:
+ events.append(loads(f.read()))
+
+ return events
+
+def getSVG(events):
+ scale = 1/60/60/24
+ period = 3*30 *60*60*24
+ end = datetime.now().timestamp()
+ start = end-period
+# start = events[0]['period'][0]
+# end = events[-1]['period'][1]
+ root = Element('svg', {
+ 'version': '1.1',
+ 'xmlns': 'http://www.w3.org/2000/svg',
+ 'viewBox': '0 0 {} 200'.format(scale*period*200),
+ 'width': str(scale*period*200),
+ 'height': '200',
+ })
+ group = Element('svg', {
+ 'viewBox': '{} 0 {} 10'.format(scale*start,scale*period),
+ 'width': '100%',
+ 'height': '100%',
+ 'preserveAspectRatio': 'none'
+ })
+
+ for i in range(0,10+1):
+ num = Element('text', {
+ 'x': str(scale*period*200-20),
+ 'y': str((1-i/10)*200)
+ })
+ num.text = str(i)
+ root.append(num)
+ root.append(group)
+
+ for event in events:
+ evstart = event['period'][0]
+ evend = event['period'][1]
+
+ if evend < start or evstart > end:
+ continue
+
+ props = { 'x1': str(scale*evstart), 'x2': str(scale*evend), 'y1': '0', 'y2': '0' }
+ titletext = 'unknown'
+
+ if 'players' in event:
+ y = str(len(event['players']))
+ props['y1'] = y
+ props['y2'] = y
+ if len(event['players']) == 0:
+ titletext = '(no one)'
+ else:
+ titletext = ', '.join(event['players'])
+
+ if event['type'] == 'error':
+ props['class'] = 'error'
+ titletext = 'error'
+
+ ev = Element('line', props)
+ title = Element('title')
+ title.text = '{} ({} — {})'.format(titletext, datetime.fromtimestamp(evstart).strftime('%Y-%m-%d %H:%M'), datetime.fromtimestamp(evend).strftime('%Y-%m-%d %H:%M'))
+ ev.append(title)
+ group.append(ev)
+
+ return root
+
+def dumpHTML(arg):
+ with open('index.html', 'r') as f:
+ html = f.read()
+ print( html.format(arg) )
+
+dumpHTML(tostring(getSVG(readEvents()), encoding='unicode'))
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..6ad6dd5
--- /dev/null
+++ b/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+ {}
+
+
+
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..f500be5
--- /dev/null
+++ b/style.css
@@ -0,0 +1,30 @@
+#graph {
+ background-color: #EEE;
+}
+
+#events {
+ transform: scaleY(-1) translateY(-100%);
+}
+
+text {
+ dominant-baseline: central;
+}
+
+#days {
+ stroke: grey;
+ stroke-width: 30;
+ stroke-dasharray: 0.01, 0.99;
+}
+
+line {
+ stroke: black;
+ stroke-width: 0.4;
+}
+
+line.error {
+ stroke: red;
+}
+
+line:hover {
+ stroke-width: 0.6;
+}