mirror of
https://github.com/GothenburgBitFactory/timewarrior.git
synced 2025-06-26 10:54:28 +02:00
Refactor totals.py
- add main - move algorithm to function - use __future__ module - introduce new style formatting - restructure test_totals.t accordingly - add test for empty range - add tests for colored output
This commit is contained in:
parent
d5e2fd8f62
commit
b5acfed98f
2 changed files with 388 additions and 169 deletions
163
ext/totals.py
163
ext/totals.py
|
@ -25,124 +25,135 @@
|
||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
from dateutil import tz
|
from dateutil import tz
|
||||||
|
|
||||||
|
DATEFORMAT = "%Y%m%dT%H%M%SZ"
|
||||||
|
|
||||||
|
|
||||||
def format_seconds(seconds):
|
def format_seconds(seconds):
|
||||||
"""Convert seconds to a formatted string
|
"""Convert seconds to a formatted string
|
||||||
|
|
||||||
Convert seconds: 3661
|
Convert seconds: 3661
|
||||||
To formatted: 1:01:01
|
To formatted: " 1:01:01"
|
||||||
"""
|
"""
|
||||||
hours = int(seconds / 3600)
|
hours = int(seconds / 3600)
|
||||||
minutes = int(seconds % 3600) / 60
|
minutes = int(seconds % 3600 / 60)
|
||||||
seconds = seconds % 60
|
seconds = seconds % 60
|
||||||
return "%4d:%02d:%02d" % (hours, minutes, seconds)
|
return "{:4d}:{:02d}:{:02d}".format(hours, minutes, seconds)
|
||||||
|
|
||||||
|
|
||||||
DATEFORMAT = "%Y%m%dT%H%M%SZ"
|
def calculate_totals(input_stream):
|
||||||
|
from_zone = tz.tzutc()
|
||||||
|
to_zone = tz.tzlocal()
|
||||||
|
|
||||||
# Extract the configuration settings.
|
# Extract the configuration settings.
|
||||||
header = 1
|
header = 1
|
||||||
configuration = dict()
|
configuration = dict()
|
||||||
body = ""
|
body = ""
|
||||||
for line in sys.stdin:
|
for line in input_stream:
|
||||||
if header:
|
if header:
|
||||||
if line == "\n":
|
if line == "\n":
|
||||||
header = 0
|
header = 0
|
||||||
else:
|
|
||||||
fields = line.strip().split(": ", 2)
|
|
||||||
if len(fields) == 2:
|
|
||||||
configuration[fields[0]] = fields[1]
|
|
||||||
else:
|
else:
|
||||||
configuration[fields[0]] = ""
|
fields = line.strip().split(": ", 2)
|
||||||
else:
|
if len(fields) == 2:
|
||||||
body += line
|
configuration[fields[0]] = fields[1]
|
||||||
|
else:
|
||||||
# Sum the second tracked by tag.
|
configuration[fields[0]] = ""
|
||||||
totals = dict()
|
|
||||||
untagged = None
|
|
||||||
|
|
||||||
from_zone = tz.tzutc()
|
|
||||||
to_zone = tz.tzlocal()
|
|
||||||
|
|
||||||
j = json.loads(body)
|
|
||||||
for object in j:
|
|
||||||
start = datetime.datetime.strptime(object["start"], DATEFORMAT)
|
|
||||||
|
|
||||||
if "end" in object:
|
|
||||||
end = datetime.datetime.strptime(object["end"], DATEFORMAT)
|
|
||||||
else:
|
|
||||||
end = datetime.datetime.utcnow()
|
|
||||||
|
|
||||||
tracked = end - start
|
|
||||||
|
|
||||||
if "tags" not in object or object["tags"] == []:
|
|
||||||
if untagged is None:
|
|
||||||
untagged = tracked
|
|
||||||
else:
|
else:
|
||||||
untagged += tracked
|
body += line
|
||||||
else:
|
|
||||||
for tag in object["tags"]:
|
# Sum the seconds tracked by tag.
|
||||||
if tag in totals:
|
totals = dict()
|
||||||
totals[tag] += tracked
|
untagged = None
|
||||||
|
j = json.loads(body)
|
||||||
|
for object in j:
|
||||||
|
start = datetime.datetime.strptime(object["start"], DATEFORMAT)
|
||||||
|
|
||||||
|
if "end" in object:
|
||||||
|
end = datetime.datetime.strptime(object["end"], DATEFORMAT)
|
||||||
|
else:
|
||||||
|
end = datetime.datetime.utcnow()
|
||||||
|
|
||||||
|
tracked = end - start
|
||||||
|
|
||||||
|
if "tags" not in object or object["tags"] == []:
|
||||||
|
if untagged is None:
|
||||||
|
untagged = tracked
|
||||||
else:
|
else:
|
||||||
totals[tag] = tracked
|
untagged += tracked
|
||||||
|
else:
|
||||||
|
for tag in object["tags"]:
|
||||||
|
if tag in totals:
|
||||||
|
totals[tag] += tracked
|
||||||
|
else:
|
||||||
|
totals[tag] = tracked
|
||||||
|
|
||||||
# Determine largest tag width.
|
# Determine largest tag width.
|
||||||
max_width = len("Total")
|
max_width = len("Total")
|
||||||
for tag in totals:
|
for tag in totals:
|
||||||
if len(tag) > max_width:
|
if len(tag) > max_width:
|
||||||
max_width = len(tag)
|
max_width = len(tag)
|
||||||
|
|
||||||
if "temp.report.start" not in configuration:
|
if "temp.report.start" not in configuration:
|
||||||
print("There is no data in the database")
|
return ["There is no data in the database"]
|
||||||
exit()
|
|
||||||
|
|
||||||
start_utc = datetime.datetime.strptime(configuration["temp.report.start"], DATEFORMAT)
|
start_utc = datetime.datetime.strptime(configuration["temp.report.start"], DATEFORMAT)
|
||||||
start_utc = start_utc.replace(tzinfo=from_zone)
|
start_utc = start_utc.replace(tzinfo=from_zone)
|
||||||
start = start_utc.astimezone(to_zone)
|
start = start_utc.astimezone(to_zone)
|
||||||
|
|
||||||
if "temp.report.end" in configuration:
|
if "temp.report.end" in configuration:
|
||||||
end_utc = datetime.datetime.strptime(configuration["temp.report.end"], DATEFORMAT)
|
end_utc = datetime.datetime.strptime(configuration["temp.report.end"], DATEFORMAT)
|
||||||
end_utc = end_utc.replace(tzinfo=from_zone)
|
end_utc = end_utc.replace(tzinfo=from_zone)
|
||||||
end = end_utc.astimezone(to_zone)
|
end = end_utc.astimezone(to_zone)
|
||||||
else:
|
else:
|
||||||
end = datetime.datetime.now()
|
end = datetime.datetime.now()
|
||||||
|
|
||||||
|
if len(totals) == 0 and untagged is None:
|
||||||
|
return ["No data in the range {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}".format(start, end)]
|
||||||
|
|
||||||
if max_width > 0:
|
|
||||||
# Compose report header.
|
# Compose report header.
|
||||||
print("\nTotal by Tag, for {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}\n".format(start, end))
|
output = [
|
||||||
|
"",
|
||||||
|
"Total by Tag, for {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}".format(start, end),
|
||||||
|
""
|
||||||
|
]
|
||||||
|
|
||||||
# Compose table header.
|
# Compose table header.
|
||||||
if configuration["color"] == "on":
|
if configuration["color"] == "on":
|
||||||
print("[4m{:{width}}[0m [4m{:>10}[0m".format("Tag", "Total", width=max_width))
|
output.append("[4m{:{width}}[0m [4m{:>10}[0m".format("Tag", "Total", width=max_width))
|
||||||
else:
|
else:
|
||||||
print("{:{width}} {:>10}".format("Tag", "Total", width=max_width))
|
output.append("{:{width}} {:>10}".format("Tag", "Total", width=max_width))
|
||||||
print("{} {}".format("-" * max_width, "----------"))
|
output.append("{} {}".format("-" * max_width, "----------"))
|
||||||
|
|
||||||
# Compose table rows.
|
# Compose table rows.
|
||||||
grand_total = 0
|
grand_total = 0
|
||||||
for tag in sorted(totals):
|
for tag in sorted(totals):
|
||||||
formatted = format_seconds(totals[tag].seconds)
|
formatted = format_seconds(totals[tag].seconds)
|
||||||
grand_total += totals[tag].seconds
|
grand_total += totals[tag].seconds
|
||||||
print("%-*s %10s" % (max_width, tag, formatted))
|
output.append("{:{width}} {:10}".format(tag, formatted, width=max_width))
|
||||||
|
|
||||||
if untagged is not None:
|
if untagged is not None:
|
||||||
formatted = format_seconds(untagged.seconds)
|
formatted = format_seconds(untagged.seconds)
|
||||||
grand_total += untagged.seconds
|
grand_total += untagged.seconds
|
||||||
print("%-*s %10s" % (max_width, "", formatted))
|
output.append("{:{width}} {:10}".format("", formatted, width=max_width))
|
||||||
|
|
||||||
# Compose total.
|
# Compose total.
|
||||||
if configuration["color"] == "on":
|
if configuration["color"] == "on":
|
||||||
print("{} {}".format(" " * max_width, "[4m [0m"))
|
output.append("{} {}".format(" " * max_width, "[4m [0m"))
|
||||||
else:
|
else:
|
||||||
print("{} {}".format(" " * max_width, "----------"))
|
output.append("{} {}".format(" " * max_width, "----------"))
|
||||||
|
|
||||||
print("%-*s %10s" % (max_width, "Total", format_seconds(grand_total)))
|
output.append("{:{width}} {:10}".format("Total", format_seconds(grand_total), width=max_width))
|
||||||
|
output.append("")
|
||||||
|
|
||||||
else:
|
return output
|
||||||
print("No data in the range {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}".format(start, end))
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
for line in calculate_totals(sys.stdin):
|
||||||
|
print(line)
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
@ -35,32 +34,27 @@ import datetime
|
||||||
|
|
||||||
# Ensure python finds the local simpletap module
|
# Ensure python finds the local simpletap module
|
||||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'ext'))
|
||||||
|
|
||||||
from basetest import TestCase
|
from basetest import TestCase
|
||||||
|
from totals import *
|
||||||
|
|
||||||
|
|
||||||
class TestTotals(TestCase):
|
class TestTotals(TestCase):
|
||||||
def setUp(self):
|
|
||||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
self.process = subprocess.Popen([os.path.join(current_dir, '../ext/totals.py')],
|
|
||||||
stdin=subprocess.PIPE,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE)
|
|
||||||
|
|
||||||
def test_totals_with_empty_database(self):
|
def test_totals_with_empty_database(self):
|
||||||
"""totals extension should report error on empty database"""
|
"""totals extension should report error on empty database"""
|
||||||
out, err = self.process.communicate(input="""\
|
input_stream = [
|
||||||
color: off
|
'color: off\n',
|
||||||
debug: off
|
'debug: on\n',
|
||||||
temp.report.start:
|
'temp.report.start: \n',
|
||||||
temp.report.end:
|
'temp.report.end: \n',
|
||||||
|
'\n',
|
||||||
|
'[]',
|
||||||
|
]
|
||||||
|
|
||||||
[
|
out = calculate_totals(input_stream)
|
||||||
]
|
|
||||||
""")
|
|
||||||
|
|
||||||
self.assertEqual('There is no data in the database\n', out)
|
self.assertEqual(['There is no data in the database'], out)
|
||||||
self.assertEqual('', err)
|
|
||||||
|
|
||||||
def test_totals_with_filled_database(self):
|
def test_totals_with_filled_database(self):
|
||||||
"""totals extension should print report for filled database"""
|
"""totals extension should print report for filled database"""
|
||||||
|
@ -70,27 +64,51 @@ temp.report.end:
|
||||||
now_utc = now.utcnow()
|
now_utc = now.utcnow()
|
||||||
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
out, err = self.process.communicate(input="""\
|
input_stream = [
|
||||||
color: off
|
'color: off\n',
|
||||||
debug: off
|
'debug: on\n',
|
||||||
temp.report.start: {0:%Y%m%dT%H%M%S}Z
|
'temp.report.start: {:%Y%m%dT%H%M%S}Z\n'.format(one_hour_before_utc),
|
||||||
temp.report.end: {1:%Y%m%dT%H%M%S}Z
|
'temp.report.end: {:%Y%m%dT%H%M%S}Z\n'.format(now_utc),
|
||||||
|
'\n',
|
||||||
|
'[{{"start":"{:%Y%m%dT%H%M%S}Z","end":"{:%Y%m%dT%H%M%S}Z","tags":["foo"]}}]'.format(one_hour_before_utc, now_utc)
|
||||||
|
]
|
||||||
|
|
||||||
[
|
out = calculate_totals(input_stream)
|
||||||
{{"start":"{0:%Y%m%dT%H%M%S}Z","end":"{1:%Y%m%dT%H%M%S}Z","tags":["foo"]}}
|
|
||||||
]
|
|
||||||
""".format(one_hour_before_utc, now_utc))
|
|
||||||
|
|
||||||
self.assertRegexpMatches(out, """
|
self.assertEqual(
|
||||||
Total by Tag, for {0:%Y-%m-%d %H:%M}:\d{{2}} - {1:%Y-%m-%d %H:%M}:\d{{2}}
|
[
|
||||||
|
'',
|
||||||
|
'Total by Tag, for {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}'.format(one_hour_before, now),
|
||||||
|
'',
|
||||||
|
'Tag Total',
|
||||||
|
'----- ----------',
|
||||||
|
'foo 1:00:00',
|
||||||
|
' ----------',
|
||||||
|
'Total 1:00:00',
|
||||||
|
'',
|
||||||
|
],
|
||||||
|
out)
|
||||||
|
|
||||||
Tag Total
|
def test_totals_with_emtpy_range(self):
|
||||||
----- ----------
|
"""totals extension should report error on emtpy range"""
|
||||||
foo 1:00:0[01]
|
now = datetime.datetime.now()
|
||||||
----------
|
one_hour_before = now - datetime.timedelta(hours=1)
|
||||||
Total 1:00:0[01]
|
|
||||||
""".format(one_hour_before, now))
|
now_utc = now.utcnow()
|
||||||
self.assertEqual('', err)
|
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
input_stream = [
|
||||||
|
'color: off\n',
|
||||||
|
'debug: on\n',
|
||||||
|
'temp.report.start: {:%Y%m%dT%H%M%S}Z\n'.format(one_hour_before_utc),
|
||||||
|
'temp.report.end: {:%Y%m%dT%H%M%S}Z\n'.format(now_utc),
|
||||||
|
'\n',
|
||||||
|
'[]',
|
||||||
|
]
|
||||||
|
|
||||||
|
out = calculate_totals(input_stream)
|
||||||
|
|
||||||
|
self.assertEqual(['No data in the range {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}'.format(one_hour_before, now)], out)
|
||||||
|
|
||||||
def test_totals_with_interval_without_tags(self):
|
def test_totals_with_interval_without_tags(self):
|
||||||
"""totals extension should handle interval without tags"""
|
"""totals extension should handle interval without tags"""
|
||||||
|
@ -100,27 +118,30 @@ Total 1:00:0[01]
|
||||||
now_utc = now.utcnow()
|
now_utc = now.utcnow()
|
||||||
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
out, err = self.process.communicate(input="""\
|
input_stream = [
|
||||||
color: off
|
'color: off\n',
|
||||||
debug: off
|
'debug: on\n',
|
||||||
temp.report.start: {0:%Y%m%dT%H%M%S}Z
|
'temp.report.start: {:%Y%m%dT%H%M%S}Z\n'.format(one_hour_before_utc),
|
||||||
temp.report.end: {1:%Y%m%dT%H%M%S}Z
|
'temp.report.end: {:%Y%m%dT%H%M%S}Z\n'.format(now_utc),
|
||||||
|
'\n',
|
||||||
|
'[{{"start":"{:%Y%m%dT%H%M%S}Z","end":"{:%Y%m%dT%H%M%S}Z"}}]'.format(one_hour_before_utc, now_utc)
|
||||||
|
]
|
||||||
|
|
||||||
[
|
out = calculate_totals(input_stream)
|
||||||
{{"start":"{0:%Y%m%dT%H%M%S}Z","end":"{1:%Y%m%dT%H%M%S}Z"}}
|
|
||||||
]
|
|
||||||
""".format(one_hour_before_utc, now_utc))
|
|
||||||
|
|
||||||
self.assertRegexpMatches(out, """
|
self.assertEqual(
|
||||||
Total by Tag, for {0:%Y-%m-%d %H:%M}:\d{{2}} - {1:%Y-%m-%d %H:%M}:\d{{2}}
|
[
|
||||||
|
'',
|
||||||
Tag Total
|
'Total by Tag, for {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}'.format(one_hour_before, now),
|
||||||
----- ----------
|
'',
|
||||||
1:00:0[01]
|
'Tag Total',
|
||||||
----------
|
'----- ----------',
|
||||||
Total 1:00:0[01]
|
' 1:00:00',
|
||||||
""".format(one_hour_before, now))
|
' ----------',
|
||||||
self.assertEqual('', err)
|
'Total 1:00:00',
|
||||||
|
'',
|
||||||
|
],
|
||||||
|
out)
|
||||||
|
|
||||||
def test_totals_with_interval_with_empty_tag_list(self):
|
def test_totals_with_interval_with_empty_tag_list(self):
|
||||||
"""totals extension should handle interval with empty tag list"""
|
"""totals extension should handle interval with empty tag list"""
|
||||||
|
@ -130,27 +151,30 @@ Total 1:00:0[01]
|
||||||
now_utc = now.utcnow()
|
now_utc = now.utcnow()
|
||||||
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
out, err = self.process.communicate(input="""\
|
input_stream = [
|
||||||
color: off
|
'color: off\n',
|
||||||
debug: off
|
'debug: on\n',
|
||||||
temp.report.start: {0:%Y%m%dT%H%M%S}Z
|
'temp.report.start: {:%Y%m%dT%H%M%S}Z\n'.format(one_hour_before_utc),
|
||||||
temp.report.end: {1:%Y%m%dT%H%M%S}Z
|
'temp.report.end: {:%Y%m%dT%H%M%S}Z\n'.format(now_utc),
|
||||||
|
'\n',
|
||||||
|
'[{{"start":"{:%Y%m%dT%H%M%S}Z","end":"{:%Y%m%dT%H%M%S}Z","tags":[]}}]'.format(one_hour_before_utc, now_utc)
|
||||||
|
]
|
||||||
|
|
||||||
[
|
out = calculate_totals(input_stream)
|
||||||
{{"start":"{0:%Y%m%dT%H%M%S}Z","end":"{1:%Y%m%dT%H%M%S}Z", "tags":[]}}
|
|
||||||
]
|
|
||||||
""".format(one_hour_before_utc, now_utc))
|
|
||||||
|
|
||||||
self.assertRegexpMatches(out, """
|
self.assertEqual(
|
||||||
Total by Tag, for {0:%Y-%m-%d %H:%M}:\d{{2}} - {1:%Y-%m-%d %H:%M}:\d{{2}}
|
[
|
||||||
|
'',
|
||||||
Tag Total
|
'Total by Tag, for {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}'.format(one_hour_before, now),
|
||||||
----- ----------
|
'',
|
||||||
1:00:0[01]
|
'Tag Total',
|
||||||
----------
|
'----- ----------',
|
||||||
Total 1:00:0[01]
|
' 1:00:00',
|
||||||
""".format(one_hour_before, now))
|
' ----------',
|
||||||
self.assertEqual('', err)
|
'Total 1:00:00',
|
||||||
|
'',
|
||||||
|
],
|
||||||
|
out)
|
||||||
|
|
||||||
def test_totals_with_open_interval(self):
|
def test_totals_with_open_interval(self):
|
||||||
"""totals extension should handle open interval"""
|
"""totals extension should handle open interval"""
|
||||||
|
@ -160,31 +184,215 @@ Total 1:00:0[01]
|
||||||
now_utc = now.utcnow()
|
now_utc = now.utcnow()
|
||||||
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
out, err = self.process.communicate(input="""\
|
input_stream = [
|
||||||
color: off
|
'color: off\n',
|
||||||
debug: off
|
'debug: off\n',
|
||||||
temp.report.start: {0:%Y%m%dT%H%M%S}Z
|
'temp.report.start: {:%Y%m%dT%H%M%S}Z\n'.format(one_hour_before_utc),
|
||||||
temp.report.end:
|
'temp.report.end: \n',
|
||||||
|
'\n',
|
||||||
|
'[{{"start":"{:%Y%m%dT%H%M%S}Z","tags":["foo"]}}]'.format(one_hour_before_utc),
|
||||||
|
]
|
||||||
|
|
||||||
[
|
out = calculate_totals(input_stream)
|
||||||
{{"start":"{0:%Y%m%dT%H%M%S}Z","tags":["foo"]}}
|
|
||||||
]
|
|
||||||
""".format(one_hour_before_utc))
|
|
||||||
|
|
||||||
self.assertRegexpMatches(out, """
|
self.assertEqual(
|
||||||
Total by Tag, for {0:%Y-%m-%d %H:%M}:\d{{2}} - {1:%Y-%m-%d %H:%M}:\d{{2}}
|
[
|
||||||
|
'',
|
||||||
|
'Total by Tag, for {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}'.format(one_hour_before, now),
|
||||||
|
'',
|
||||||
|
'Tag Total',
|
||||||
|
'----- ----------',
|
||||||
|
'foo 1:00:00',
|
||||||
|
' ----------',
|
||||||
|
'Total 1:00:00',
|
||||||
|
'',
|
||||||
|
],
|
||||||
|
out)
|
||||||
|
|
||||||
Tag Total
|
def test_totals_colored_with_empty_database(self):
|
||||||
----- ----------
|
"""totals extension should report error on empty database (colored)"""
|
||||||
foo 1:00:0[01]
|
input_stream = [
|
||||||
----------
|
'color: on\n',
|
||||||
Total 1:00:0[01]
|
'debug: on\n',
|
||||||
""".format(one_hour_before, now))
|
'temp.report.start: \n',
|
||||||
self.assertEqual('', err)
|
'temp.report.end: \n',
|
||||||
|
'\n',
|
||||||
|
'[]',
|
||||||
|
]
|
||||||
|
|
||||||
|
out = calculate_totals(input_stream)
|
||||||
|
|
||||||
|
self.assertEqual(['There is no data in the database'], out)
|
||||||
|
|
||||||
|
def test_totals_colored_with_filled_database(self):
|
||||||
|
"""totals extension should print report for filled database (colored)"""
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
one_hour_before = now - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
now_utc = now.utcnow()
|
||||||
|
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
input_stream = [
|
||||||
|
'color: on\n',
|
||||||
|
'debug: on\n',
|
||||||
|
'temp.report.start: {:%Y%m%dT%H%M%S}Z\n'.format(one_hour_before_utc),
|
||||||
|
'temp.report.end: {:%Y%m%dT%H%M%S}Z\n'.format(now_utc),
|
||||||
|
'\n',
|
||||||
|
'[{{"start":"{:%Y%m%dT%H%M%S}Z","end":"{:%Y%m%dT%H%M%S}Z","tags":["foo"]}}]'.format(one_hour_before_utc, now_utc)
|
||||||
|
]
|
||||||
|
|
||||||
|
out = calculate_totals(input_stream)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
'Total by Tag, for {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}'.format(one_hour_before, now),
|
||||||
|
'',
|
||||||
|
'[4mTag [0m [4m Total[0m',
|
||||||
|
'foo 1:00:00',
|
||||||
|
' [4m [0m',
|
||||||
|
'Total 1:00:00',
|
||||||
|
'',
|
||||||
|
],
|
||||||
|
out)
|
||||||
|
|
||||||
|
def test_totals_colored_with_emtpy_range(self):
|
||||||
|
"""totals extension should report error on emtpy range (colored)"""
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
one_hour_before = now - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
now_utc = now.utcnow()
|
||||||
|
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
input_stream = [
|
||||||
|
'color: on\n',
|
||||||
|
'debug: on\n',
|
||||||
|
'temp.report.start: {:%Y%m%dT%H%M%S}Z\n'.format(one_hour_before_utc),
|
||||||
|
'temp.report.end: {:%Y%m%dT%H%M%S}Z\n'.format(now_utc),
|
||||||
|
'\n',
|
||||||
|
'[]',
|
||||||
|
]
|
||||||
|
|
||||||
|
out = calculate_totals(input_stream)
|
||||||
|
|
||||||
|
self.assertEqual(['No data in the range {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}'.format(one_hour_before, now)], out)
|
||||||
|
|
||||||
|
def test_totals_colored_with_interval_without_tags(self):
|
||||||
|
"""totals extension should handle interval without tags (colored)"""
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
one_hour_before = now - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
now_utc = now.utcnow()
|
||||||
|
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
input_stream = [
|
||||||
|
'color: on\n',
|
||||||
|
'debug: on\n',
|
||||||
|
'temp.report.start: {:%Y%m%dT%H%M%S}Z\n'.format(one_hour_before_utc),
|
||||||
|
'temp.report.end: {:%Y%m%dT%H%M%S}Z\n'.format(now_utc),
|
||||||
|
'\n',
|
||||||
|
'[{{"start":"{:%Y%m%dT%H%M%S}Z","end":"{:%Y%m%dT%H%M%S}Z"}}]'.format(one_hour_before_utc, now_utc)
|
||||||
|
]
|
||||||
|
|
||||||
|
out = calculate_totals(input_stream)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
'Total by Tag, for {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}'.format(one_hour_before, now),
|
||||||
|
'',
|
||||||
|
'[4mTag [0m [4m Total[0m',
|
||||||
|
' 1:00:00',
|
||||||
|
' [4m [0m',
|
||||||
|
'Total 1:00:00',
|
||||||
|
'',
|
||||||
|
],
|
||||||
|
out)
|
||||||
|
|
||||||
|
def test_totals_colored_with_interval_with_empty_tag_list(self):
|
||||||
|
"""totals extension should handle interval with empty tag list (colored)"""
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
one_hour_before = now - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
now_utc = now.utcnow()
|
||||||
|
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
input_stream = [
|
||||||
|
'color: on\n',
|
||||||
|
'debug: on\n',
|
||||||
|
'temp.report.start: {:%Y%m%dT%H%M%S}Z\n'.format(one_hour_before_utc),
|
||||||
|
'temp.report.end: {:%Y%m%dT%H%M%S}Z\n'.format(now_utc),
|
||||||
|
'\n',
|
||||||
|
'[{"start":"20160101T070000Z","end":"20160101T080000Z","tags":[]}]',
|
||||||
|
]
|
||||||
|
|
||||||
|
out = calculate_totals(input_stream)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
'Total by Tag, for {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}'.format(one_hour_before, now),
|
||||||
|
'',
|
||||||
|
'[4mTag [0m [4m Total[0m',
|
||||||
|
' 1:00:00',
|
||||||
|
' [4m [0m',
|
||||||
|
'Total 1:00:00',
|
||||||
|
'',
|
||||||
|
],
|
||||||
|
out)
|
||||||
|
|
||||||
|
def test_totals_colored_with_open_interval(self):
|
||||||
|
"""totals extension should handle open interval (colored)"""
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
one_hour_before = now - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
now_utc = now.utcnow()
|
||||||
|
one_hour_before_utc = now_utc - datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
input_stream = [
|
||||||
|
'color: on\n',
|
||||||
|
'debug: off\n',
|
||||||
|
'temp.report.start: {:%Y%m%dT%H%M%S}Z\n'.format(one_hour_before_utc),
|
||||||
|
'temp.report.end: \n',
|
||||||
|
'\n',
|
||||||
|
'[{{"start":"{:%Y%m%dT%H%M%S}Z","tags":["foo"]}}]'.format(one_hour_before_utc),
|
||||||
|
]
|
||||||
|
|
||||||
|
out = calculate_totals(input_stream)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
'Total by Tag, for {:%Y-%m-%d %H:%M:%S} - {:%Y-%m-%d %H:%M:%S}'.format(one_hour_before, now),
|
||||||
|
'',
|
||||||
|
'[4mTag [0m [4m Total[0m',
|
||||||
|
'foo 1:00:00',
|
||||||
|
' [4m [0m',
|
||||||
|
'Total 1:00:00',
|
||||||
|
'',
|
||||||
|
],
|
||||||
|
out)
|
||||||
|
|
||||||
|
def test_format_seconds_with_less_than_1_minute(self):
|
||||||
|
"""Test format_seconds with less than 1 minute"""
|
||||||
|
self.assertEqual(format_seconds(34), ' 0:00:34')
|
||||||
|
|
||||||
|
def test_format_seconds_with_1_minute(self):
|
||||||
|
"""Test format_seconds with 1 minute"""
|
||||||
|
self.assertEqual(format_seconds(60), ' 0:01:00')
|
||||||
|
|
||||||
|
def test_format_seconds_with_1_hour(self):
|
||||||
|
"""Test format_seconds with 1 hour"""
|
||||||
|
self.assertEqual(format_seconds(3600), ' 1:00:00')
|
||||||
|
|
||||||
|
def test_format_seconds_with_more_than_1_hour(self):
|
||||||
|
"""Test format_seconds with more than 1 hour"""
|
||||||
|
self.assertEqual(format_seconds(3645), ' 1:00:45')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
from simpletap import TAPTestRunner
|
from simpletap import TAPTestRunner
|
||||||
|
|
||||||
unittest.main(testRunner=TAPTestRunner())
|
unittest.main(testRunner=TAPTestRunner())
|
||||||
|
|
||||||
# vim: ai sts=4 et sw=4 ft=python
|
# vim: ai sts=4 et sw=4 ft=python
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue