Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add option to list to show column with state #102

Merged
merged 6 commits into from
Feb 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .isort.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[settings]
known_first_party=doing
10 changes: 3 additions & 7 deletions src/doing/issue/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@

@click.group()
def issue():
"""
Work with issues.
"""
"""Work with issues."""
pass


Expand All @@ -24,8 +22,7 @@ def issue():
@issue.command()
@click.argument("work_item_id", nargs=-1, required=True)
def close(work_item_id):
"""
Close a specific WORK_ITEM_ID.
"""Close a specific WORK_ITEM_ID.

A '#' prefix is allowed. You can specify multiple IDs by separating with a space.
"""
Expand Down Expand Up @@ -125,8 +122,7 @@ def create(
web: bool,
story_points: str,
) -> None:
"""
Create an issue.
"""Create an issue.

ISSUE is the title to be used for the new work item.
"""
Expand Down
58 changes: 44 additions & 14 deletions src/doing/list/_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
def work_item_query(
assignee: str, author: str, label: str, state: str, area: str, iteration: str, type: str, story_points: str
):
"""
Build query in wiql.
"""Build query in wiql.

# More on 'work item query language' syntax:
# https://docs.microsoft.com/en-us/azure/devops/boards/queries/wiql-syntax?view=azure-devops
Expand All @@ -28,7 +27,7 @@ def work_item_query(

# Get all workitems
query = "SELECT [System.Id],[System.Title],[System.AssignedTo],"
query += "[System.WorkItemType],[System.State],[System.CreatedDate]"
query += "[System.WorkItemType],[System.State],[System.CreatedDate], [System.State]"
query += f"FROM WorkItems WHERE [System.AreaPath] = '{area}' "
# Filter on iteration. Note we use UNDER so that user can choose to provide teams path for all sprints.
query += f"AND [System.IterationPath] UNDER '{iteration}' "
Expand Down Expand Up @@ -82,12 +81,11 @@ def cmd_list(
organization: str,
project: str,
type: str,
show_state: bool,
story_points: str = "",
output_format: str = "table",
) -> None:
"""
Run `doing list` command.
"""
"""Run `doing list` command."""
# Get config settings
assignee = replace_user_aliases(assignee)
author = replace_user_aliases(author)
Expand Down Expand Up @@ -115,7 +113,17 @@ def cmd_list(
query += '--status active --query "[].pullRequestId"'
active_pullrequest_ids = run_command(query)

with Live(build_table(work_items, workitem_prs, iteration, False), refresh_per_second=4, console=console) as live:
with Live(
build_table(
work_items=work_items,
workitem_prs=workitem_prs,
iteration=iteration,
last_build=False,
show_state=show_state,
),
refresh_per_second=4,
console=console,
) as live:

# For each PR, get linked work items. Note that "az repos pr list --include-links" does not work :(
# Posted issue on bug here: https://github.com/Azure/azure-cli-extensions/issues/2946
Expand All @@ -129,21 +137,39 @@ def cmd_list(
else:
workitem_prs[work_item] = [str(pr_id)]

live.update(build_table(work_items, workitem_prs, iteration, False))
live.update(
build_table(
work_items=work_items,
workitem_prs=workitem_prs,
iteration=iteration,
last_build=False,
show_state=show_state,
)
)

live.update(build_table(work_items, workitem_prs, iteration, last_build=True))
live.update(
build_table(
work_items=work_items,
workitem_prs=workitem_prs,
iteration=iteration,
last_build=False,
show_state=show_state,
)
)


def build_table(work_items: List, workitem_prs: Dict, iteration: str, last_build: bool = False) -> Table:
"""
Build rich table with open issues.
"""
def build_table(
work_items: List, workitem_prs: Dict, iteration: str, show_state: bool, last_build: bool = False
) -> Table:
"""Build rich table with open issues."""
# Create our table
table = Table(title=f"Work-items in current iteration {iteration}")
table.add_column("ID", justify="right", style="cyan", no_wrap=True)
table.add_column("Title", justify="left", style="cyan", no_wrap=False)
table.add_column("Assignee", justify="left", style="cyan", no_wrap=False)
table.add_column("Type", justify="left", style="cyan", no_wrap=True)
if show_state:
table.add_column("State", justify="right", style="cyan", no_wrap=True)
table.add_column("Created", justify="right", style="cyan", no_wrap=True)
table.add_column("PRs", justify="right", style="cyan", no_wrap=True)

Expand All @@ -157,6 +183,7 @@ def build_table(work_items: List, workitem_prs: Dict, iteration: str, last_build
item_title = fields.get("System.Title")
item_createdby = fields.get("System.AssignedTo", {}).get("displayName", "")
item_type = fields.get("System.WorkItemType")
item_state = fields.get("System.State")

# For example:
# '2020-11-17T13:33:32.463Z'
Expand All @@ -179,6 +206,9 @@ def build_table(work_items: List, workitem_prs: Dict, iteration: str, last_build
item_linked_prs = "[bright_black]loading..[bright_black]"

# TODO: If current git branch equal to branch ID name, different color.
table.add_row(str(item_id), item_title, item_createdby, item_type, item_datetime, item_linked_prs)
row = [str(item_id), item_title, item_createdby, item_type, item_datetime, item_linked_prs]
if show_state:
row.insert(4, item_state)
table.add_row(*row)

return table
23 changes: 15 additions & 8 deletions src/doing/list/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,16 @@
help="Output format. 'table' has a rich display, 'array' will return a string list with ID's.",
show_envvar=True,
)
def list(assignee, author, label, state, type, web, story_points, output_format):
"""
List issues related to the project.
"""
@click.option(
"--show_state/--no-show_state",
required=False,
default=True,
type=bool,
help="Show column with work item state.",
show_envvar=True,
)
def list(assignee, author, label, state, type, web, story_points, output_format, show_state):
"""List issues related to the project."""
if web:
iteration = get_config("iteration")
area = get_config("area")
Expand All @@ -101,12 +107,13 @@ def list(assignee, author, label, state, type, web, story_points, output_format)
click.launch(f"{organization}/{project}/_workitems/?_a=query&wiql={quote(query)}")
else:
cmd_list(
assignee,
author,
label,
state,
assignee=assignee,
author=author,
label=label,
state=state,
type=type,
story_points=story_points,
output_format=output_format,
show_state=show_state,
**get_common_options(),
)
78 changes: 78 additions & 0 deletions tests/test_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import pytest

from doing.list._list import build_table


@pytest.fixture
def work_items():
"""Create a mock list of work items."""
work_items = [
{
"fields": {
"System.AssignedTo": {
"descriptor": "descriptor_mock_value",
"displayName": "John Doe",
"id": "id_mock_value",
"imageUrl": "https://imageUrl.mock.value",
"uniqueName": "[email protected]",
"url": "https://url.mock.value",
},
"System.CreatedDate": "2022-01-11T12:00:00.000Z",
"System.Id": 1,
"System.State": "Active",
"System.Title": "Mock User Story work item",
"System.WorkItemType": "User Story",
},
"id": 1,
"relations": None,
"rev": 5,
"url": "https://dev.azure.com/mockProject/_apis/wit/workItems/1",
},
{
"fields": {
"System.CreatedDate": "2022-02-15T12:00:00.000Z",
"System.Id": 2,
"System.State": "New",
"System.Title": "Mock Task work item",
"System.WorkItemType": "Task",
},
"id": 2,
"relations": None,
"rev": 3,
"url": "https://dev.azure.com/mockProject/_apis/wit/workItems/2",
},
]
return work_items


@pytest.fixture
def workitem_prs():
"""Create a mock list of work items and their associated PRs."""
workitem_prs = {1: ["3"], 2: ["4"]}
return workitem_prs


def test_build_table_show_state(work_items, workitem_prs):
"""Test the show_state option of the build_table function."""
iteration = "Test Iteration"

# Generate table without state column
table = build_table(
work_items=work_items, workitem_prs=workitem_prs, iteration=iteration, last_build=False, show_state=False
)
headers = [col.header for col in table.columns]
assert "State" not in headers

# Generate table with state column
table = build_table(
work_items=work_items, workitem_prs=workitem_prs, iteration=iteration, last_build=False, show_state=True
)
headers = [col.header for col in table.columns]
assert "State" in headers

# Check if state column values are correct
state_col = [col for col in table.columns if col.header == "State"][0]
work_item_states = []
for item in work_items:
work_item_states.append(item.get("fields").get("System.State"))
assert state_col._cells == work_item_states