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; +}