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,9 +1,14 @@
|
||||||
from json import loads, dumps
|
from json import loads, dumps
|
||||||
|
from makeline import make_line
|
||||||
|
from sys import argv
|
||||||
|
|
||||||
|
log_path = '/dev/null'
|
||||||
|
|
||||||
|
def parse_logs():
|
||||||
with open('all-time.txt', 'r') as fin:
|
with open('all-time.txt', 'r') as fin:
|
||||||
(last_t, last_ev, last_evt, last_ps) = (0, 'error', 0, [])
|
(last_t, last_ev, last_evt, last_ps) = (0, 'error', 0, [])
|
||||||
|
|
||||||
with open('events.json', 'w') as fout:
|
with open(log_path+'events.json', 'w') as fout:
|
||||||
cur_t = 0
|
cur_t = 0
|
||||||
cur_ps = []
|
cur_ps = []
|
||||||
|
|
||||||
|
@ -15,7 +20,7 @@ with open('all-time.txt', 'r') as fin:
|
||||||
if last_t == 0:
|
if last_t == 0:
|
||||||
(last_t, last_ev, last_evt, last_ps) = (cur_t, 'online', cur_t, cur_ps)
|
(last_t, last_ev, last_evt, last_ps) = (cur_t, 'online', cur_t, cur_ps)
|
||||||
else:
|
else:
|
||||||
if cur_t - last_t > 70:
|
if cur_t - last_t > 90:
|
||||||
if last_ev == 'online':
|
if last_ev == 'online':
|
||||||
fout.write(dumps({'period': [last_evt, last_t], 'status': last_ev, 'players': last_ps})+'\n')
|
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)
|
(last_ev, last_evt, last_ps) = ('unreachable', last_t, cur_ps)
|
||||||
|
@ -30,33 +35,21 @@ with open('all-time.txt', 'r') as fin:
|
||||||
|
|
||||||
last_t = cur_t
|
last_t = cur_t
|
||||||
|
|
||||||
with open('current-state.json', 'w') as fout:
|
with open(log_path+'current-state.json', 'w') as fout:
|
||||||
fout.write(dumps({'period': [last_evt, last_t], 'status': 'online', 'players': last_ps}))
|
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():
|
def events_to_lines():
|
||||||
with open('events.json', 'r') as fin:
|
with open(log_path+'events.json', 'r') as fin:
|
||||||
with open('events.log', 'w') as fout:
|
with open(log_path+'events.log', 'w') as fout:
|
||||||
for l in fin:
|
for l in fin:
|
||||||
fout.write(make_line(loads(l))+'\n')
|
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 json import loads
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from pytz import utc
|
||||||
|
from makeline import make_line, scale
|
||||||
from re import compile
|
from re import compile
|
||||||
|
from sys import argv
|
||||||
|
|
||||||
# scale down coordinates to dx=1 per day to prevent SVG coordinate overflows
|
log_path = '/dev/null'
|
||||||
scale = 1/60/60/24
|
|
||||||
|
|
||||||
def get_date(dtime):
|
def get_lines(start):
|
||||||
return datetime.fromtimestamp(dtime).strftime('%Y-%m-%d %H:%M')
|
lines = ''
|
||||||
|
|
||||||
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.]+)"')
|
|
||||||
find_stop = compile(r'x2="([0-9.]+)"')
|
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:
|
for l in fin:
|
||||||
evstart = float(find_start.search(l).group(1))
|
|
||||||
evstop = float(find_stop.search(l).group(1))
|
evstop = float(find_stop.search(l).group(1))
|
||||||
|
|
||||||
if evstop > start and evstart < stop:
|
if evstop > start:
|
||||||
lines.append(l)
|
break
|
||||||
with open('current-state.json', 'r') as fin:
|
lines += l
|
||||||
lines.append(make_line(loads(fin.read())))
|
lines += fin.read()
|
||||||
|
|
||||||
|
with open(log_path+'current-state.json', 'r') as fin:
|
||||||
|
lines += make_line(loads(fin.read()))
|
||||||
|
|
||||||
return lines
|
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()
|
stop = scale*datetime.now().timestamp()
|
||||||
start = stop-period
|
start = stop-period
|
||||||
lines = get_lines(start, stop)
|
day_start = scale*datetime.fromtimestamp(start/scale).replace(hour=0,minute=0,second=0,microsecond=0,tzinfo=utc).timestamp()
|
||||||
display_scale = 24*60/10 # 1 pixel per 10 minutes
|
lines = get_lines(start)
|
||||||
|
|
||||||
with open('template.html', 'r') as fin:
|
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(
|
fout.write(fin.read().format(
|
||||||
width = display_scale*period,
|
width = display_scale*period,
|
||||||
start = start,
|
start = start,
|
||||||
period = period,
|
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,
|
day_stop = stop,
|
||||||
events = ''.join(lines)
|
events = lines
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
def make_htmls():
|
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('month', 31)
|
||||||
make_html('4months', 120)
|
make_html('4months', 120)
|
||||||
|
make_html('18months', 365.256*1.5, 24) # 1 pixel per hour
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(argv) > 1:
|
||||||
|
log_path = argv[1]+'/'
|
||||||
make_htmls()
|
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>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset='utf-8'>
|
<meta charset='utf-8'>
|
||||||
<!-- <meta http-equiv='refresh' content='29'> -->
|
<meta http-equiv='refresh' content='17'>
|
||||||
<link rel='stylesheet' href='svg.css'>
|
<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>
|
</head>
|
||||||
<body>
|
<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'>
|
<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>
|
<line id='days' x1='{day_start}' x2='{day_stop}'></line>
|
||||||
{events}
|
{events}
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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