Skip to content

Commit

Permalink
Better estimation logging (#39) (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
caseneuve authored Jul 21, 2024
1 parent af6887a commit a096ea4
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 23 deletions.
6 changes: 3 additions & 3 deletions src/dzira/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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)
Expand Down
12 changes: 7 additions & 5 deletions src/dzira/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
10 changes: 6 additions & 4 deletions tests/cli/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,8 @@ def setup_method(self, _):
timespent=3600*4,
timetracking=Mock(
remainingEstimate="1d",
originalEstimate="2d"
originalEstimate="2d",
raw={"timeSpent": "4h"}
)
)
)
Expand All @@ -439,16 +440,17 @@ def setup_method(self, _):
timespent=3600*2,
timetracking=Mock(
remainingEstimate="3d",
originalEstimate="3d"
originalEstimate="3d",
raw={"timeSpent": "2h"}
)
)
)
self.issues = [issue1, issue2]
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

Expand Down
28 changes: 17 additions & 11 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import pytest

from dzira.api import (
ISSUES_DEAFAULT_FIELDS,
connect_to_jira,
get_board_by_key,
get_closed_sprints_issues,
Expand All @@ -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")
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -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)
)


Expand All @@ -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)
)

0 comments on commit a096ea4

Please sign in to comment.