From 65a83792615d6aafdd1f26494e0d083b028741fd Mon Sep 17 00:00:00 2001 From: Thor77 <thor77@thor77.org> Date: Sat, 20 May 2017 00:31:26 +0200 Subject: [PATCH] Use pendulum instead of plain datetime because it is more intuitive to use and doesn't require the tz_aware_datetime-workaround. --- requirements.txt | 1 + tsstats/log.py | 17 +++++++--------- tsstats/template.py | 10 ++++----- tsstats/tests/test_log.py | 37 ++++++++++++++++++++++------------ tsstats/tests/test_template.py | 4 ++-- tsstats/utils.py | 31 ---------------------------- 6 files changed, 38 insertions(+), 62 deletions(-) diff --git a/requirements.txt b/requirements.txt index f10e84e..a673ff4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ Jinja2>=2.8 +pendulum diff --git a/tsstats/log.py b/tsstats/log.py index 1a4ff80..ca28156 100644 --- a/tsstats/log.py +++ b/tsstats/log.py @@ -4,13 +4,13 @@ import logging import re from codecs import open from collections import namedtuple -from datetime import datetime from glob import glob from os.path import basename from time import time +import pendulum + 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)' '__(?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' ) -log_timestamp_format = '%Y-%m-%d %H:%M:%S.%f' - TimedLog = namedtuple('TimedLog', ['path', 'timestamp']) Server = namedtuple('Server', ['sid', 'clients']) @@ -81,9 +79,9 @@ def _bundle_logs(logs): match = re_log_filename.match(basename(log)) if match: match = match.groupdict() - timestamp = datetime.strptime('{0} {1}'.format( - match['date'], match['time'].replace('_', ':')), - log_timestamp_format) + timestamp = pendulum.parse('{0} {1}'.format( + match['date'], match['time'].replace('_', ':')) + ) tl = TimedLog(log, timestamp) sid = match['sid'] 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) continue match = match.groupdict() - logdatetime = tz_aware_datime(datetime.strptime(match['timestamp'], - log_timestamp_format)) + logdatetime = pendulum.parse(match['timestamp']) message = match['message'] if message.startswith('client'): 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: def _reconnect(client): - client.disconnect(tz_aware_datime(datetime.utcnow())) + client.disconnect(pendulum.now()) client.connected += 1 [_reconnect(client) for client in clients if client.connected] logger.debug( diff --git a/tsstats/template.py b/tsstats/template.py index abe438f..fd290a4 100644 --- a/tsstats/template.py +++ b/tsstats/template.py @@ -2,14 +2,13 @@ import logging from collections import namedtuple -from datetime import datetime from os.path import dirname, join +import pendulum from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PackageLoader from tsstats.log import Server -from tsstats.utils import (filter_threshold, seconds_to_text, sort_clients, - tz_aware_datime) +from tsstats.utils import filter_threshold, seconds_to_text, sort_clients logger = logging.getLogger('tsstats') @@ -43,8 +42,7 @@ def prepare_clients(clients, onlinetime_threshold=-1): clients, lambda c: c.onlinetime.total_seconds() ) # filter clients not matching threshold - onlinetime_ = filter_threshold(onlinetime_, - onlinetime_threshold) + onlinetime_ = filter_threshold(onlinetime_, onlinetime_threshold) # convert timespans to text onlinetime = [ (client, seconds_to_text(int(onlinetime))) @@ -105,6 +103,6 @@ def render_servers(servers, output, title='TeamspeakStats', logger.debug('Rendering template %s', template) template.stream(title=title, servers=prepared_servers, debug=logger.level <= logging.DEBUG, - creation_time=tz_aware_datime(datetime.utcnow()))\ + creation_time=pendulum.utcnow())\ .dump(output, encoding='utf-8') logger.debug('Wrote rendered template to %s', output) diff --git a/tsstats/tests/test_log.py b/tsstats/tests/test_log.py index 1ef16bd..785aee8 100644 --- a/tsstats/tests/test_log.py +++ b/tsstats/tests/test_log.py @@ -1,6 +1,4 @@ -from datetime import datetime, timedelta -from time import sleep - +import pendulum import pytest from tsstats.exceptions import InvalidLog @@ -20,8 +18,10 @@ def test_log_client_count(clients): def test_log_onlinetime(clients): - assert clients['1'].onlinetime == timedelta(0, 402, 149208) - assert clients['2'].onlinetime == timedelta(0, 19, 759644) + assert clients['1'].onlinetime == pendulum.Interval( + seconds=402, microseconds=149208) + assert clients['2'].onlinetime == pendulum.Interval( + seconds=19, microseconds=759644) def test_log_kicks(clients): @@ -52,12 +52,20 @@ def test_log_pbans(clients): ], { '1': [ - TimedLog('ts3server_2016-06-06__14_22_09.527229_1.log', - datetime(year=2016, month=6, day=6, hour=14, - minute=22, second=9, microsecond=527229)), - TimedLog('ts3server_2017-07-07__15_23_10.638340_1.log', - datetime(year=2017, month=7, day=7, hour=15, - minute=23, second=10, microsecond=638340)) + TimedLog( + 'ts3server_2016-06-06__14_22_09.527229_1.log', + pendulum.create( + year=2016, month=6, day=6, hour=14, minute=22, + second=9, microsecond=527229 + ) + ), + 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') -@pytest.mark.slowtest def test_log_client_online(): + current_time = pendulum.now() + + pendulum.set_test_now(current_time) clients = _parse_details(testlog_path) 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) assert int(clients['1'].onlinetime.total_seconds()) == old_onlinetime + 2 diff --git a/tsstats/tests/test_template.py b/tsstats/tests/test_template.py index f7422d3..d48c0c3 100644 --- a/tsstats/tests/test_template.py +++ b/tsstats/tests/test_template.py @@ -1,6 +1,6 @@ import logging -from datetime import timedelta +import pendulum import pytest from bs4 import BeautifulSoup @@ -44,7 +44,7 @@ def test_onlinetime(soup): onlinetime = onlinetime.text # find corresponding client-object client = list(filter( - lambda c: c.nick == nick and c.onlinetime > timedelta(0), + lambda c: c.nick == nick and c.onlinetime > pendulum.Interval(), clients )) # assert existence diff --git a/tsstats/utils.py b/tsstats/utils.py index 12b8459..62044d7 100644 --- a/tsstats/utils.py +++ b/tsstats/utils.py @@ -1,7 +1,4 @@ # -*- coding: utf-8 -*- -import datetime - - def sort_clients(clients, key_l): ''' sort `clients` by `key` @@ -52,34 +49,6 @@ def filter_threshold(clients, threshold): 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): ''' Transforms a list of client ID mappings from a more descriptive format