Merge pull request #24 from darko-poljak/fix-summary-option
Fix summary option
This commit is contained in:
commit
91cbda6c7b
10 changed files with 223 additions and 177 deletions
|
@ -36,11 +36,13 @@ MAIL = 'nico-ctt at schottelius.org'
|
||||||
WWW = 'http://www.nico.schottelius.org/software/ctt/'
|
WWW = 'http://www.nico.schottelius.org/software/ctt/'
|
||||||
# Name of the folder to create - should not contain special characters
|
# Name of the folder to create - should not contain special characters
|
||||||
# to ensure cross-os compatibility
|
# to ensure cross-os compatibility
|
||||||
DISKFORMAT = DATETIMEFORMAT
|
DISKFORMAT = DATETIMEFORMAT
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Our output format
|
# Our output format
|
||||||
def user_timedelta(seconds):
|
def user_timedelta(seconds):
|
||||||
"""Format timedelta for the user"""
|
"""Format timedelta for the user"""
|
||||||
|
@ -59,15 +61,17 @@ def user_timedelta(seconds):
|
||||||
|
|
||||||
return (hours, minutes, seconds)
|
return (hours, minutes, seconds)
|
||||||
|
|
||||||
|
|
||||||
def ctt_dir():
|
def ctt_dir():
|
||||||
home = os.environ['HOME']
|
home = os.environ['HOME']
|
||||||
ctt_dir = os.path.join(home, ".ctt")
|
ctt_dir = os.path.join(home, ".ctt")
|
||||||
|
|
||||||
return ctt_dir
|
return ctt_dir
|
||||||
|
|
||||||
|
|
||||||
def project_dir(project):
|
def project_dir(project):
|
||||||
project_dir = os.path.join(ctt_dir(), project)
|
project_dir = os.path.join(ctt_dir(), project)
|
||||||
|
|
||||||
return project_dir
|
return project_dir
|
||||||
|
|
||||||
return os.listdir(ctt_dir)
|
# return os.listdir(ctt_dir)
|
||||||
|
|
|
@ -26,6 +26,7 @@ import os
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ListProjects(object):
|
class ListProjects(object):
|
||||||
"""Return existing projects"""
|
"""Return existing projects"""
|
||||||
|
|
||||||
|
@ -33,7 +34,6 @@ class ListProjects(object):
|
||||||
def commandline(cls, args):
|
def commandline(cls, args):
|
||||||
cls.print_projects()
|
cls.print_projects()
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def print_projects(cls):
|
def print_projects(cls):
|
||||||
for project in cls.list_projects():
|
for project in cls.list_projects():
|
||||||
|
|
|
@ -21,16 +21,13 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
import calendar
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import time
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
import glob
|
import glob
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
|
@ -39,11 +36,12 @@ import ctt.listprojects
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Report(object):
|
class Report(object):
|
||||||
"""Create a report on tracked time"""
|
"""Create a report on tracked time"""
|
||||||
|
|
||||||
def __init__(self, project, start_date, end_date,
|
def __init__(self, project, start_date, end_date,
|
||||||
output_format, regexp, ignore_case):
|
output_format, regexp, ignore_case):
|
||||||
|
|
||||||
self.project = project
|
self.project = project
|
||||||
self.project_dir = ctt.project_dir(self.project)
|
self.project_dir = ctt.project_dir(self.project)
|
||||||
|
@ -63,7 +61,7 @@ class Report(object):
|
||||||
def commandline(cls, args):
|
def commandline(cls, args):
|
||||||
# Report time for all projects
|
# Report time for all projects
|
||||||
if args.all:
|
if args.all:
|
||||||
projects=ctt.listprojects.ListProjects.list_projects()
|
projects = ctt.listprojects.ListProjects.list_projects()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
projects = []
|
projects = []
|
||||||
|
@ -75,8 +73,8 @@ class Report(object):
|
||||||
reports = collections.OrderedDict()
|
reports = collections.OrderedDict()
|
||||||
for project in projects:
|
for project in projects:
|
||||||
report = cls(project=project, start_date=args.start,
|
report = cls(project=project, start_date=args.start,
|
||||||
end_date=args.end, output_format=args.output_format,
|
end_date=args.end, output_format=args.output_format,
|
||||||
regexp=args.regexp, ignore_case=args.ignore_case)
|
regexp=args.regexp, ignore_case=args.ignore_case)
|
||||||
report_data = report.report()
|
report_data = report.report()
|
||||||
reports[report.project] = (report, report_data)
|
reports[report.project] = (report, report_data)
|
||||||
total_time = total_time + report.total_time
|
total_time = total_time + report.total_time
|
||||||
|
@ -84,7 +82,6 @@ class Report(object):
|
||||||
|
|
||||||
cls.summary(total_time)
|
cls.summary(total_time)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def print_report_time_entries(report_data, output_format, summary):
|
def print_report_time_entries(report_data, output_format, summary):
|
||||||
''' Print time entries from report_data report using output_format.
|
''' Print time entries from report_data report using output_format.
|
||||||
|
@ -109,53 +106,58 @@ class Report(object):
|
||||||
report, report_data = reports[project]
|
report, report_data = reports[project]
|
||||||
if summary:
|
if summary:
|
||||||
for time in report_data:
|
for time in report_data:
|
||||||
if not time in summary_report:
|
if time not in summary_report:
|
||||||
summary_report[time] = report_data[time]
|
summary_report[time] = report_data[time]
|
||||||
else:
|
else:
|
||||||
summary_report[time].extend(report_data[time])
|
summary_report[time].extend(report_data[time])
|
||||||
else:
|
else:
|
||||||
report.header()
|
report.header()
|
||||||
Report.print_report_time_entries(report_data,
|
Report.print_report_time_entries(report_data,
|
||||||
output_format, summary)
|
output_format, summary)
|
||||||
if summary:
|
# For summary do not print time entries.
|
||||||
Report.print_report_time_entries(summary_report,
|
# if summary:
|
||||||
output_format, summary)
|
# Report.print_report_time_entries(summary_report,
|
||||||
|
# output_format, summary)
|
||||||
|
|
||||||
def _init_date(self, start_date, end_date):
|
def _init_date(self, start_date, end_date):
|
||||||
"""Setup date - either default or user given values"""
|
"""Setup date - either default or user given values"""
|
||||||
|
|
||||||
|
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
first_day_this_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
first_day_this_month = now.replace(
|
||||||
next_month = first_day_this_month.replace(day=28) + datetime.timedelta(days=4)
|
day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
next_month = first_day_this_month.replace(
|
||||||
|
day=28) + datetime.timedelta(days=4)
|
||||||
first_day_next_month = next_month.replace(day=1)
|
first_day_next_month = next_month.replace(day=1)
|
||||||
last_day_this_month = first_day_next_month - datetime.timedelta(seconds=1)
|
last_day_this_month = first_day_next_month - datetime.timedelta(
|
||||||
|
seconds=1)
|
||||||
|
|
||||||
default_start_date = first_day_this_month
|
default_start_date = first_day_this_month
|
||||||
default_end_date = last_day_this_month
|
default_end_date = last_day_this_month
|
||||||
|
|
||||||
#default_end_date = first_day - datetime.timedelta(days=1)
|
# default_end_date = first_day - datetime.timedelta(days=1)
|
||||||
#default_start_date = default_end_date.replace(day=1)
|
# default_start_date = default_end_date.replace(day=1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if start_date:
|
if start_date:
|
||||||
self.start_date = datetime.datetime.strptime(start_date[0], ctt.DATEFORMAT)
|
self.start_date = datetime.datetime.strptime(
|
||||||
|
start_date[0], ctt.DATEFORMAT)
|
||||||
else:
|
else:
|
||||||
self.start_date = default_start_date
|
self.start_date = default_start_date
|
||||||
|
|
||||||
if end_date:
|
if end_date:
|
||||||
self.end_date = datetime.datetime.strptime(end_date[0], ctt.DATEFORMAT)
|
self.end_date = datetime.datetime.strptime(
|
||||||
|
end_date[0], ctt.DATEFORMAT)
|
||||||
else:
|
else:
|
||||||
self.end_date = default_end_date
|
self.end_date = default_end_date
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise ctt.Error(e)
|
raise ctt.Error(e)
|
||||||
|
|
||||||
self.end_date = self.end_date.replace(hour=23,minute=59,second=59)
|
self.end_date = self.end_date.replace(
|
||||||
|
hour=23, minute=59, second=59)
|
||||||
|
|
||||||
if self.start_date >= self.end_date:
|
if self.start_date >= self.end_date:
|
||||||
raise ctt.Error("End date must be after start date (%s >= %s)" %
|
raise ctt.Error("End date must be after start date (%s >= %s)" %
|
||||||
(self.start_date, self.end_date))
|
(self.start_date, self.end_date))
|
||||||
|
|
||||||
def _init_report_db(self):
|
def _init_report_db(self):
|
||||||
"""Read all contents from db"""
|
"""Read all contents from db"""
|
||||||
|
@ -167,24 +169,31 @@ class Report(object):
|
||||||
for dirname in os.listdir(self.project_dir):
|
for dirname in os.listdir(self.project_dir):
|
||||||
log.debug("Dirname: %s" % dirname)
|
log.debug("Dirname: %s" % dirname)
|
||||||
try:
|
try:
|
||||||
dir_datetime = datetime.datetime.strptime(dirname, ctt.DISKFORMAT)
|
dir_datetime = datetime.datetime.strptime(
|
||||||
|
dirname, ctt.DISKFORMAT)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ctt.Error("Invalid time entry {entry} for project {project}, aborting!".format(entry=dirname, project=self.project))
|
raise ctt.Error(("Invalid time entry {entry} for project "
|
||||||
|
"{project}, aborting!").format(
|
||||||
|
entry=dirname, project=self.project))
|
||||||
|
|
||||||
if dir_datetime >= self.start_date and dir_datetime <= self.end_date:
|
if (dir_datetime >= self.start_date and
|
||||||
filename = os.path.join(self.project_dir, dirname, ctt.FILE_DELTA)
|
dir_datetime <= self.end_date):
|
||||||
comment_filename = os.path.join(self.project_dir, dirname, ctt.FILE_COMMENT)
|
filename = os.path.join(
|
||||||
|
self.project_dir, dirname, ctt.FILE_DELTA)
|
||||||
|
comment_filename = os.path.join(
|
||||||
|
self.project_dir, dirname, ctt.FILE_COMMENT)
|
||||||
|
|
||||||
# Check for matching comment
|
# Check for matching comment
|
||||||
comment = None
|
comment = None
|
||||||
if os.path.exists(comment_filename):
|
if os.path.exists(comment_filename):
|
||||||
with open(comment_filename, "r") as fd:
|
with open(comment_filename, "r") as fd:
|
||||||
comment = fd.read().rstrip('\n')
|
comment = fd.read().rstrip('\n')
|
||||||
|
|
||||||
# If regular expression given, but not matching, skip entry
|
|
||||||
if self.regexp and not re.search(self.regexp, comment, self.search_flags):
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
# If regular expression given, but not matching, skip entry
|
||||||
|
if (self.regexp and
|
||||||
|
not re.search(self.regexp, comment,
|
||||||
|
self.search_flags)):
|
||||||
|
continue
|
||||||
|
|
||||||
self._report_db[dirname] = {}
|
self._report_db[dirname] = {}
|
||||||
if comment:
|
if comment:
|
||||||
|
@ -193,7 +202,8 @@ class Report(object):
|
||||||
with open(filename, "r") as fd:
|
with open(filename, "r") as fd:
|
||||||
self._report_db[dirname]['delta'] = fd.read().rstrip('\n')
|
self._report_db[dirname]['delta'] = fd.read().rstrip('\n')
|
||||||
|
|
||||||
log.debug("Recording: %s: %s" % (dirname, self._report_db[dirname]['delta']))
|
log.debug("Recording: %s: %s"
|
||||||
|
% (dirname, self._report_db[dirname]['delta']))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
log.debug("Skipping: %s" % dirname)
|
log.debug("Skipping: %s" % dirname)
|
||||||
|
@ -201,14 +211,14 @@ class Report(object):
|
||||||
def header(self):
|
def header(self):
|
||||||
project_name = os.path.basename(self.project)
|
project_name = os.path.basename(self.project)
|
||||||
print("Report for %s between %s and %s" %
|
print("Report for %s between %s and %s" %
|
||||||
(project_name, self.start_date, self.end_date))
|
(project_name, self.start_date, self.end_date))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def summary(total_time):
|
def summary(total_time):
|
||||||
hours, minutes, seconds = ctt.user_timedelta(total_time)
|
hours, minutes, seconds = ctt.user_timedelta(total_time)
|
||||||
|
|
||||||
print("Total time tracked: %sh %sm %ss." %
|
print("Total time tracked: %sh %sm %ss." %
|
||||||
(hours, minutes, seconds))
|
(hours, minutes, seconds))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total_time(self):
|
def total_time(self):
|
||||||
|
@ -222,17 +232,16 @@ class Report(object):
|
||||||
|
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
|
||||||
def _get_report_entry(self, time, entry):
|
def _get_report_entry(self, time, entry):
|
||||||
''' Get one time entry data.
|
''' Get one time entry data.
|
||||||
'''
|
'''
|
||||||
report = {}
|
report = {}
|
||||||
start_datetime = datetime.datetime.strptime(time, ctt.DATETIMEFORMAT)
|
start_datetime = datetime.datetime.strptime(time, ctt.DATETIMEFORMAT)
|
||||||
delta = datetime.timedelta(seconds=int(float(entry['delta'])))
|
delta = datetime.timedelta(seconds=int(float(entry['delta'])))
|
||||||
end_datetime = (start_datetime + delta).replace(microsecond = 0)
|
end_datetime = (start_datetime + delta).replace(microsecond=0)
|
||||||
|
|
||||||
report['start_datetime'] = start_datetime.strftime(ctt.DATETIMEFORMAT)
|
report['start_datetime'] = start_datetime.strftime(ctt.DATETIMEFORMAT)
|
||||||
report['end_datetime'] = end_datetime.strftime(ctt.DATETIMEFORMAT)
|
report['end_datetime'] = end_datetime.strftime(ctt.DATETIMEFORMAT)
|
||||||
|
|
||||||
report['delta'] = delta
|
report['delta'] = delta
|
||||||
report['delta_seconds'] = int(float(entry['delta']))
|
report['delta_seconds'] = int(float(entry['delta']))
|
||||||
|
@ -244,7 +253,6 @@ class Report(object):
|
||||||
report['comment'] = False
|
report['comment'] = False
|
||||||
return report
|
return report
|
||||||
|
|
||||||
|
|
||||||
def report(self):
|
def report(self):
|
||||||
"""Return total time tracked"""
|
"""Return total time tracked"""
|
||||||
|
|
||||||
|
@ -253,7 +261,7 @@ class Report(object):
|
||||||
for time in time_keys:
|
for time in time_keys:
|
||||||
entry = self._report_db[time]
|
entry = self._report_db[time]
|
||||||
report = self._get_report_entry(time, entry)
|
report = self._get_report_entry(time, entry)
|
||||||
if not time in entries:
|
if time not in entries:
|
||||||
entries[time] = [report]
|
entries[time] = [report]
|
||||||
else:
|
else:
|
||||||
entries[time].append(report)
|
entries[time].append(report)
|
||||||
|
|
|
@ -22,7 +22,9 @@
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
fixtures_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "fixtures"))
|
fixtures_dir = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||||
|
"fixtures"))
|
||||||
|
|
||||||
|
|
||||||
class CttTestCase(unittest.TestCase):
|
class CttTestCase(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -25,12 +25,13 @@ import ctt
|
||||||
import ctt.listprojects as cttls
|
import ctt.listprojects as cttls
|
||||||
import ctt.test
|
import ctt.test
|
||||||
|
|
||||||
|
|
||||||
class ListProjectsTestCase(ctt.test.CttTestCase):
|
class ListProjectsTestCase(ctt.test.CttTestCase):
|
||||||
|
|
||||||
def test_list_projects(self):
|
def test_list_projects(self):
|
||||||
projects = cttls.ListProjects.list_projects()
|
projects = cttls.ListProjects.list_projects()
|
||||||
expected_projects = [ 'foo1', 'foo2', 'foo3', 'spam-eggs',
|
expected_projects = ['foo1', 'foo2', 'foo3', 'spam-eggs',
|
||||||
'test-1', 'test-2', 'test-3', ]
|
'test-1', 'test-2', 'test-3', ]
|
||||||
gotten_projects = sorted(projects)
|
gotten_projects = sorted(projects)
|
||||||
self.assertEqual(gotten_projects, expected_projects)
|
self.assertEqual(gotten_projects, expected_projects)
|
||||||
|
|
||||||
|
|
|
@ -51,17 +51,17 @@ class ReportTestCase(ctt.test.CttTestCase):
|
||||||
report_data = {
|
report_data = {
|
||||||
'2016-04-07-0826': [
|
'2016-04-07-0826': [
|
||||||
{
|
{
|
||||||
'start_datetime': '2016-04-07-0826',
|
'start_datetime': '2016-04-07-0826',
|
||||||
'end_datetime': '2016-04-07-2359',
|
'end_datetime': '2016-04-07-2359',
|
||||||
'comment': 'foo1',
|
'comment': 'foo1',
|
||||||
'delta': '6',
|
'delta': '6',
|
||||||
'delta_seconds': '6',
|
'delta_seconds': '6',
|
||||||
'delta_minutes': '0',
|
'delta_minutes': '0',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
report.Report.print_report_time_entries(report_data, ctt.REPORTFORMAT,
|
report.Report.print_report_time_entries(report_data, ctt.REPORTFORMAT,
|
||||||
False)
|
False)
|
||||||
output = self._get_output()
|
output = self._get_output()
|
||||||
expected_output = "2016-04-07-0826 (6): foo1"
|
expected_output = "2016-04-07-0826 (6): foo1"
|
||||||
self.assertEqual(output, expected_output)
|
self.assertEqual(output, expected_output)
|
||||||
|
@ -70,70 +70,72 @@ class ReportTestCase(ctt.test.CttTestCase):
|
||||||
report_data = {
|
report_data = {
|
||||||
'2016-04-07-0826': [
|
'2016-04-07-0826': [
|
||||||
{
|
{
|
||||||
'start_datetime': '2016-04-07-0826',
|
'start_datetime': '2016-04-07-0826',
|
||||||
'end_datetime': '2016-04-07-2359',
|
'end_datetime': '2016-04-07-2359',
|
||||||
'comment': 'foo1',
|
'comment': 'foo1',
|
||||||
'delta': '6',
|
'delta': '6',
|
||||||
'delta_seconds': '6',
|
'delta_seconds': '6',
|
||||||
'delta_minutes': '0',
|
'delta_minutes': '0',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'2016-04-07-0926': [
|
'2016-04-07-0926': [
|
||||||
{
|
{
|
||||||
'start_datetime': '2016-04-07-0926',
|
'start_datetime': '2016-04-07-0926',
|
||||||
'end_datetime': '2016-04-07-2359',
|
'end_datetime': '2016-04-07-2359',
|
||||||
'comment': 'foo12',
|
'comment': 'foo12',
|
||||||
'delta': '10',
|
'delta': '10',
|
||||||
'delta_seconds': '10',
|
'delta_seconds': '10',
|
||||||
'delta_minutes': '0',
|
'delta_minutes': '0',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
report.Report.print_report_time_entries(report_data, ctt.REPORTFORMAT,
|
report.Report.print_report_time_entries(report_data, ctt.REPORTFORMAT,
|
||||||
True)
|
True)
|
||||||
output = self._get_output()
|
output = self._get_output()
|
||||||
expected_output = ("2016-04-07-0826 (6): foo1\n"
|
expected_output = ("2016-04-07-0826 (6): foo1\n"
|
||||||
"2016-04-07-0926 (10): foo12")
|
"2016-04-07-0926 (10): foo12")
|
||||||
self.assertEqual(output, expected_output)
|
self.assertEqual(output, expected_output)
|
||||||
|
|
||||||
def test_print_reports(self):
|
def test_print_reports(self):
|
||||||
reports = collections.OrderedDict()
|
reports = collections.OrderedDict()
|
||||||
for project in ('foo1', 'foo2'):
|
for project in ('foo1', 'foo2'):
|
||||||
rep = report.Report(project, ('2016-04-07',), ('2016-04-08',),
|
rep = report.Report(project, ('2016-04-07', ), ('2016-04-08', ),
|
||||||
ctt.REPORTFORMAT, None, None)
|
ctt.REPORTFORMAT, None, None)
|
||||||
report_data = rep.report()
|
report_data = rep.report()
|
||||||
reports[project] = (rep, report_data)
|
reports[project] = (rep, report_data)
|
||||||
expected_output = (
|
expected_output = (
|
||||||
"Report for foo1 between 2016-04-07 00:00:00 and 2016-04-08 23:59:59\n"
|
"Report for foo1 between 2016-04-07 00:00:00 and "
|
||||||
|
"2016-04-08 23:59:59\n"
|
||||||
"2016-04-07-0826 (0:00:06): foo1\n"
|
"2016-04-07-0826 (0:00:06): foo1\n"
|
||||||
"2016-04-08-1200 (1:23:20): foo1 12\n"
|
"2016-04-08-1200 (1:23:20): foo1 12\n"
|
||||||
"Report for foo2 between 2016-04-07 00:00:00 and 2016-04-08 23:59:59\n"
|
"Report for foo2 between 2016-04-07 00:00:00 and "
|
||||||
|
"2016-04-08 23:59:59\n"
|
||||||
"2016-04-07-0810 (0:00:10): foo2"
|
"2016-04-07-0810 (0:00:10): foo2"
|
||||||
)
|
)
|
||||||
rep.print_reports(reports, ctt.REPORTFORMAT, summary=False)
|
rep.print_reports(reports, ctt.REPORTFORMAT, summary=False)
|
||||||
output = self._get_output()
|
output = self._get_output()
|
||||||
self.assertEqual(output, expected_output)
|
self.assertEqual(output, expected_output)
|
||||||
|
|
||||||
def test_print_reports_summary(self):
|
# Summary should not print time entries
|
||||||
reports = collections.OrderedDict()
|
# def test_print_reports_summary(self):
|
||||||
for project in ('foo1', 'foo2'):
|
# reports = collections.OrderedDict()
|
||||||
rep = report.Report(project, ('2016-04-07',), ('2016-04-08',),
|
# for project in ('foo1', 'foo2'):
|
||||||
ctt.REPORTFORMAT, None, None)
|
# rep = report.Report(project, ('2016-04-07',), ('2016-04-08',),
|
||||||
report_data = rep.report()
|
# ctt.REPORTFORMAT, None, None)
|
||||||
reports[project] = (rep, report_data)
|
# report_data = rep.report()
|
||||||
expected_output = (
|
# reports[project] = (rep, report_data)
|
||||||
"2016-04-07-0810 (0:00:10): foo2\n"
|
# expected_output = (
|
||||||
"2016-04-07-0826 (0:00:06): foo1\n"
|
# "2016-04-07-0810 (0:00:10): foo2\n"
|
||||||
"2016-04-08-1200 (1:23:20): foo1 12"
|
# "2016-04-07-0826 (0:00:06): foo1\n"
|
||||||
)
|
# "2016-04-08-1200 (1:23:20): foo1 12"
|
||||||
rep.print_reports(reports, ctt.REPORTFORMAT, summary=True)
|
# )
|
||||||
output = self._get_output()
|
# rep.print_reports(reports, ctt.REPORTFORMAT, summary=True)
|
||||||
self.assertEqual(output, expected_output)
|
# output = self._get_output()
|
||||||
|
# self.assertEqual(output, expected_output)
|
||||||
|
|
||||||
def test__init_date(self):
|
def test__init_date(self):
|
||||||
rep = report.Report('foo1', ('2016-04-07',), ('2016-04-07',),
|
rep = report.Report('foo1', ('2016-04-07', ), ('2016-04-07', ),
|
||||||
ctt.REPORTFORMAT, None, None)
|
ctt.REPORTFORMAT, None, None)
|
||||||
expected_start_date = datetime.datetime(2016, 4, 7)
|
expected_start_date = datetime.datetime(2016, 4, 7)
|
||||||
expected_end_date = datetime.datetime(2016, 4, 7, 23, 59, 59)
|
expected_end_date = datetime.datetime(2016, 4, 7, 23, 59, 59)
|
||||||
self.assertEqual(rep.start_date, expected_start_date)
|
self.assertEqual(rep.start_date, expected_start_date)
|
||||||
|
@ -141,44 +143,46 @@ class ReportTestCase(ctt.test.CttTestCase):
|
||||||
|
|
||||||
@unittest.expectedFailure
|
@unittest.expectedFailure
|
||||||
def test__init_date_fail(self):
|
def test__init_date_fail(self):
|
||||||
rep = report.Report('foo1', ('2016-04-08',), ('2016-04-07',),
|
report.Report('foo1', ('2016-04-08', ), ('2016-04-07', ),
|
||||||
ctt.REPORTFORMAT, None, None)
|
ctt.REPORTFORMAT, None, None)
|
||||||
|
|
||||||
def test__init_date_defaults(self):
|
def test__init_date_defaults(self):
|
||||||
rep = report.Report('foo1', None, None,
|
rep = report.Report('foo1', None, None,
|
||||||
ctt.REPORTFORMAT, None, None)
|
ctt.REPORTFORMAT, None, None)
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
expected_start_date = now.replace(day=1, hour=0, minute=0, second=0,
|
expected_start_date = now.replace(day=1, hour=0, minute=0, second=0,
|
||||||
microsecond=0)
|
microsecond=0)
|
||||||
next_month = expected_start_date.replace(day=28) + datetime.timedelta(days=4)
|
next_month = expected_start_date.replace(day=28) + datetime.timedelta(
|
||||||
|
days=4)
|
||||||
first_day_next_month = next_month.replace(day=1)
|
first_day_next_month = next_month.replace(day=1)
|
||||||
expected_end_date = first_day_next_month - datetime.timedelta(seconds=1)
|
expected_end_date = first_day_next_month - datetime.timedelta(
|
||||||
|
seconds=1)
|
||||||
self.assertEqual(rep.start_date, expected_start_date)
|
self.assertEqual(rep.start_date, expected_start_date)
|
||||||
self.assertEqual(rep.end_date, expected_end_date)
|
self.assertEqual(rep.end_date, expected_end_date)
|
||||||
|
|
||||||
@unittest.expectedFailure
|
@unittest.expectedFailure
|
||||||
def test__init_report_db_fail(self):
|
def test__init_report_db_fail(self):
|
||||||
rep = report.Report('unexisting', ('2016-04-07',), ('2016-04-07',),
|
report.Report('unexisting', ('2016-04-07',), ('2016-04-07',),
|
||||||
ctt.REPORTFORMAT, None, None)
|
ctt.REPORTFORMAT, None, None)
|
||||||
|
|
||||||
def test__init_report_db(self):
|
def test__init_report_db(self):
|
||||||
rep = report.Report('foo1', ('2016-04-07',), ('2016-04-07',),
|
rep = report.Report('foo1', ('2016-04-07',), ('2016-04-07',),
|
||||||
ctt.REPORTFORMAT, None, None)
|
ctt.REPORTFORMAT, None, None)
|
||||||
expected_db = {
|
expected_db = {
|
||||||
'2016-04-07-0826': {
|
'2016-04-07-0826': {
|
||||||
'comment': 'foo1',
|
'comment': 'foo1',
|
||||||
'delta': '6.248274'
|
'delta': '6.248274'
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
self.assertEqual(rep._report_db, expected_db)
|
self.assertEqual(rep._report_db, expected_db)
|
||||||
|
|
||||||
def test_header(self):
|
def test_header(self):
|
||||||
rep = report.Report('foo1', ('2016-04-07',), ('2016-04-07',),
|
rep = report.Report('foo1', ('2016-04-07',), ('2016-04-07',),
|
||||||
ctt.REPORTFORMAT, None, None)
|
ctt.REPORTFORMAT, None, None)
|
||||||
rep.header()
|
rep.header()
|
||||||
output = self._get_output()
|
output = self._get_output()
|
||||||
self.assertEqual(output, ("Report for foo1 between 2016-04-07 00:00:00"
|
self.assertEqual(output, ("Report for foo1 between 2016-04-07 00:00:00"
|
||||||
" and 2016-04-07 23:59:59"))
|
" and 2016-04-07 23:59:59"))
|
||||||
|
|
||||||
def test_summary(self):
|
def test_summary(self):
|
||||||
report.Report.summary(10)
|
report.Report.summary(10)
|
||||||
|
@ -186,36 +190,36 @@ class ReportTestCase(ctt.test.CttTestCase):
|
||||||
self.assertEqual(output, "Total time tracked: 0h 0m 10s.")
|
self.assertEqual(output, "Total time tracked: 0h 0m 10s.")
|
||||||
|
|
||||||
def test_total_time(self):
|
def test_total_time(self):
|
||||||
rep = report.Report('foo1', ('2016-04-07',), ('2016-04-07',),
|
rep = report.Report('foo1', ('2016-04-07',), ('2016-04-07',),
|
||||||
ctt.REPORTFORMAT, None, None)
|
ctt.REPORTFORMAT, None, None)
|
||||||
total_time = rep.total_time
|
total_time = rep.total_time
|
||||||
expected_total_time = 6.248274
|
expected_total_time = 6.248274
|
||||||
self.assertEqual(total_time, expected_total_time)
|
self.assertEqual(total_time, expected_total_time)
|
||||||
|
|
||||||
def test_report(self):
|
def test_report(self):
|
||||||
rep = report.Report('foo1', ('2016-04-07',), ('2016-04-08',),
|
rep = report.Report('foo1', ('2016-04-07',), ('2016-04-08',),
|
||||||
ctt.REPORTFORMAT, None, None)
|
ctt.REPORTFORMAT, None, None)
|
||||||
expected_entries = {
|
expected_entries = {
|
||||||
'2016-04-07-0826': [
|
'2016-04-07-0826': [
|
||||||
{
|
{
|
||||||
'start_datetime': '2016-04-07-0826',
|
'start_datetime': '2016-04-07-0826',
|
||||||
'end_datetime': '2016-04-07-0826',
|
'end_datetime': '2016-04-07-0826',
|
||||||
'comment': 'foo1',
|
'comment': 'foo1',
|
||||||
'delta': datetime.timedelta(seconds=6),
|
'delta': datetime.timedelta(seconds=6),
|
||||||
'delta_seconds': 6,
|
'delta_seconds': 6,
|
||||||
'delta_minutes': 0,
|
'delta_minutes': 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'2016-04-08-1200': [
|
'2016-04-08-1200': [
|
||||||
{
|
{
|
||||||
'start_datetime': '2016-04-08-1200',
|
'start_datetime': '2016-04-08-1200',
|
||||||
'end_datetime': '2016-04-08-1323',
|
'end_datetime': '2016-04-08-1323',
|
||||||
'comment': 'foo1 12',
|
'comment': 'foo1 12',
|
||||||
'delta': datetime.timedelta(seconds=5000),
|
'delta': datetime.timedelta(seconds=5000),
|
||||||
'delta_seconds': 5000,
|
'delta_seconds': 5000,
|
||||||
'delta_minutes': 83,
|
'delta_minutes': 83,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
entries = rep.report()
|
entries = rep.report()
|
||||||
self.assertEqual(entries, expected_entries)
|
self.assertEqual(entries, expected_entries)
|
||||||
|
|
|
@ -28,6 +28,7 @@ import os
|
||||||
import datetime
|
import datetime
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
class TrackerTestCase(ctt.test.CttTestCase):
|
class TrackerTestCase(ctt.test.CttTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -43,7 +44,7 @@ class TrackerTestCase(ctt.test.CttTestCase):
|
||||||
def test___init__(self):
|
def test___init__(self):
|
||||||
project = 'foo1'
|
project = 'foo1'
|
||||||
expected_project_dir = os.path.join(ctt.test.fixtures_dir,
|
expected_project_dir = os.path.join(ctt.test.fixtures_dir,
|
||||||
os.path.join('.ctt', project))
|
os.path.join('.ctt', project))
|
||||||
tracker = tr.Tracker(project)
|
tracker = tr.Tracker(project)
|
||||||
self.assertEqual(tracker.project, project)
|
self.assertEqual(tracker.project, project)
|
||||||
self.assertEqual(tracker.project_dir, expected_project_dir)
|
self.assertEqual(tracker.project_dir, expected_project_dir)
|
||||||
|
@ -52,31 +53,31 @@ class TrackerTestCase(ctt.test.CttTestCase):
|
||||||
self.assertIsNone(tracker.comment)
|
self.assertIsNone(tracker.comment)
|
||||||
self.assertFalse(tracker._tracked_time)
|
self.assertFalse(tracker._tracked_time)
|
||||||
|
|
||||||
tracker = tr.Tracker(project, start_datetime=('2016-04-09-0900',))
|
tracker = tr.Tracker(project, start_datetime=('2016-04-09-0900', ))
|
||||||
self.assertEqual(tracker.start_datetime,
|
self.assertEqual(tracker.start_datetime,
|
||||||
datetime.datetime(2016, 4, 9, 9, 0))
|
datetime.datetime(2016, 4, 9, 9, 0))
|
||||||
self.assertIsNone(tracker.end_datetime)
|
self.assertIsNone(tracker.end_datetime)
|
||||||
self.assertFalse(tracker._tracked_time)
|
self.assertFalse(tracker._tracked_time)
|
||||||
|
|
||||||
tracker = tr.Tracker(project, start_datetime=('2016-04-04-0900',),
|
tracker = tr.Tracker(project, start_datetime=('2016-04-04-0900', ),
|
||||||
end_datetime=('2016-04-09-2000',))
|
end_datetime=('2016-04-09-2000',))
|
||||||
self.assertEqual(tracker.start_datetime,
|
self.assertEqual(tracker.start_datetime,
|
||||||
datetime.datetime(2016, 4, 4, 9, 0))
|
datetime.datetime(2016, 4, 4, 9, 0))
|
||||||
self.assertEqual(tracker.end_datetime,
|
self.assertEqual(tracker.end_datetime,
|
||||||
datetime.datetime(2016, 4, 9, 20, 0))
|
datetime.datetime(2016, 4, 9, 20, 0))
|
||||||
self.assertTrue(tracker._tracked_time)
|
self.assertTrue(tracker._tracked_time)
|
||||||
|
|
||||||
@unittest.expectedFailure
|
@unittest.expectedFailure
|
||||||
def test__init__fail(self):
|
def test__init__fail(self):
|
||||||
project = 'foo1'
|
project = 'foo1'
|
||||||
tracker = tr.Tracker(project, start_datetime=('2016-04-090900',))
|
tr.Tracker(project, start_datetime=('2016-04-090900', ))
|
||||||
|
|
||||||
def test_delta(self):
|
def test_delta(self):
|
||||||
project = 'foo1'
|
project = 'foo1'
|
||||||
start_dt = datetime.datetime(2016, 4, 4, 9, 0)
|
start_dt = datetime.datetime(2016, 4, 4, 9, 0)
|
||||||
end_dt = datetime.datetime(2016, 4, 9, 20, 0)
|
end_dt = datetime.datetime(2016, 4, 9, 20, 0)
|
||||||
tracker = tr.Tracker(project, start_datetime=('2016-04-04-0900',),
|
tracker = tr.Tracker(project, start_datetime=('2016-04-04-0900', ),
|
||||||
end_datetime=('2016-04-09-2000',))
|
end_datetime=('2016-04-09-2000', ))
|
||||||
expected_delta = end_dt - start_dt
|
expected_delta = end_dt - start_dt
|
||||||
tracker._tracked_time = True
|
tracker._tracked_time = True
|
||||||
delta = tracker.delta(True)
|
delta = tracker.delta(True)
|
||||||
|
@ -93,7 +94,7 @@ class TrackerTestCase(ctt.test.CttTestCase):
|
||||||
project = 'foo1'
|
project = 'foo1'
|
||||||
start_dt = '2016-04-09-1730'
|
start_dt = '2016-04-09-1730'
|
||||||
tracker = tr.Tracker(project, start_datetime=(start_dt,),
|
tracker = tr.Tracker(project, start_datetime=(start_dt,),
|
||||||
comment=True)
|
comment=True)
|
||||||
end_dt = datetime.datetime(2016, 4, 9, hour=17, minute=45)
|
end_dt = datetime.datetime(2016, 4, 9, hour=17, minute=45)
|
||||||
expected_delta = str(15 * 60) + '.0\n' # seconds
|
expected_delta = str(15 * 60) + '.0\n' # seconds
|
||||||
tracker.end_datetime = end_dt
|
tracker.end_datetime = end_dt
|
||||||
|
@ -102,7 +103,7 @@ class TrackerTestCase(ctt.test.CttTestCase):
|
||||||
tracker.comment = expected_comment
|
tracker.comment = expected_comment
|
||||||
expected_comment += "\n"
|
expected_comment += "\n"
|
||||||
timedir = os.path.join(ctt.test.fixtures_dir, '.ctt', project,
|
timedir = os.path.join(ctt.test.fixtures_dir, '.ctt', project,
|
||||||
'2016-04-09-1730')
|
'2016-04-09-1730')
|
||||||
self.rm_dirs.append(timedir)
|
self.rm_dirs.append(timedir)
|
||||||
if os.path.exists(timedir):
|
if os.path.exists(timedir):
|
||||||
shutil.rmtree(timedir)
|
shutil.rmtree(timedir)
|
||||||
|
@ -124,15 +125,15 @@ class TrackerTestCase(ctt.test.CttTestCase):
|
||||||
project = 'foo1'
|
project = 'foo1'
|
||||||
start_dt = '2016-04-09-1730'
|
start_dt = '2016-04-09-1730'
|
||||||
tracker = tr.Tracker(project, start_datetime=(start_dt,),
|
tracker = tr.Tracker(project, start_datetime=(start_dt,),
|
||||||
comment=True)
|
comment=True)
|
||||||
end_dt = datetime.datetime(2016, 4, 9, hour=17, minute=45)
|
end_dt = datetime.datetime(2016, 4, 9, hour=17, minute=45)
|
||||||
expected_delta = 15 * 60 # seconds
|
# expected_delta = 15 * 60 # seconds
|
||||||
tracker.end_datetime = end_dt
|
tracker.end_datetime = end_dt
|
||||||
tracker._tracked_time = True
|
tracker._tracked_time = True
|
||||||
expected_comment = "test"
|
expected_comment = "test"
|
||||||
tracker.comment = expected_comment
|
tracker.comment = expected_comment
|
||||||
timedir = os.path.join(ctt.test.fixtures_dir, '.ctt', project,
|
timedir = os.path.join(ctt.test.fixtures_dir, '.ctt', project,
|
||||||
'2016-04-09-1730')
|
'2016-04-09-1730')
|
||||||
self.rm_dirs.append(timedir)
|
self.rm_dirs.append(timedir)
|
||||||
if os.path.exists(timedir):
|
if os.path.exists(timedir):
|
||||||
shutil.rmtree(timedir)
|
shutil.rmtree(timedir)
|
||||||
|
|
|
@ -22,17 +22,17 @@
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import time
|
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
|
||||||
|
|
||||||
import ctt
|
import ctt
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Tracker:
|
class Tracker:
|
||||||
def __init__(self, project, start_datetime = None, end_datetime = None, comment = True):
|
def __init__(self, project, start_datetime=None, end_datetime=None,
|
||||||
|
comment=True):
|
||||||
self.project = project
|
self.project = project
|
||||||
self.project_dir = ctt.project_dir(project)
|
self.project_dir = ctt.project_dir(project)
|
||||||
|
|
||||||
|
@ -42,12 +42,14 @@ class Tracker:
|
||||||
# Setup default values
|
# Setup default values
|
||||||
try:
|
try:
|
||||||
if start_datetime:
|
if start_datetime:
|
||||||
self.start_datetime = datetime.datetime.strptime(start_datetime[0], ctt.DATETIMEFORMAT)
|
self.start_datetime = datetime.datetime.strptime(
|
||||||
|
start_datetime[0], ctt.DATETIMEFORMAT)
|
||||||
else:
|
else:
|
||||||
self.start_datetime = None
|
self.start_datetime = None
|
||||||
|
|
||||||
if end_datetime:
|
if end_datetime:
|
||||||
self.end_datetime = datetime.datetime.strptime(end_datetime[0], ctt.DATETIMEFORMAT)
|
self.end_datetime = datetime.datetime.strptime(
|
||||||
|
end_datetime[0], ctt.DATETIMEFORMAT)
|
||||||
else:
|
else:
|
||||||
self.end_datetime = None
|
self.end_datetime = None
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
|
@ -56,7 +58,6 @@ class Tracker:
|
||||||
if self.start_datetime and self.end_datetime:
|
if self.start_datetime and self.end_datetime:
|
||||||
self._tracked_time = True
|
self._tracked_time = True
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def commandline(cls, args):
|
def commandline(cls, args):
|
||||||
tracker = cls(args.project[0], args.start, args.end, args.comment)
|
tracker = cls(args.project[0], args.start, args.end, args.comment)
|
||||||
|
@ -100,13 +101,14 @@ class Tracker:
|
||||||
|
|
||||||
if self.start_datetime >= self.end_datetime:
|
if self.start_datetime >= self.end_datetime:
|
||||||
raise ctt.Error("End date must be after start date! (%s > %s)!" %
|
raise ctt.Error("End date must be after start date! (%s > %s)!" %
|
||||||
(self.start_datetime, self.end_datetime))
|
(self.start_datetime, self.end_datetime))
|
||||||
|
|
||||||
subdirname = self.start_datetime.strftime(ctt.DISKFORMAT)
|
subdirname = self.start_datetime.strftime(ctt.DISKFORMAT)
|
||||||
time_dir = os.path.join(self.project_dir, subdirname)
|
time_dir = os.path.join(self.project_dir, subdirname)
|
||||||
|
|
||||||
if os.path.exists(time_dir):
|
if os.path.exists(time_dir):
|
||||||
raise ctt.Error("Already tracked time at this beginning for this project")
|
raise ctt.Error(
|
||||||
|
"Already tracked time at this beginning for this project")
|
||||||
|
|
||||||
os.makedirs(time_dir, mode=0o700)
|
os.makedirs(time_dir, mode=0o700)
|
||||||
|
|
||||||
|
|
75
scripts/ctt
75
scripts/ctt
|
@ -23,65 +23,89 @@
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Setup locale for calendar printing
|
# Setup locale for calendar printing
|
||||||
# Setup locale to get Timezone information?
|
# Setup locale to get Timezone information?
|
||||||
#print(locale.getlocale())
|
# print(locale.getlocale())
|
||||||
|
|
||||||
# Record tags
|
# Record tags
|
||||||
|
|
||||||
|
|
||||||
def parse_argv(argv, version):
|
def parse_argv(argv, version):
|
||||||
parser = {}
|
parser = {}
|
||||||
parser['loglevel'] = argparse.ArgumentParser(add_help=False)
|
parser['loglevel'] = argparse.ArgumentParser(add_help=False)
|
||||||
parser['loglevel'].add_argument('-d', '--debug',
|
parser['loglevel'].add_argument(
|
||||||
help='set log level to debug', action='store_true',
|
'-d', '--debug', help='set log level to debug', action='store_true',
|
||||||
default=False)
|
default=False)
|
||||||
parser['loglevel'].add_argument('-v', '--verbose',
|
parser['loglevel'].add_argument(
|
||||||
|
'-v', '--verbose',
|
||||||
help='set log level to info, be more verbose',
|
help='set log level to info, be more verbose',
|
||||||
action='store_true', default=False)
|
action='store_true', default=False)
|
||||||
|
|
||||||
parser['main'] = argparse.ArgumentParser(description='ctt ' + version,
|
parser['main'] = argparse.ArgumentParser(description='ctt ' + version,
|
||||||
parents=[parser['loglevel']])
|
parents=[parser['loglevel']])
|
||||||
parser['sub'] = parser['main'].add_subparsers(title="Commands")
|
parser['sub'] = parser['main'].add_subparsers(title="Commands")
|
||||||
|
|
||||||
|
parser['listprojects'] = parser['sub'].add_parser(
|
||||||
parser['listprojects'] = parser['sub'].add_parser('listprojects',
|
'listprojects', parents=[parser['loglevel']])
|
||||||
parents=[parser['loglevel']])
|
|
||||||
parser['listprojects'].set_defaults(func=ListProjects.commandline)
|
parser['listprojects'].set_defaults(func=ListProjects.commandline)
|
||||||
|
|
||||||
parser['track'] = parser['sub'].add_parser('track',
|
parser['track'] = parser['sub'].add_parser('track',
|
||||||
parents=[parser['loglevel']])
|
parents=[parser['loglevel']])
|
||||||
parser['track'].set_defaults(func=Tracker.commandline)
|
parser['track'].set_defaults(func=Tracker.commandline)
|
||||||
parser['track'].add_argument("--sd", "--start", help="start date (default: first of this month, format: %s)" % ctt.DATEFORMAT_PLAIN,
|
parser['track'].add_argument(
|
||||||
|
"--sd", "--start",
|
||||||
|
help="start date (default: first of this month, format: %s)"
|
||||||
|
% ctt.DATEFORMAT_PLAIN,
|
||||||
nargs=1, dest="start")
|
nargs=1, dest="start")
|
||||||
parser['track'].add_argument("--ed", "--end", help="end date (default: last of this month, format: %s)" % ctt.DATEFORMAT_PLAIN,
|
parser['track'].add_argument(
|
||||||
|
"--ed", "--end",
|
||||||
|
help="end date (default: last of this month, format: %s)"
|
||||||
|
% ctt.DATEFORMAT_PLAIN,
|
||||||
nargs=1, default=None, dest="end")
|
nargs=1, default=None, dest="end")
|
||||||
parser['track'].add_argument("-n", "--no-comment", help="disable comment prompting after tracking",
|
parser['track'].add_argument(
|
||||||
|
"-n", "--no-comment", help="disable comment prompting after tracking",
|
||||||
action='store_false', dest="comment")
|
action='store_false', dest="comment")
|
||||||
parser['track'].add_argument("project", help="project to track time for", nargs=1)
|
parser['track'].add_argument(
|
||||||
|
"project", help="project to track time for", nargs=1)
|
||||||
|
|
||||||
parser['report'] = parser['sub'].add_parser('report',
|
parser['report'] = parser['sub'].add_parser('report',
|
||||||
parents=[parser['loglevel']])
|
parents=[parser['loglevel']])
|
||||||
parser['report'].set_defaults(func=Report.commandline)
|
parser['report'].set_defaults(func=Report.commandline)
|
||||||
parser['report'].add_argument("project", help="project to report time for", nargs='*')
|
parser['report'].add_argument(
|
||||||
parser['report'].add_argument("--sd", "--start", help="start date (default: first of this month, format: %s)" % ctt.DATEFORMAT_PLAIN,
|
"project", help="project to report time for", nargs='*')
|
||||||
|
parser['report'].add_argument(
|
||||||
|
"--sd", "--start",
|
||||||
|
help="start date (default: first of this month, format: %s)"
|
||||||
|
% ctt.DATEFORMAT_PLAIN,
|
||||||
nargs=1, dest="start")
|
nargs=1, dest="start")
|
||||||
parser['report'].add_argument("--ed", "--end", help="end date (default: last of this month, format: %s)" % ctt.DATEFORMAT_PLAIN,
|
parser['report'].add_argument(
|
||||||
|
"--ed", "--end",
|
||||||
|
help="end date (default: last of this month, format: %s)"
|
||||||
|
% ctt.DATEFORMAT_PLAIN,
|
||||||
nargs=1, default=None, dest="end")
|
nargs=1, default=None, dest="end")
|
||||||
|
|
||||||
parser['report'].add_argument("-a", "--all", help="List entries for all projects", action='store_true')
|
parser['report'].add_argument(
|
||||||
parser['report'].add_argument("-e", "--regexp", help="regular expression to match",
|
"-a", "--all", help="List entries for all projects",
|
||||||
default=None)
|
action='store_true')
|
||||||
parser['report'].add_argument("-i", "--ignore-case", help="ignore case distinctions", action="store_true")
|
parser['report'].add_argument(
|
||||||
parser['report'].add_argument("-f", "--format", help="output format (default: %s)" % ctt.REPORTFORMAT,
|
"-e", "--regexp", help="regular expression to match", default=None)
|
||||||
|
parser['report'].add_argument(
|
||||||
|
"-i", "--ignore-case", help="ignore case distinctions",
|
||||||
|
action="store_true")
|
||||||
|
parser['report'].add_argument(
|
||||||
|
"-f", "--format",
|
||||||
|
help="output format (default: %s)" % ctt.REPORTFORMAT,
|
||||||
default=ctt.REPORTFORMAT, dest="output_format")
|
default=ctt.REPORTFORMAT, dest="output_format")
|
||||||
parser['report'].add_argument("-s", "--summary", help="hide project names and list time entries in chronological order", action="store_true")
|
parser['report'].add_argument(
|
||||||
|
"-s", "--summary",
|
||||||
|
help="hide project names and list time entries in chronological order",
|
||||||
|
action="store_true")
|
||||||
|
|
||||||
#parser['track'].add_argument("-t", "--tag", help="Add tags",
|
# parser['track'].add_argument("-t", "--tag", help="Add tags",
|
||||||
# action="store_true")
|
# action="store_true")
|
||||||
|
|
||||||
args = parser['main'].parse_args()
|
args = parser['main'].parse_args()
|
||||||
|
@ -114,6 +138,5 @@ if __name__ == "__main__":
|
||||||
from ctt.report import Report
|
from ctt.report import Report
|
||||||
from ctt.listprojects import ListProjects
|
from ctt.listprojects import ListProjects
|
||||||
|
|
||||||
|
|
||||||
parse_argv(sys.argv[1:], ctt.VERSION)
|
parse_argv(sys.argv[1:], ctt.VERSION)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
7
setup.py
7
setup.py
|
@ -4,9 +4,10 @@ script to install ctt
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
sys.path.insert(0, 'lib/')
|
|
||||||
import ctt
|
import ctt
|
||||||
|
|
||||||
|
sys.path.insert(0, 'lib/')
|
||||||
|
|
||||||
setup(name='ctt',
|
setup(name='ctt',
|
||||||
version=ctt.VERSION,
|
version=ctt.VERSION,
|
||||||
author=ctt.AUTHOR,
|
author=ctt.AUTHOR,
|
||||||
|
@ -28,5 +29,5 @@ setup(name='ctt',
|
||||||
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
||||||
'Operating System :: POSIX',
|
'Operating System :: POSIX',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Requires-Python:: 3.x']
|
'Requires-Python:: 3.x'
|
||||||
)
|
])
|
||||||
|
|
Loading…
Reference in a new issue