Use pendulum instead of plain datetime
because it is more intuitive to use and doesn't require the tz_aware_datetime-workaround.
This commit is contained in:
parent
3d469ce28c
commit
65a8379261
|
@ -1 +1,2 @@
|
||||||
Jinja2>=2.8
|
Jinja2>=2.8
|
||||||
|
pendulum
|
||||||
|
|
|
@ -4,13 +4,13 @@ import logging
|
||||||
import re
|
import re
|
||||||
from codecs import open
|
from codecs import open
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from datetime import datetime
|
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from os.path import basename
|
from os.path import basename
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
|
import pendulum
|
||||||
|
|
||||||
from tsstats.client import Client, Clients
|
from tsstats.client import Client, Clients
|
||||||
from tsstats.utils import tz_aware_datime
|
|
||||||
|
|
||||||
re_log_filename = re.compile(r'ts3server_(?P<date>\d{4}-\d\d-\d\d)'
|
re_log_filename = re.compile(r'ts3server_(?P<date>\d{4}-\d\d-\d\d)'
|
||||||
'__(?P<time>\d\d_\d\d_\d\d.\d+)_(?P<sid>\d).log')
|
'__(?P<time>\d\d_\d\d_\d\d.\d+)_(?P<sid>\d).log')
|
||||||
|
@ -23,8 +23,6 @@ re_disconnect_invoker = re.compile(
|
||||||
r'invokername=(.*)\ invokeruid=(.*)\ reasonmsg'
|
r'invokername=(.*)\ invokeruid=(.*)\ reasonmsg'
|
||||||
)
|
)
|
||||||
|
|
||||||
log_timestamp_format = '%Y-%m-%d %H:%M:%S.%f'
|
|
||||||
|
|
||||||
TimedLog = namedtuple('TimedLog', ['path', 'timestamp'])
|
TimedLog = namedtuple('TimedLog', ['path', 'timestamp'])
|
||||||
Server = namedtuple('Server', ['sid', 'clients'])
|
Server = namedtuple('Server', ['sid', 'clients'])
|
||||||
|
|
||||||
|
@ -81,9 +79,9 @@ def _bundle_logs(logs):
|
||||||
match = re_log_filename.match(basename(log))
|
match = re_log_filename.match(basename(log))
|
||||||
if match:
|
if match:
|
||||||
match = match.groupdict()
|
match = match.groupdict()
|
||||||
timestamp = datetime.strptime('{0} {1}'.format(
|
timestamp = pendulum.parse('{0} {1}'.format(
|
||||||
match['date'], match['time'].replace('_', ':')),
|
match['date'], match['time'].replace('_', ':'))
|
||||||
log_timestamp_format)
|
)
|
||||||
tl = TimedLog(log, timestamp)
|
tl = TimedLog(log, timestamp)
|
||||||
sid = match['sid']
|
sid = match['sid']
|
||||||
if sid in vserver_logfiles:
|
if sid in vserver_logfiles:
|
||||||
|
@ -136,8 +134,7 @@ def _parse_details(log_path, ident_map=None, clients=None, online_dc=True):
|
||||||
logger.debug('No match: "%s"', line)
|
logger.debug('No match: "%s"', line)
|
||||||
continue
|
continue
|
||||||
match = match.groupdict()
|
match = match.groupdict()
|
||||||
logdatetime = tz_aware_datime(datetime.strptime(match['timestamp'],
|
logdatetime = pendulum.parse(match['timestamp'])
|
||||||
log_timestamp_format))
|
|
||||||
message = match['message']
|
message = match['message']
|
||||||
if message.startswith('client'):
|
if message.startswith('client'):
|
||||||
match = re_dis_connect.match(message)
|
match = re_dis_connect.match(message)
|
||||||
|
@ -178,7 +175,7 @@ def _parse_details(log_path, ident_map=None, clients=None, online_dc=True):
|
||||||
]
|
]
|
||||||
if online_dc:
|
if online_dc:
|
||||||
def _reconnect(client):
|
def _reconnect(client):
|
||||||
client.disconnect(tz_aware_datime(datetime.utcnow()))
|
client.disconnect(pendulum.now())
|
||||||
client.connected += 1
|
client.connected += 1
|
||||||
[_reconnect(client) for client in clients if client.connected]
|
[_reconnect(client) for client in clients if client.connected]
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
|
|
@ -2,14 +2,13 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from datetime import datetime
|
|
||||||
from os.path import dirname, join
|
from os.path import dirname, join
|
||||||
|
|
||||||
|
import pendulum
|
||||||
from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PackageLoader
|
from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PackageLoader
|
||||||
|
|
||||||
from tsstats.log import Server
|
from tsstats.log import Server
|
||||||
from tsstats.utils import (filter_threshold, seconds_to_text, sort_clients,
|
from tsstats.utils import filter_threshold, seconds_to_text, sort_clients
|
||||||
tz_aware_datime)
|
|
||||||
|
|
||||||
logger = logging.getLogger('tsstats')
|
logger = logging.getLogger('tsstats')
|
||||||
|
|
||||||
|
@ -43,8 +42,7 @@ def prepare_clients(clients, onlinetime_threshold=-1):
|
||||||
clients, lambda c: c.onlinetime.total_seconds()
|
clients, lambda c: c.onlinetime.total_seconds()
|
||||||
)
|
)
|
||||||
# filter clients not matching threshold
|
# filter clients not matching threshold
|
||||||
onlinetime_ = filter_threshold(onlinetime_,
|
onlinetime_ = filter_threshold(onlinetime_, onlinetime_threshold)
|
||||||
onlinetime_threshold)
|
|
||||||
# convert timespans to text
|
# convert timespans to text
|
||||||
onlinetime = [
|
onlinetime = [
|
||||||
(client, seconds_to_text(int(onlinetime)))
|
(client, seconds_to_text(int(onlinetime)))
|
||||||
|
@ -105,6 +103,6 @@ def render_servers(servers, output, title='TeamspeakStats',
|
||||||
logger.debug('Rendering template %s', template)
|
logger.debug('Rendering template %s', template)
|
||||||
template.stream(title=title, servers=prepared_servers,
|
template.stream(title=title, servers=prepared_servers,
|
||||||
debug=logger.level <= logging.DEBUG,
|
debug=logger.level <= logging.DEBUG,
|
||||||
creation_time=tz_aware_datime(datetime.utcnow()))\
|
creation_time=pendulum.utcnow())\
|
||||||
.dump(output, encoding='utf-8')
|
.dump(output, encoding='utf-8')
|
||||||
logger.debug('Wrote rendered template to %s', output)
|
logger.debug('Wrote rendered template to %s', output)
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
from datetime import datetime, timedelta
|
import pendulum
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tsstats.exceptions import InvalidLog
|
from tsstats.exceptions import InvalidLog
|
||||||
|
@ -20,8 +18,10 @@ def test_log_client_count(clients):
|
||||||
|
|
||||||
|
|
||||||
def test_log_onlinetime(clients):
|
def test_log_onlinetime(clients):
|
||||||
assert clients['1'].onlinetime == timedelta(0, 402, 149208)
|
assert clients['1'].onlinetime == pendulum.Interval(
|
||||||
assert clients['2'].onlinetime == timedelta(0, 19, 759644)
|
seconds=402, microseconds=149208)
|
||||||
|
assert clients['2'].onlinetime == pendulum.Interval(
|
||||||
|
seconds=19, microseconds=759644)
|
||||||
|
|
||||||
|
|
||||||
def test_log_kicks(clients):
|
def test_log_kicks(clients):
|
||||||
|
@ -52,12 +52,20 @@ def test_log_pbans(clients):
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
'1': [
|
'1': [
|
||||||
TimedLog('ts3server_2016-06-06__14_22_09.527229_1.log',
|
TimedLog(
|
||||||
datetime(year=2016, month=6, day=6, hour=14,
|
'ts3server_2016-06-06__14_22_09.527229_1.log',
|
||||||
minute=22, second=9, microsecond=527229)),
|
pendulum.create(
|
||||||
TimedLog('ts3server_2017-07-07__15_23_10.638340_1.log',
|
year=2016, month=6, day=6, hour=14, minute=22,
|
||||||
datetime(year=2017, month=7, day=7, hour=15,
|
second=9, microsecond=527229
|
||||||
minute=23, second=10, microsecond=638340))
|
)
|
||||||
|
),
|
||||||
|
TimedLog(
|
||||||
|
'ts3server_2017-07-07__15_23_10.638340_1.log',
|
||||||
|
pendulum.create(
|
||||||
|
year=2017, month=7, day=7, hour=15, minute=23,
|
||||||
|
second=10, microsecond=638340
|
||||||
|
)
|
||||||
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -71,11 +79,14 @@ def test_log_invalid():
|
||||||
_parse_details('tsstats/tests/res/test.log.broken')
|
_parse_details('tsstats/tests/res/test.log.broken')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.slowtest
|
|
||||||
def test_log_client_online():
|
def test_log_client_online():
|
||||||
|
current_time = pendulum.now()
|
||||||
|
|
||||||
|
pendulum.set_test_now(current_time)
|
||||||
clients = _parse_details(testlog_path)
|
clients = _parse_details(testlog_path)
|
||||||
old_onlinetime = int(clients['1'].onlinetime.total_seconds())
|
old_onlinetime = int(clients['1'].onlinetime.total_seconds())
|
||||||
sleep(2)
|
|
||||||
|
pendulum.set_test_now(current_time.add(seconds=2)) # add 2s to .now()
|
||||||
clients = _parse_details(testlog_path)
|
clients = _parse_details(testlog_path)
|
||||||
assert int(clients['1'].onlinetime.total_seconds()) == old_onlinetime + 2
|
assert int(clients['1'].onlinetime.total_seconds()) == old_onlinetime + 2
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
|
import pendulum
|
||||||
import pytest
|
import pytest
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ def test_onlinetime(soup):
|
||||||
onlinetime = onlinetime.text
|
onlinetime = onlinetime.text
|
||||||
# find corresponding client-object
|
# find corresponding client-object
|
||||||
client = list(filter(
|
client = list(filter(
|
||||||
lambda c: c.nick == nick and c.onlinetime > timedelta(0),
|
lambda c: c.nick == nick and c.onlinetime > pendulum.Interval(),
|
||||||
clients
|
clients
|
||||||
))
|
))
|
||||||
# assert existence
|
# assert existence
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import datetime
|
|
||||||
|
|
||||||
|
|
||||||
def sort_clients(clients, key_l):
|
def sort_clients(clients, key_l):
|
||||||
'''
|
'''
|
||||||
sort `clients` by `key`
|
sort `clients` by `key`
|
||||||
|
@ -52,34 +49,6 @@ def filter_threshold(clients, threshold):
|
||||||
return list(filter(lambda c: c[1] > threshold, clients))
|
return list(filter(lambda c: c[1] > threshold, clients))
|
||||||
|
|
||||||
|
|
||||||
class UTC(datetime.tzinfo):
|
|
||||||
'''
|
|
||||||
Reimplementation of `timezone.utc` for Python2-Compatibility
|
|
||||||
'''
|
|
||||||
|
|
||||||
def utcoffset(self, dt):
|
|
||||||
return datetime.timedelta(0)
|
|
||||||
|
|
||||||
def dst(self, dt):
|
|
||||||
return datetime.timedelta(0)
|
|
||||||
|
|
||||||
def tzname(self, dt):
|
|
||||||
return 'UTC'
|
|
||||||
|
|
||||||
|
|
||||||
def tz_aware_datime(datetime, timezone=UTC()):
|
|
||||||
'''
|
|
||||||
Make `datetime` aware of it's timezone (UTC by default)
|
|
||||||
|
|
||||||
:param datetime: Target datetime
|
|
||||||
:param timezone: Target timezone
|
|
||||||
|
|
||||||
:type datetime: datetime.datetime
|
|
||||||
:type timezone: datetime.timezone
|
|
||||||
'''
|
|
||||||
return datetime.replace(tzinfo=timezone)
|
|
||||||
|
|
||||||
|
|
||||||
def transform_pretty_identmap(pretty_identmap):
|
def transform_pretty_identmap(pretty_identmap):
|
||||||
'''
|
'''
|
||||||
Transforms a list of client ID mappings from a more descriptive format
|
Transforms a list of client ID mappings from a more descriptive format
|
||||||
|
|
Loading…
Reference in New Issue