def test022(self): """Parse mix of <date>: <number of hours> | <period>+""" stream = StringIO.StringIO( "20111202: 4.5\n" "20111205: 10:00-12:15, 12:45-17:45 # Comment..." ) records = track_time.parse(stream) self.assertEqual(len(records), 2) record = records[0] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 4.5) self.assertEqual(record.project, [""]) record = records[1] self.assertEqual(record.date, datetime.date(2011, 12, 5)) self.assertEqual(record.nr_hours, 7.25) self.assertEqual(record.project, [""]) stream = StringIO.StringIO( "20111202: 4.5 # Comment...\n" "20111202: 10:00-12:15" ) records = track_time.parse(stream) self.assertEqual(len(records), 2) record = records[0] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 4.5) self.assertEqual(record.project, [""]) record = records[1] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 2.25) self.assertEqual(record.project, [""]) stream = StringIO.StringIO( "20111202: 10:00-12:15, 4.5" ) records = track_time.parse(stream) self.assertEqual(len(records), 1) record = records[0] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 6.75) self.assertEqual(record.project, [""])
def test003(self): """Parse empty line""" stream = StringIO.StringIO( "\n" ) records = track_time.parse(stream) self.assertEqual(len(records), 0)
def query_hours( timesheet_pathname, nr_hours_to_work, project_pattern, nr_weeks_to_report): selected_records = track_time.parse(file(timesheet_pathname, "r")) to_time_point = track_time.last_day_of_week(datetime.date.today()) from_time_point = to_time_point - datetime.timedelta( days=(nr_weeks_to_report * 7) - 1) assert from_time_point.isocalendar()[2] == 1 # Monday. assert to_time_point.isocalendar()[2] == 7 # Sunday. selected_records = track_time.filter_projects_by_name(selected_records, project_pattern) selected_records = track_time.filter_projects_by_date(selected_records, from_time_point, to_time_point) merged_records = track_time.merge_records_by_date(selected_records) merged_records = sorted(merged_records, key=lambda record: record.date) # Hours per day (work + sick + holiday + vacation). table = prettytable.PrettyTable(["Date", "Hours"]) table.align = "r" for record in merged_records: table.add_row([ "{} {}".format(record.date.strftime("%a"), record.date), "{:.2f}".format(record.nr_hours) ]) write_table(table) merged_records = track_time.merge_records_by_week(selected_records) merged_records = sorted(merged_records, key=lambda record: record.date) # Weekly balance. table = prettytable.PrettyTable(["Week", "Balance"]) table.align = "r" for record in merged_records: table.add_row([ "{} {}".format(record.date.strftime("%a"), record.date), "{:+.2f}".format(record.nr_hours - nr_hours_to_work) ]) write_table(table) # Balance of the whole period. if merged_records: table = prettytable.PrettyTable(["Period", "Balance"]) table.align = "r" record = track_time.merge_records(selected_records) record.date = merged_records[0].date table.add_row([ "{} {}".format(record.date.strftime("%a"), record.date), "{:+.2f}".format(record.nr_hours - (nr_weeks_to_report * nr_hours_to_work)) ]) write_table(table)
def test031(self): """Parse <date>: <number of hours>: <project>""" stream = StringIO.StringIO( "20121030: 3: project_x\n" "20121030: 5: project_y # Comment...\n" "20121031: 8: project_z" ) records = track_time.parse(stream) self.assertEqual(len(records), 3) record = records[0] self.assertEqual(record.date, datetime.date(2012, 10, 30)) self.assertEqual(record.nr_hours, 3.0) self.assertEqual(len(record.project), 1) self.assertEqual(record.project[0], "project_x") record = records[1] self.assertEqual(record.date, datetime.date(2012, 10, 30)) self.assertEqual(record.nr_hours, 5.0) self.assertEqual(len(record.project), 1) self.assertEqual(record.project[0], "project_y") record = records[2] self.assertEqual(record.date, datetime.date(2012, 10, 31)) self.assertEqual(record.nr_hours, 8.0) self.assertEqual(len(record.project), 1) self.assertEqual(record.project[0], "project_z")
def test002(self): """Parse comment""" stream = StringIO.StringIO( "# This is a comment\n" ) records = track_time.parse(stream) self.assertEqual(len(records), 0)
def test004(self): """Parse line with only whitespace""" stream = StringIO.StringIO( " \n" "\t\n" ) records = track_time.parse(stream) self.assertEqual(len(records), 0)
def test_merge_records_by_category(self): records = track_time.parse(file("categories-001.txt")) merged_records = track_time.merge_records_by_category(records) self.assertEqual(len(merged_records), 4) self.assertEqual(merged_records[0].project, ["project"]) self.assertEqual(merged_records[1].project, ["holiday"]) self.assertEqual(merged_records[2].project, ["vacation"]) self.assertEqual(merged_records[3].project, ["sick"])
def test011(self): """Parse <date>: <number of hours>""" stream = StringIO.StringIO( "20111202: 8\n" "20111205: 4.5 # Comment..." ) records = track_time.parse(stream) self.assertEqual(len(records), 2) record = records[0] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 8.0) self.assertEqual(record.project, [""]) record = records[1] self.assertEqual(record.date, datetime.date(2011, 12, 5)) self.assertEqual(record.nr_hours, 4.5) self.assertEqual(record.project, [""]) stream = StringIO.StringIO( "20111202: 8 # Comment...\n" "20111202: 4.5\n" "20111202: 3, 4" ) records = track_time.parse(stream) self.assertEqual(len(records), 3) record = records[0] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 8.0) self.assertEqual(record.project, [""]) record = records[1] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 4.5) self.assertEqual(record.project, [""]) record = records[2] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 7.0) self.assertEqual(record.project, [""])
def test_merge_child_projects_with_parents(self): records = track_time.parse(file("sub_projects-001.txt")) merged_records = track_time.merge_child_projects_with_parents(records) self.assertEqual(len(merged_records), 2) self.assertEqual(merged_records[0].date, None) self.assertEqual(merged_records[0].nr_hours, 48) self.assertEqual(merged_records[0].project, ["my_project_a"]) self.assertEqual(merged_records[1].date, None) self.assertEqual(merged_records[1].nr_hours, 48) self.assertEqual(merged_records[1].project, ["my_project_b"])
def test051(self): """Parse Unicode characters""" stream = StringIO.StringIO( "20111202: 8:30-12:00, 12:30-17:00: prøject_ø # Cømment...\n" ) records = track_time.parse(stream) self.assertEqual(len(records), 1) record = records[0] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 8) self.assertEqual(len(record.project), 1) self.assertEqual(record.project[0], "prøject_ø")
def test_filter_projects_by_name(self): records = track_time.parse(file("sub_projects-001.txt")) self.assertEqual(len(records), 20) selected_records = track_time.filter_projects_by_name(records) self.assertEqual(len(selected_records), 20) selected_records = track_time.filter_projects_by_name(records, project_name_pattern="my_project_b/*") self.assertEqual(len(selected_records), 10) selected_records = track_time.filter_projects_by_name(records, project_name_pattern="*/blog") self.assertEqual(len(selected_records), 4)
def test_merge_records_by_week(self): records = track_time.parse(file("sub_projects-001.txt")) merged_records = track_time.merge_records_by_week(records) self.assertEqual(len(merged_records), 5) self.assertEqual(merged_records[0].date, datetime.date(2013, 12, 30)) self.assertEqual(merged_records[1].date, datetime.date(2014, 1, 6)) self.assertEqual(merged_records[2].date, datetime.date(2014, 1, 27)) self.assertEqual(merged_records[3].date, datetime.date(2014, 2, 3)) self.assertEqual(merged_records[4].date, datetime.date(2014, 2, 10)) self.assertEqual(merged_records[0].nr_hours, 30) self.assertEqual(merged_records[1].nr_hours, 18) self.assertEqual(merged_records[2].nr_hours, 13) self.assertEqual(merged_records[3].nr_hours, 23) self.assertEqual(merged_records[4].nr_hours, 12)
def query_project( timesheet_pathname, project_pattern, aggregate): records = track_time.parse(file(timesheet_pathname, "r")) selected_records = track_time.filter_projects_by_name(records, project_pattern) merged_records = track_time.merge_records_by_project(selected_records) if aggregate: merged_records = track_time.merge_child_projects_with_parents( merged_records) # Number of hours per projects --------------------------------------------- table = prettytable.PrettyTable(["Project", "Hours", "Days"]) table.align["Project"] = "l" table.align["Hours"] = "r" table.align["Days"] = "r" table.sortby="Project" for record in merged_records: table.add_row([ record.project_string(), "{:.2f}".format(record.nr_hours), "{:.2f}".format(record.nr_days) ]) write_table(table) # Number of hours overall -------------------------------------------------- table = prettytable.PrettyTable(["Hours", "Days"]) table.align["Hours"] = "r" table.align["Days"] = "r" nr_hours = 0.0 nr_days = 0.0 for record in merged_records: nr_hours += record.nr_hours nr_days += record.nr_days table.add_row([ "{:.2f}".format(nr_hours), "{:.2f}".format(nr_days) ]) write_table(table)
def test061(self): """Parse sub-projects""" stream = StringIO.StringIO( "20111202: 8: a/b/c/d/e/f" ) records = track_time.parse(stream) self.assertEqual(len(records), 1) record = records[0] self.assertEqual(len(record.project), 6) self.assertEqual(record.project[0], "a") self.assertEqual(record.project[1], "b") self.assertEqual(record.project[2], "c") self.assertEqual(record.project[3], "d") self.assertEqual(record.project[4], "e") self.assertEqual(record.project[5], "f")
def test101(self): """Parse error when input is wrongly formatted""" lines = [ "20111202:", "20111202: project_a", "201x1202: 8", "20111202: 8:05-9x", ] for line in lines: with self.assertRaises(ValueError) as context_manager: stream = StringIO.StringIO( line ) records = track_time.parse(stream) exception = context_manager.exception self.assert_(str(exception).find("Parse error") != -1)
def test010(self): """Parse <date>""" stream = StringIO.StringIO( "20111202\n" "20111205 # Comment..." ) records = track_time.parse(stream) self.assertEqual(len(records), 2) record = records[0] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 8.0) self.assertEqual(record.project, [""]) record = records[1] self.assertEqual(record.date, datetime.date(2011, 12, 5)) self.assertEqual(record.nr_hours, 8.0) self.assertEqual(record.project, [""])
def test021(self): """Parse <date>: <period>+""" stream = StringIO.StringIO( "20111202: 8:30-12:00, 12:30-17:00 # Comment...\n" "20111205: 10:00-12:15, 12:45-17:45" ) records = track_time.parse(stream) self.assertEqual(len(records), 2) record = records[0] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 8) self.assertEqual(record.project, [""]) record = records[1] self.assertEqual(record.date, datetime.date(2011, 12, 5)) self.assertEqual(record.nr_hours, 7.25) self.assertEqual(record.project, [""])
def test041(self): """Parse <date>: <period>+: <project>""" stream = StringIO.StringIO( "20111202: 8:30-12:00, 12:30-17:00: project_a\n" "20111205: 10:00-12:15, 12:45-17:45: project_b # Comment..." ) records = track_time.parse(stream) self.assertEqual(len(records), 2) record = records[0] self.assertEqual(record.date, datetime.date(2011, 12, 2)) self.assertEqual(record.nr_hours, 8) self.assertEqual(len(record.project), 1) self.assertEqual(record.project[0], "project_a") record = records[1] self.assertEqual(record.date, datetime.date(2011, 12, 5)) self.assertEqual(record.nr_hours, 7.25) self.assertEqual(len(record.project), 1) self.assertEqual(record.project[0], "project_b")
def query_vacation( timesheet_pathname, nr_hours_to_work, # Per week. nr_hours_vacation): # Per year. records = track_time.parse(file(timesheet_pathname, "r")) merged_records = track_time.merge_records_by_category(records) # Vacation ----------------------------------------------------------------- record_by_category = {record.project_string(): record for record in merged_records} vacation_record = record_by_category["vacation"] nr_hours_spent = vacation_record.nr_hours nr_hours_left = nr_hours_vacation - nr_hours_spent nr_days_left = nr_hours_left / 8.0 table = prettytable.PrettyTable(["Available", "Spent", "Balance (h)", "Balance (d)"]) table.align = "r" table.add_row([ "{:.2f}".format(nr_hours_vacation), "{:.2f}".format(nr_hours_spent), "{:.2f}".format(nr_hours_left), "{:.2f}".format(nr_days_left) ]) write_table(table, header="vacation") # Overtime ----------------------------------------------------------------- # Number of hours that should have been spent on work, by the end of the # week. to_time_point = track_time.last_day_of_week(datetime.date.today()) week_number = to_time_point.isocalendar()[1] nr_hours_to_work *= week_number # Number of hours that have been spent on work, in whatever way. nr_hours_spent_on_work = sum([record.nr_hours for record in merged_records]) nr_hours_overtime = nr_hours_spent_on_work - nr_hours_to_work nr_days_overtime = nr_hours_overtime / 8.0 table = prettytable.PrettyTable(["To work", "Worked", "Balance (h)", "Balance (d)"]) table.align = "r" table.add_row([ "{:.2f}".format(nr_hours_to_work), "{:.2f}".format(nr_hours_spent_on_work), "{:.2f}".format(nr_hours_overtime), "{:.2f}".format(nr_days_overtime) ]) write_table(table, header="overtime") # Overall ------------------------------------------------------------------ balance_vacation = nr_hours_left balance_overtime = nr_hours_overtime balance_in_hours = nr_hours_left + nr_hours_overtime balance_in_days = balance_in_hours / 8.0 table = prettytable.PrettyTable(["Balance vacation", "Balance overtime", "Balance (h)", "Balance (d)"]) table.align = "r" table.add_row([ "{:.2f}".format(balance_vacation), "{:.2f}".format(balance_overtime), "{:.2f}".format(balance_in_hours), "{:.2f}".format(balance_in_days) ]) write_table(table, header="balance")
def test_merge_records_by_date(self): records = track_time.parse(file("sub_projects-001.txt")) merged_records = track_time.merge_records_by_date(records) self.assertEqual(len(merged_records), 16)
def test_filter_projects_by_date(self): records = track_time.parse(file("sub_projects-001.txt")) selected_records = track_time.filter_projects_by_date(records, from_time_point=datetime.date(2014, 2, 2), to_time_point=datetime.date(2014, 2, 5)) self.assertEqual(len(selected_records), 5)
def test001(self): """Parse empty file""" stream = StringIO.StringIO() records = track_time.parse(stream) self.assertEqual(len(records), 0)