Skip to content

Commit

Permalink
#11 unified json formats for report and ls commands
Browse files Browse the repository at this point in the history
  • Loading branch information
caseneuve committed Jan 27, 2024
1 parent 21d98bd commit d45dab2
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 60 deletions.
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,41 @@ Usage: dzira report [OPTIONS]
Show work logged for today or for DATE using given FORMAT.
TABLE format (default) will print a table with summary showing worklogs for
every issue having work logged on given DATE.
CSV and JSON formats are indended to be parsable.
CSV format:
issue, summary, worklog, started, spent, spent_seconds, comment
XY-1,Foo bar,1234,12:45:00,1h 15m,4500,implementing foo in bar
JSON format:
{
"issues": [
{
"key": "XY-1",
"summary": "Foo bar",
"issue_total_time": "1h 15m",
"issue_total_spent_seconds": 4500,
"worklogs": [
{
"id": "1",
"started": "12:45:00",
"spent": "1h 15m",
"spent_seconds": 4500,
"comment": "implementing foo in bar"
}
]
},
],
"total_time": "1h 15m",
"total_seconds": 4500
}
Options:
-d, --date [%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]
Date to show report for
-f, --format [simple|csv|json] How to display the report [default: simple]
-f, --format [table|csv|json] How to display the report [default: table]
-h, --help Show this message and exit
```
64 changes: 51 additions & 13 deletions src/dzira/dzira.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ def estimate(i):
except Exception:
return fmt(i.fields.timeestimate)

headers = ("#", "summary", "state", "spent", "estimated")
headers = ("key", "summary", "state", "spent", "estimated")
issues = [
(
c("^blue", i.key),
Expand Down Expand Up @@ -792,7 +792,7 @@ def _seconds_to_hour_minute_fmt(seconds):
def show_report(worklogs: D, format: str | None) -> None:
total_time = 0
csv_rows = []
json_dict = {}
json_dict = {"issues": [], "total_time": None, "total_seconds": None}
tables = []
for issue in worklogs:
key, summary = issue
Expand Down Expand Up @@ -824,19 +824,22 @@ def show_report(worklogs: D, format: str | None) -> None:
processed_worklog = [f"[{w.id}]", formatted_time, f":{time_spent:>6}", comment or ""]
issue_worklogs.append(processed_worklog)

if format == "simple":
if format == "table":
header = c(
"^bold", f"[{key}] {summary} ",
"^cyan", f"({_seconds_to_hour_minute_fmt(issue_total_time)})"
)
tables.append((header, issue_worklogs))
elif format == "json":
json_dict[key] = {
"summary": summary,
"issue_total_time": _seconds_to_hour_minute_fmt(issue_total_time),
"issue_total_spent_seconds": issue_total_time,
"worklogs": issue_worklogs
}
json_dict["issues"].append(
{
"key": key,
"summary": summary,
"issue_total_time": _seconds_to_hour_minute_fmt(issue_total_time),
"issue_total_spent_seconds": issue_total_time,
"worklogs": issue_worklogs
}
)

if format == "csv":
headers = ["issue", "summary", "worklog", "started", "spent", "spent_seconds", "comment"]
Expand All @@ -852,7 +855,10 @@ def show_report(worklogs: D, format: str | None) -> None:
print()
print(header)
print(tabulate(rows, maxcolwidths=[None, None, None, 60]))
print(f"\n{c('^bold', 'Total spent time')}: {_seconds_to_hour_minute_fmt(total_time)}\n")
if tables:
print(f"\n{c('^bold', 'Total spent time')}: {_seconds_to_hour_minute_fmt(total_time)}\n")
else:
print("No work logged on given date")


# TODO: add sprint option
Expand All @@ -861,23 +867,55 @@ def show_report(worklogs: D, format: str | None) -> None:
@click.option("-d", "--date", "report_date", help="Date to show report for", type=click.DateTime())
@click.option(
"-f", "--format",
type=click.Choice(["simple", "csv", "json"]), default="simple", show_default=True,
type=click.Choice(["table", "csv", "json"]), default="table", show_default=True,
help="How to display the report",
)
@click.help_option("-h", "--help", help="Show this message and exit")
def report(ctx, report_date, format):
"""
Show work logged for today or for DATE using given FORMAT.
TABLE format (default) will print a table with summary showing
worklogs for every issue having work logged on given DATE.
\b
CSV format:
issue, summary, worklog, started, spent, spent_seconds, comment
XY-1,Foo bar,1234,12:45:00,1h 15m,4500,implementing foo in bar
\b
JSON format:
{
"issues": [
{
"key": "XY-1",
"summary": "Foo bar",
"issue_total_time": "1h 15m",
"issue_total_spent_seconds": 4500,
"worklogs": [
{
"id": "1",
"started": "12:45:00",
"spent": "1h 15m",
"spent_seconds": 4500,
"comment": "implementing foo in bar"
}
]
},
],
"total_time": "1h 15m",
"total_seconds": 4500
}
"""
config = get_config(config=ctx.obj)
jira = get_jira(config).result
user = get_user(jira, config).result
issues = get_issues_with_work_logged_on_date(jira, report_date)
if issues.result:
worklogs = get_user_worklogs_from_date(jira, user, issues).result
show_report(worklogs, format=format)
else:
print("No work logged on given date")
worklogs = D()
show_report(worklogs, format=format)


@cli.command(hidden=True)
Expand Down
84 changes: 38 additions & 46 deletions tests/test_dzira.py
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ def setup(self):
)
)
self.issues = [issue1, issue2]
self.headers = ("#", "summary", "state", "spent", "estimated")
self.headers = ("key", "summary", "state", "spent", "estimated")
self.processed_issues = [
(
"XYZ-2", "description 2", "To Do",
Expand Down Expand Up @@ -1448,15 +1448,11 @@ def setup(self):
}
)

@patch("src.dzira.dzira.json")
@patch("src.dzira.dzira.csv")
@patch("src.dzira.dzira.print")
@patch("src.dzira.dzira.tabulate")
def test_uses_tabulate_to_show_the_report_with_worklog_id_timestamp_timespent_and_comment(
self, mock_tabulate, mock_print, mock_csv, mock_json
):
mock_tabulate.side_effect = [sentinel.t1, sentinel.t2]
show_report(self.worklogs_of_issues, format="simple")
show_report(self.worklogs_of_issues, format="table")

assert mock_tabulate.call_args_list == [
call([["[1]", "12:42:16", ": 30m", "task a"]], maxcolwidths=[None, None, None, 60]),
Expand All @@ -1474,10 +1470,6 @@ def test_uses_tabulate_to_show_the_report_with_worklog_id_timestamp_timespent_an
assert not mock_csv.DictWriter.called
assert not mock_json.dumps.called

@patch("src.dzira.dzira.tabulate")
@patch("src.dzira.dzira.json")
@patch("src.dzira.dzira.print")
@patch("src.dzira.dzira.csv")
def test_prints_data_in_csv_format(
self, mock_csv, mock_print, mock_json, mock_tabulate
):
Expand All @@ -1497,44 +1489,44 @@ def test_prints_data_in_csv_format(
assert not mock_json.dumps.called
assert not mock_print.called

@patch("src.dzira.dzira.tabulate")
@patch("src.dzira.dzira.json")
@patch("src.dzira.dzira.print")
@patch("src.dzira.dzira.csv")
def test_prints_data_in_json_format(
self, mock_csv, mock_print, mock_json, mock_tabulate
):
show_report(self.worklogs_of_issues, format="json")

processed_worklogs = {
"XY-1": {
"summary": "issue 1",
"issue_total_time": "0h 30m",
"issue_total_spent_seconds": 30 * 60,
"worklogs": [
{
"id": "1",
"started": "12:42:16",
"spent": "30m",
"spent_seconds": 30 * 60,
"comment": "task a"
}
]
},
"XY-2": {
"summary": "issue 2",
"issue_total_time": "1h 15m",
"issue_total_spent_seconds": (60 * 60) + (15 * 60),
"worklogs": [
{
"id": "2",
"started": "14:42:00",
"spent": "1h 15m",
"spent_seconds": (60 * 60) + (15 * 60),
"comment": "task b"
}
]
},
"issues": [
{
"key": "XY-1",
"summary": "issue 1",
"issue_total_time": "0h 30m",
"issue_total_spent_seconds": 30 * 60,
"worklogs": [
{
"id": "1",
"started": "12:42:16",
"spent": "30m",
"spent_seconds": 30 * 60,
"comment": "task a"
}
]
},
{
"key": "XY-2",
"summary": "issue 2",
"issue_total_time": "1h 15m",
"issue_total_spent_seconds": (60 * 60) + (15 * 60),
"worklogs": [
{
"id": "2",
"started": "14:42:00",
"spent": "1h 15m",
"spent_seconds": (60 * 60) + (15 * 60),
"comment": "task b"
}
]
}
],
"total_time": "1h 45m",
"total_seconds": (30 * 60) + (60 * 60) + (15 * 60)
}
Expand Down Expand Up @@ -1585,7 +1577,7 @@ def test_runs_stuff_in_order(
)
mock_show_report.assert_called_once_with(
mock_get_user_worklogs_from_date.return_value.result,
format="simple"
format="table"
)

@patch("src.dzira.dzira.get_user", Mock())
Expand All @@ -1594,7 +1586,7 @@ def test_runs_stuff_in_order(
@patch("src.dzira.dzira.show_report")
@patch("src.dzira.dzira.get_user_worklogs_from_date")
@patch("src.dzira.dzira.get_issues_with_work_logged_on_date")
def test_does_not_run_show_report_when_no_worklogs_found(
def test_runs_show_report_with_empty_dict_when_no_worklogs_found(
self,
mock_get_issues_with_work_logged_on_date,
mock_get_user_worklogs_from_date,
Expand All @@ -1605,9 +1597,9 @@ def test_does_not_run_show_report_when_no_worklogs_found(
self.runner.invoke(report, ["--date", "2023-11-26"])

assert not mock_get_user_worklogs_from_date.called
assert not mock_show_report.called
mock_show_report.assert_called_once_with(D(), format="table")

@pytest.mark.parametrize("fmt", ["csv", "json", "simple"])
@pytest.mark.parametrize("fmt", ["csv", "json", "table"])
@patch("src.dzira.dzira.get_user", Mock())
@patch("src.dzira.dzira.get_jira", Mock(return_value=Mock(result=sentinel.jira)))
@patch("src.dzira.dzira.get_config", Mock(return_value=Mock(result=sentinel.user)))
Expand Down

0 comments on commit d45dab2

Please sign in to comment.