From a096ea41562f4ab1bfe5b3c80aa046719b2333cb Mon Sep 17 00:00:00 2001 From: Piotr Kaznowski Date: Sun, 21 Jul 2024 18:39:39 +0200 Subject: [PATCH] Better estimation logging (#39) (#41) --- src/dzira/api.py | 6 +++--- src/dzira/cli/commands.py | 12 +++++++----- tests/cli/test_commands.py | 10 ++++++---- tests/test_api.py | 28 +++++++++++++++++----------- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/dzira/api.py b/src/dzira/api.py index e8ff1c2..6d58ccf 100644 --- a/src/dzira/api.py +++ b/src/dzira/api.py @@ -119,7 +119,7 @@ def get_issue_worklogs_by_user_and_date( return matching -ISSUES_DEAFAULT_FIELDS = ["Sprint,status,summary,timespent,timeestimate"] +ISSUES_DEFAULT_FIELDS = ["Sprint,status,summary,timespent,timeestimate,timetracking"] def search_issues_with_sprint_info( @@ -136,9 +136,9 @@ def search_issues_with_sprint_info( query = f"project = {project_key} AND sprint in {fn}" if extra_fields: - extra_fields += ISSUES_DEAFAULT_FIELDS + extra_fields += ISSUES_DEFAULT_FIELDS else: - extra_fields = ISSUES_DEAFAULT_FIELDS + extra_fields = ISSUES_DEFAULT_FIELDS fields = ",".join(extra_fields) issues = jira.search_issues(jql_str=query, fields=fields) diff --git a/src/dzira/cli/commands.py b/src/dzira/cli/commands.py index 1023649..6ae2064 100644 --- a/src/dzira/cli/commands.py +++ b/src/dzira/cli/commands.py @@ -242,25 +242,27 @@ def show_issues(sprint_and_issues: D, format: str) -> None: if format in ("json", "csv"): colors.use = False - fmt = lambda t: str(timedelta(seconds=t)) if t else None state_clr = {"To Do": "^magenta", "In Progress": "^yellow", "Done": "^green"} clr = lambda s: c(state_clr.get(s, "^reset"), "^bold", s) def _estimate(i): try: - remining = i.fields.timetracking.remainingEstimate + remaining = i.fields.timetracking.remainingEstimate original = i.fields.timetracking.originalEstimate - return f"{remining} ({original})" if remining != original else original + return f"{remaining} ({original})" if remaining != original else original except Exception: - return fmt(i.fields.timeestimate) + _get_estimated_seconds_or_nothing = lambda t: str(timedelta(seconds=t)) if t else None + return _get_estimated_seconds_or_nothing(i.fields.timeestimate) headers = ["key", "summary", "state", "spent", "estimated"] + + _get_time_spent_or_nothing = lambda t: t.raw.get('timeSpent') if t.raw else None processed_issues = [ [ c("^blue", i.key), i.fields.summary, clr(i.fields.status.name), - fmt(i.fields.timespent), + _get_time_spent_or_nothing(i.fields.timetracking), _estimate(i), ] for i in reversed(sorted(sprint_and_issues["issues"], key=lambda i: i.fields.status.name)) diff --git a/tests/cli/test_commands.py b/tests/cli/test_commands.py index b17a0ef..35b235a 100644 --- a/tests/cli/test_commands.py +++ b/tests/cli/test_commands.py @@ -427,7 +427,8 @@ def setup_method(self, _): timespent=3600*4, timetracking=Mock( remainingEstimate="1d", - originalEstimate="2d" + originalEstimate="2d", + raw={"timeSpent": "4h"} ) ) ) @@ -439,7 +440,8 @@ def setup_method(self, _): timespent=3600*2, timetracking=Mock( remainingEstimate="3d", - originalEstimate="3d" + originalEstimate="3d", + raw={"timeSpent": "2h"} ) ) ) @@ -447,8 +449,8 @@ def setup_method(self, _): self.sprint_and_issues = D(sprint=self.sprint, issues=self.issues) self.headers = ["key", "summary", "state", "spent", "estimated"] self.processed_issues = [ - ["XYZ-2", "description 2", "To Do", str(datetime.timedelta(seconds=3600*2)), "3d"], - ["XYZ-1", "description 1", "In Progress", str(datetime.timedelta(seconds=3600*4)), "1d (2d)"] + ["XYZ-2", "description 2", "To Do", "2h", "3d"], + ["XYZ-1", "description 1", "In Progress", "4h", "1d (2d)"] ] colors.use = False diff --git a/tests/test_api.py b/tests/test_api.py index 5af5820..b249ab9 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -6,7 +6,6 @@ import pytest from dzira.api import ( - ISSUES_DEAFAULT_FIELDS, connect_to_jira, get_board_by_key, get_closed_sprints_issues, @@ -26,6 +25,11 @@ # fixtures: +@pytest.fixture() +def issues_default_fields(): + return ["Sprint,status,summary,timespent,timeestimate,timetracking"] + + @pytest.fixture() def mock_jira(mocker): return mocker.patch("dzira.api.JIRA") @@ -268,7 +272,7 @@ def test_get_issue_worklogs_by_user_and_date_exists_early_when_no_worklogs_found assert not mock_jira.worklogs.called -def test_search_issues_with_sprint_info_uses_sprint_id(mock_jira): +def test_search_issues_with_sprint_info_uses_sprint_id(mock_jira, issues_default_fields): sprint_id = "123" result = search_issues_with_sprint_info( @@ -277,12 +281,12 @@ def test_search_issues_with_sprint_info_uses_sprint_id(mock_jira): mock_jira.search_issues.assert_called_once_with( jql_str=f"sprint = {sprint_id}", - fields=",".join(ISSUES_DEAFAULT_FIELDS) + fields=",".join(issues_default_fields) ) assert result == list(mock_jira.search_issues_with_sprint_info.return_value) -def test_search_issues_with_sprint_info_sprint_id_has_precedence(mock_jira): +def test_search_issues_with_sprint_info_sprint_id_has_precedence(mock_jira, issues_default_fields): sprint_id = "123" search_issues_with_sprint_info( @@ -291,18 +295,18 @@ def test_search_issues_with_sprint_info_sprint_id_has_precedence(mock_jira): mock_jira.search_issues.assert_called_once_with( jql_str=f"sprint = {sprint_id}", - fields=",".join(ISSUES_DEAFAULT_FIELDS) + fields=",".join(issues_default_fields) ) -def test_search_issues_with_sprint_info_uses_default_state(mock_jira): +def test_search_issues_with_sprint_info_uses_default_state(mock_jira, issues_default_fields): project_key = "ABC-123" search_issues_with_sprint_info(mock_jira, project_key=project_key) mock_jira.search_issues.assert_called_once_with( jql_str=f"project = {project_key} AND sprint in openSprints()", - fields=",".join(ISSUES_DEAFAULT_FIELDS) + fields=",".join(issues_default_fields) ) @@ -314,23 +318,25 @@ def test_search_issues_with_sprint_info_uses_default_state(mock_jira): ("future", "futureSprints()"), ] ) -def test_search_issues_with_sprint_info_uses_provided_state(mock_jira, state, fn): +def test_search_issues_with_sprint_info_uses_provided_state( + mock_jira, state, fn, issues_default_fields +): project_key = "ABC-123" search_issues_with_sprint_info(mock_jira, project_key=project_key, state=state) mock_jira.search_issues.assert_called_once_with( jql_str=f"project = {project_key} AND sprint in {fn}", - fields=",".join(ISSUES_DEAFAULT_FIELDS) + fields=",".join(issues_default_fields) ) -def test_search_issues_with_sprint_info_fetches_extra_fields(mock_jira): +def test_search_issues_with_sprint_info_fetches_extra_fields(mock_jira, issues_default_fields): project_key = "ABC-123" search_issues_with_sprint_info(mock_jira, project_key=project_key, extra_fields=["Foo", "Bar"]) mock_jira.search_issues.assert_called_once_with( jql_str=f"project = {project_key} AND sprint in openSprints()", - fields=",".join(["Foo", "Bar"] + ISSUES_DEAFAULT_FIELDS) + fields=",".join(["Foo", "Bar"] + issues_default_fields) )