use virtualenv, pytz, added status monitor, target folder as parameter
This commit is contained in:
parent
b7b7996a3d
commit
a8f78b1c70
|
@ -0,0 +1,7 @@
|
|||
/web/logs/
|
||||
/bin/
|
||||
/lib/
|
||||
/lib64
|
||||
/include/
|
||||
/share/
|
||||
/__pycache__
|
|
@ -1,62 +1,55 @@
|
|||
from json import loads, dumps
|
||||
from makeline import make_line
|
||||
from sys import argv
|
||||
|
||||
with open('all-time.txt', 'r') as fin:
|
||||
(last_t, last_ev, last_evt, last_ps) = (0, 'error', 0, [])
|
||||
log_path = '/dev/null'
|
||||
|
||||
with open('events.json', 'w') as fout:
|
||||
cur_t = 0
|
||||
cur_ps = []
|
||||
def parse_logs():
|
||||
with open('all-time.txt', 'r') as fin:
|
||||
(last_t, last_ev, last_evt, last_ps) = (0, 'error', 0, [])
|
||||
|
||||
for line in fin:
|
||||
current = loads(line)
|
||||
cur_t = current['time']
|
||||
cur_ps = current['players']
|
||||
with open(log_path+'events.json', 'w') as fout:
|
||||
cur_t = 0
|
||||
cur_ps = []
|
||||
|
||||
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)
|
||||
for line in fin:
|
||||
current = loads(line)
|
||||
cur_t = current['time']
|
||||
cur_ps = current['players']
|
||||
|
||||
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)
|
||||
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 > 90:
|
||||
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)
|
||||
|
||||
last_t = cur_t
|
||||
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)
|
||||
|
||||
with open('current-state.json', 'w') as fout:
|
||||
fout.write(dumps({'period': [last_evt, last_t], 'status': 'online', 'players': last_ps}))
|
||||
last_t = cur_t
|
||||
|
||||
with open(log_path+'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:
|
||||
with open(log_path+'events.json', 'r') as fin:
|
||||
with open(log_path+'events.log', 'w') as fout:
|
||||
for l in fin:
|
||||
fout.write(make_line(loads(l))+'\n')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(argv) > 1:
|
||||
log_path = argv[1]+'/'
|
||||
parse_logs()
|
||||
events_to_lines()
|
||||
else:
|
||||
print('error: specify target path')
|
||||
|
|
|
@ -1,61 +1,59 @@
|
|||
from json import loads
|
||||
from datetime import datetime
|
||||
from pytz import utc
|
||||
from makeline import make_line, scale
|
||||
from re import compile
|
||||
from sys import argv
|
||||
|
||||
# scale down coordinates to dx=1 per day to prevent SVG coordinate overflows
|
||||
scale = 1/60/60/24
|
||||
log_path = '/dev/null'
|
||||
|
||||
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 get_lines(start,stop):
|
||||
lines = []
|
||||
find_start = compile(r'x1="([0-9.]+)"')
|
||||
def get_lines(start):
|
||||
lines = ''
|
||||
find_stop = compile(r'x2="([0-9.]+)"')
|
||||
with open('events.log', 'r') as fin:
|
||||
with open(log_path+'events.log', 'r') as fin:
|
||||
for l in fin:
|
||||
evstart = float(find_start.search(l).group(1))
|
||||
evstop = float(find_stop.search(l).group(1))
|
||||
|
||||
if evstop > start and evstart < stop:
|
||||
lines.append(l)
|
||||
with open('current-state.json', 'r') as fin:
|
||||
lines.append(make_line(loads(fin.read())))
|
||||
if evstop > start:
|
||||
break
|
||||
lines += l
|
||||
lines += fin.read()
|
||||
|
||||
with open(log_path+'current-state.json', 'r') as fin:
|
||||
lines += make_line(loads(fin.read()))
|
||||
|
||||
return lines
|
||||
|
||||
def make_html(name,period):
|
||||
|
||||
def make_html(name, period, display_scale = 144): # display_scale = 1 pixel per 10 minutes
|
||||
stop = scale*datetime.now().timestamp()
|
||||
start = stop-period
|
||||
lines = get_lines(start, stop)
|
||||
display_scale = 24*60/10 # 1 pixel per 10 minutes
|
||||
day_start = scale*datetime.fromtimestamp(start/scale).replace(hour=0,minute=0,second=0,microsecond=0,tzinfo=utc).timestamp()
|
||||
lines = get_lines(start)
|
||||
|
||||
with open('template.html', 'r') as fin:
|
||||
with open(name+'.html', 'w') as fout:
|
||||
with open(log_path+name+'.html', 'w') as fout:
|
||||
fout.write(fin.read().format(
|
||||
width = display_scale*period,
|
||||
start = start,
|
||||
period = period,
|
||||
day_start = scale*datetime.fromtimestamp(start/scale).replace(hour=0,minute=0,second=0,microsecond=0).timestamp(),
|
||||
day_start = day_start,
|
||||
day_stop = stop,
|
||||
events = ''.join(lines)
|
||||
events = lines
|
||||
))
|
||||
|
||||
|
||||
def make_htmls():
|
||||
make_html('day', 1)
|
||||
make_html('hour', 1/24, 24*60*4) # 4 pixels per minute
|
||||
make_html('day', 1, 24*60) # 1 pixel per minute
|
||||
make_html('month', 31)
|
||||
make_html('4months', 120)
|
||||
make_html('18months', 365.256*1.5, 24) # 1 pixel per hour
|
||||
|
||||
make_htmls()
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(argv) > 1:
|
||||
log_path = argv[1]+'/'
|
||||
make_htmls()
|
||||
else:
|
||||
print('error: specify target path')
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
from datetime import datetime
|
||||
from pytz import utc
|
||||
|
||||
# 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).astimezone(utc).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']
|
||||
)
|
|
@ -0,0 +1,3 @@
|
|||
home = /usr/bin
|
||||
include-system-site-packages = false
|
||||
version = 3.6.7
|
|
@ -2,20 +2,15 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<!-- <meta http-equiv='refresh' content='29'> -->
|
||||
<link rel='stylesheet' href='svg.css'>
|
||||
<meta http-equiv='refresh' content='17'>
|
||||
<link rel='stylesheet' href='../css/svg.css'>
|
||||
<link rel='icon' href='../img/favicon.png'>
|
||||
<script src="../js/svg-click.js" charset="utf-8"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<a href="day.html">last 24 hours</a>
|
||||
<a href="month.html">last month</a>
|
||||
<a href="4months.html">last 4 months</a>
|
||||
</nav>
|
||||
<div class="overflow">
|
||||
<svg id='graph' width='{width}' height='200' viewBox='{start} -0.5 {period} 10' preserveAspectRatio='none' version='1.1' xmlns='http://www.w3.org/2000/svg'>
|
||||
<line id='days' x1='{day_start}' x2='{day_stop}'></line>
|
||||
<svg id='graph' width='{width}' height='200' viewBox='{start} -0.5 {period} 10' preserveAspectRatio='none' version='1.1' xmlns='http://www.w3.org/2000/svg'>
|
||||
<line id='days' x1='{day_start}' x2='{day_stop}'></line>
|
||||
{events}
|
||||
</svg>
|
||||
</div>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
from mcstatus import MinecraftServer
|
||||
from datetime import datetime
|
||||
from makeline import make_line
|
||||
from json import loads, dumps
|
||||
from sys import argv
|
||||
|
||||
log_path = '/dev/null'
|
||||
|
||||
def get_status():
|
||||
time = datetime.now().timestamp()
|
||||
try:
|
||||
server = MinecraftServer("xkcd.cbouton.com", 25555)
|
||||
query = server.query(retries=1)
|
||||
players = query.players.names
|
||||
return {'time': time, 'status': 'online', 'players': players}
|
||||
except:
|
||||
return {'time': time, 'status': 'unreachable', 'players': []}
|
||||
|
||||
|
||||
def log(event):
|
||||
if event['period'][0] != event['period'][1]:
|
||||
with open(log_path+'events.json', 'a') as fout:
|
||||
fout.write(dumps(event)+'\n')
|
||||
with open(log_path+'events.log', 'a') as fout:
|
||||
fout.write(make_line(event)+'\n')
|
||||
|
||||
|
||||
def new_state_and_log():
|
||||
prev = {'period': [0,0], 'status': 'undefined', 'players': []}
|
||||
cur = get_status()
|
||||
|
||||
with open(log_path+'current-state.json', 'r') as fin:
|
||||
prev = loads(fin.read())
|
||||
|
||||
if cur['time'] - prev['period'][1] > 90:
|
||||
log(prev)
|
||||
log({'period': [prev['period'][1], cur['time']], 'status': 'monitor-offline' })
|
||||
return {'period': [cur['time'],cur['time']], 'status': cur['status'], 'players': cur['players']}
|
||||
else:
|
||||
prev['period'][1] = cur['time']
|
||||
if prev['status'] == cur['status'] and prev['players'] == cur['players']:
|
||||
return prev
|
||||
else:
|
||||
log(prev)
|
||||
return {'period': [cur['time'],cur['time']], 'status': cur['status'], 'players': cur['players']}
|
||||
|
||||
|
||||
def update():
|
||||
next = new_state_and_log()
|
||||
if next['status'] != 'online':
|
||||
del next['players']
|
||||
|
||||
with open(log_path+'current-state.json', 'w') as fout:
|
||||
fout.write(dumps(next))
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(argv) > 1:
|
||||
log_path = argv[1]+'/'
|
||||
update()
|
||||
else:
|
||||
print('error: specify target path')
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
source bin/activate
|
||||
|
||||
python3 update-status.py /var/www/xkcd.flumble.nl/logs
|
||||
python3 generate-html.py /var/www/xkcd.flumble.nl/logs
|
Loading…
Reference in New Issue