generate svg with XML
This commit is contained in:
commit
436a00d8ce
|
@ -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 '<line{type} x1="{start}" x2="{stop}" y1="{height}" y2="{height}"><title>{text} ({start_date} — {stop_date})</title></line>'.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')
|
|
@ -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'))
|
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<link rel='stylesheet' href='style.css'>
|
||||
</head>
|
||||
<body>
|
||||
{}
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue