From d0196fefd00f23bfa35d502fdb6a818d54632ed3 Mon Sep 17 00:00:00 2001 From: Daniel Dybing Date: Sat, 14 Mar 2026 11:52:31 +0100 Subject: [PATCH 1/4] Fix issue1: Hide zero-hour days by default and add toggle option in menu --- tamigo.py | 26 +++++++++++++++++++++++--- test_export.py | 27 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/tamigo.py b/tamigo.py index d31683d..cfd64dd 100644 --- a/tamigo.py +++ b/tamigo.py @@ -20,10 +20,13 @@ def load_config(): if os.path.exists(CONFIG_FILE): try: with open(CONFIG_FILE, "r") as f: - return json.load(f) + config = json.load(f) + if "show_zero_hours" not in config: + config["show_zero_hours"] = False + return config except: pass - return {"theme": "fallout"} # Default to fallout because it's cooler + return {"theme": "fallout", "show_zero_hours": False} # Default to fallout and hide zero hours def save_config(config): with open(CONFIG_FILE, "w") as f: @@ -190,6 +193,8 @@ def get_worked_days(client, start_date_dt, end_date_dt): with console.status("[highlight]INITIALIZING DATA RETRIEVAL...[/highlight]" if current_theme_name == "fallout" else "Fetching data..."): data = client.get_employee_actual_shifts(start_date_dt, end_date_dt) + show_zero = current_config.get("show_zero_hours", False) + if data: work_days = {} for item in data: @@ -200,6 +205,11 @@ def get_worked_days(client, start_date_dt, end_date_dt): if hours == 0 and item.get("StartTime") and item.get("EndTime"): st, et = parse_tamigo_date(item.get("StartTime")), parse_tamigo_date(item.get("EndTime")) if st and et: hours = (et - st).total_seconds() / 3600.0 + + # Filter zero hours if requested + if hours == 0 and not show_zero: + continue + if (hours > 0 or item.get("CheckInTime") or item.get("ActualStartTime") or item.get("StartTime")) and not item.get("IsAbsent", False): if date_str not in work_days: work_days[date_str] = {"hours": hours, "text": item.get("ActualShiftText") or item.get("ActivityName") or "WORKED"} else: work_days[date_str]["hours"] += hours @@ -305,9 +315,14 @@ def main(): def menu(client): while True: + show_zero = current_config.get("show_zero_hours", False) + toggle_text = "HIDE ZERO-HOUR DAYS" if show_zero else "SHOW ZERO-HOUR DAYS" + if current_theme_name != "fallout": + toggle_text = "Hide zero-hour days" if show_zero else "Show zero-hour days" + choice = questionary.select( "SELECT ACTION:" if current_theme_name == "fallout" else "What would you like to do?", - choices=["Calculate actual work days", "Export hours worked", "Show profile info", "Switch UI Theme", "Logout and Exit"], + choices=["Calculate actual work days", "Export hours worked", "Show profile info", "Switch UI Theme", toggle_text, "Logout and Exit"], style=theme_data["questionary"] ).ask() @@ -320,6 +335,11 @@ def menu(client): new_theme = "normal" if current_theme_name == "fallout" else "fallout" apply_theme(new_theme) console.print(f"[success]UI THEME SET TO: {new_theme.upper()}[/success]") + elif choice == toggle_text: + current_config["show_zero_hours"] = not show_zero + save_config(current_config) + state = "VISIBLE" if not show_zero else "HIDDEN" + console.print(f"[success]ZERO-HOUR DAYS ARE NOW {state}.[/success]" if current_theme_name == "fallout" else f"[success]Zero-hour days are now {state.lower()}.[/success]") else: console.print("[highlight]SYSTEM SHUTDOWN...[/highlight]" if current_theme_name == "fallout" else "Exiting...") break diff --git a/test_export.py b/test_export.py index bee7040..10034b7 100644 --- a/test_export.py +++ b/test_export.py @@ -41,6 +41,33 @@ class TestTamigoExport(unittest.TestCase): self.assertIn("2025-03-10", work_days) self.assertEqual(work_days["2025-03-10"]["hours"], 8.0) + def test_get_worked_days_zero_hour_filtering(self): + # Mock API response with a 0-hour shift (missing EndTime so it stays 0) + mock_shifts = [ + { + "Date": "/Date(1741564800000)/", # 2025-03-10 + "StartTime": "2025-03-10T09:00:00Z", + "ActualShiftHours": 0, + "ActualShiftText": "Sick Day", + "IsAbsent": False + } + ] + self.client.get_employee_actual_shifts.return_value = mock_shifts + start_date = datetime(2025, 3, 1) + end_date = datetime(2025, 3, 15) + + # 1. Test with show_zero_hours = False (Default) + with patch('tamigo.console'), patch('tamigo.current_config', {'show_zero_hours': False}): + work_days = get_worked_days(self.client, start_date, end_date) + self.assertEqual(len(work_days), 0) + + # 2. Test with show_zero_hours = True + with patch('tamigo.console'), patch('tamigo.current_config', {'show_zero_hours': True}): + work_days = get_worked_days(self.client, start_date, end_date) + self.assertEqual(len(work_days), 1) + self.assertIn("2025-03-10", work_days) + self.assertEqual(work_days["2025-03-10"]["hours"], 0) + @patch('tamigo.select_date_range') @patch('tamigo.get_worked_days') @patch('questionary.select') -- 2.49.1 From b100533e03687c4fbb0ce513889022bb449af07e Mon Sep 17 00:00:00 2001 From: Daniel Dybing Date: Sat, 14 Mar 2026 11:53:53 +0100 Subject: [PATCH 2/4] Refactor: Move theme and display settings to a dedicated Settings menu --- tamigo.py | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tamigo.py b/tamigo.py index cfd64dd..65bce72 100644 --- a/tamigo.py +++ b/tamigo.py @@ -315,14 +315,10 @@ def main(): def menu(client): while True: - show_zero = current_config.get("show_zero_hours", False) - toggle_text = "HIDE ZERO-HOUR DAYS" if show_zero else "SHOW ZERO-HOUR DAYS" - if current_theme_name != "fallout": - toggle_text = "Hide zero-hour days" if show_zero else "Show zero-hour days" - + settings_text = "SETTINGS" if current_theme_name == "fallout" else "Settings" choice = questionary.select( "SELECT ACTION:" if current_theme_name == "fallout" else "What would you like to do?", - choices=["Calculate actual work days", "Export hours worked", "Show profile info", "Switch UI Theme", toggle_text, "Logout and Exit"], + choices=["Calculate actual work days", "Export hours worked", "Show profile info", settings_text, "Logout and Exit"], style=theme_data["questionary"] ).ask() @@ -331,7 +327,28 @@ def menu(client): elif choice == "Show profile info": if client.user_info: console.print_json(data=client.user_info) else: console.print("[error]ERROR: NO PROFILE INFO.[/error]") - elif choice == "Switch UI Theme": + elif choice == settings_text: + settings_menu() + else: + console.print("[highlight]SYSTEM SHUTDOWN...[/highlight]" if current_theme_name == "fallout" else "Exiting...") + break + +def settings_menu(): + while True: + show_zero = current_config.get("show_zero_hours", False) + toggle_text = "HIDE ZERO-HOUR DAYS" if current_theme_name == "fallout" else "Hide zero-hour days" + if not show_zero: + toggle_text = "SHOW ZERO-HOUR DAYS" if current_theme_name == "fallout" else "Show zero-hour days" + + back_text = "BACK" if current_theme_name == "fallout" else "Back" + + choice = questionary.select( + "SELECT SETTING:" if current_theme_name == "fallout" else "Settings:", + choices=["Switch UI Theme", toggle_text, back_text], + style=theme_data["questionary"] + ).ask() + + if choice == "Switch UI Theme": new_theme = "normal" if current_theme_name == "fallout" else "fallout" apply_theme(new_theme) console.print(f"[success]UI THEME SET TO: {new_theme.upper()}[/success]") @@ -341,7 +358,6 @@ def menu(client): state = "VISIBLE" if not show_zero else "HIDDEN" console.print(f"[success]ZERO-HOUR DAYS ARE NOW {state}.[/success]" if current_theme_name == "fallout" else f"[success]Zero-hour days are now {state.lower()}.[/success]") else: - console.print("[highlight]SYSTEM SHUTDOWN...[/highlight]" if current_theme_name == "fallout" else "Exiting...") break if __name__ == "__main__": -- 2.49.1 From d3d861bd2d824600b681c006824e3b1a62410708 Mon Sep 17 00:00:00 2001 From: Daniel Dybing Date: Sat, 14 Mar 2026 11:55:35 +0100 Subject: [PATCH 3/4] UI: Use title case for Settings and zero-hour toggle menu items --- tamigo.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tamigo.py b/tamigo.py index 65bce72..c1350d8 100644 --- a/tamigo.py +++ b/tamigo.py @@ -315,7 +315,7 @@ def main(): def menu(client): while True: - settings_text = "SETTINGS" if current_theme_name == "fallout" else "Settings" + settings_text = "Settings" choice = questionary.select( "SELECT ACTION:" if current_theme_name == "fallout" else "What would you like to do?", choices=["Calculate actual work days", "Export hours worked", "Show profile info", settings_text, "Logout and Exit"], @@ -336,11 +336,9 @@ def menu(client): def settings_menu(): while True: show_zero = current_config.get("show_zero_hours", False) - toggle_text = "HIDE ZERO-HOUR DAYS" if current_theme_name == "fallout" else "Hide zero-hour days" - if not show_zero: - toggle_text = "SHOW ZERO-HOUR DAYS" if current_theme_name == "fallout" else "Show zero-hour days" + toggle_text = "Hide zero-hour days" if show_zero else "Show zero-hour days" - back_text = "BACK" if current_theme_name == "fallout" else "Back" + back_text = "Back" choice = questionary.select( "SELECT SETTING:" if current_theme_name == "fallout" else "Settings:", -- 2.49.1 From b5025bf7eca79105731b25f0e57005a6271cf566 Mon Sep 17 00:00:00 2001 From: Daniel Dybing Date: Sat, 14 Mar 2026 11:57:03 +0100 Subject: [PATCH 4/4] Test: Verify export functionality respects zero-hour filtering --- test_export.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test_export.py b/test_export.py index 10034b7..738c50c 100644 --- a/test_export.py +++ b/test_export.py @@ -68,6 +68,49 @@ class TestTamigoExport(unittest.TestCase): self.assertIn("2025-03-10", work_days) self.assertEqual(work_days["2025-03-10"]["hours"], 0) + @patch('tamigo.select_date_range') + @patch('questionary.select') + @patch('questionary.text') + @patch('tamigo.console') + def test_export_worked_hours_respects_zero_hour_filtering(self, mock_console, mock_text, mock_select, mock_date_range): + from tamigo import export_worked_hours + + # Mock API response with a 0-hour shift + mock_shifts = [ + { + "Date": "/Date(1741564800000)/", # 2025-03-10 + "StartTime": "2025-03-10T09:00:00Z", + "ActualShiftHours": 0, + "ActualShiftText": "Sick Day", + "IsAbsent": False + } + ] + self.client.get_employee_actual_shifts.return_value = mock_shifts + mock_date_range.return_value = (datetime(2025, 3, 1), datetime(2025, 3, 15)) + + # 1. Test with show_zero_hours = False -> Should NOT export anything + with patch('tamigo.current_config', {'show_zero_hours': False}): + export_worked_hours(self.client) + # Verify the warning was printed + mock_console.print.assert_any_call("[warning]WARNING: NO LOG ENTRIES DETECTED.[/warning]") + self.assertFalse(os.path.exists("tamigo_export_20250301_20250315.csv")) + + # 2. Test with show_zero_hours = True -> Should export the 0-hour day + mock_select.return_value.ask.return_value = "CSV" + mock_text.return_value.ask.return_value = "test_zero_export.csv" + + with patch('tamigo.current_config', {'show_zero_hours': True}): + export_worked_hours(self.client) + + self.assertTrue(os.path.exists("test_zero_export.csv")) + with open("test_zero_export.csv", "r") as f: + rows = list(csv.reader(f)) + self.assertEqual(len(rows), 2) # Header + 1 data row + self.assertEqual(rows[1][0], "2025-03-10") + self.assertEqual(rows[1][1], "0.00") + + os.remove("test_zero_export.csv") + @patch('tamigo.select_date_range') @patch('tamigo.get_worked_days') @patch('questionary.select') -- 2.49.1