phable-cli 0.1.10__tar.gz → 0.1.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {phable_cli-0.1.10 → phable_cli-0.1.11}/PKG-INFO +1 -1
  2. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/assign.py +6 -2
  3. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/list.py +4 -4
  4. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/report.py +3 -3
  5. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/show.py +2 -2
  6. phable_cli-0.1.11/phable/display.py +127 -0
  7. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/phabricator.py +0 -3
  8. {phable_cli-0.1.10 → phable_cli-0.1.11}/pyproject.toml +1 -1
  9. phable_cli-0.1.10/phable/display.py +0 -68
  10. {phable_cli-0.1.10 → phable_cli-0.1.11}/LICENSE +0 -0
  11. {phable_cli-0.1.10 → phable_cli-0.1.11}/README.md +0 -0
  12. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cache.py +0 -0
  13. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/__init__.py +0 -0
  14. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/cache.py +0 -0
  15. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/comment.py +0 -0
  16. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/config.py +0 -0
  17. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/create.py +0 -0
  18. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/main.py +0 -0
  19. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/move.py +0 -0
  20. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/subscribe.py +0 -0
  21. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/cli/utils.py +0 -0
  22. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/config.py +0 -0
  23. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/tests/conftest.py +0 -0
  24. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/tests/fixtures/simple_task.json +0 -0
  25. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/tests/phabricator_test.py +0 -0
  26. {phable_cli-0.1.10 → phable_cli-0.1.11}/phable/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: phable-cli
3
- Version: 0.1.10
3
+ Version: 0.1.11
4
4
  Summary: Manage Phabricator tasks from the comfort of your terminal
5
5
  License: MIT
6
6
  Author: Balthazar Rouberol
@@ -25,8 +25,12 @@ def assign_task(
25
25
 
26
26
  \b
27
27
  Examples:
28
- $ phable assign T123456 # self assign task
29
- $ phable assign T123456 brouberol # asign to username
28
+ \b
29
+ # self assign task
30
+ $ phable assign T123456
31
+ \b
32
+ # asign to username
33
+ $ phable assign T123456 --usernamme brouberol
30
34
 
31
35
  """
32
36
  if not username:
@@ -3,7 +3,7 @@ from typing import Optional
3
3
 
4
4
  from phable.phabricator import PhabricatorClient
5
5
  from phable.config import config
6
- from phable.display import display_tasks
6
+ from phable.display import display_tasks, TaskFormat
7
7
 
8
8
 
9
9
  @click.command(name="list")
@@ -30,7 +30,7 @@ from phable.display import display_tasks
30
30
  @click.option(
31
31
  "--format",
32
32
  required=False,
33
- type=click.Choice(("html", "plain", "json", "markdown")),
33
+ type=click.Choice(TaskFormat, case_sensitive=False),
34
34
  default="plain",
35
35
  help="The output format of the task list",
36
36
  )
@@ -42,7 +42,7 @@ def list_tasks(
42
42
  columns: list[str],
43
43
  owner: Optional[str] = None,
44
44
  milestone: bool = False,
45
- format: str = "plain",
45
+ format: TaskFormat = TaskFormat.PLAIN,
46
46
  ):
47
47
  """Lists and filter tasks
48
48
 
@@ -78,4 +78,4 @@ def list_tasks(
78
78
  column_phids=column_phids, owner_phid=owner_user, project_phid=project_phid
79
79
  )
80
80
  tasks = [client.enrich_task(task) for task in tasks]
81
- display_tasks(tasks=tasks, format=format, separator="")
81
+ display_tasks(tasks=tasks, format=format)
@@ -1,7 +1,7 @@
1
1
  import click
2
2
  from phable.config import config
3
3
  from phable.phabricator import PhabricatorClient
4
- from phable.display import display_tasks
4
+ from phable.display import display_tasks, TaskFormat
5
5
 
6
6
 
7
7
  @click.command(name="report-done-tasks")
@@ -15,7 +15,7 @@ from phable.display import display_tasks
15
15
  )
16
16
  @click.option(
17
17
  "--format",
18
- type=click.Choice(("plain", "json")),
18
+ type=click.Choice(TaskFormat, case_sensitive=False),
19
19
  default="plain",
20
20
  help="Output format",
21
21
  )
@@ -51,7 +51,7 @@ def report_done_tasks(
51
51
  column_destination_phid = client.find_column_in_project(
52
52
  target_project_phid, destination
53
53
  )
54
- tasks = client.find_tasks_in_column(column_source_phid)
54
+ tasks = client.find_tasks(column_phids=[column_source_phid])
55
55
 
56
56
  enriched_tasks = []
57
57
  for task in tasks:
@@ -1,13 +1,13 @@
1
1
  import click
2
2
  from phable.utils import Task
3
3
  from phable.phabricator import PhabricatorClient
4
- from phable.display import display_task
4
+ from phable.display import display_task, TaskFormat
5
5
 
6
6
 
7
7
  @click.command(name="show")
8
8
  @click.option(
9
9
  "--format",
10
- type=click.Choice(("plain", "json")),
10
+ type=click.Choice(TaskFormat, case_sensitive=False),
11
11
  default="plain",
12
12
  help="Output format",
13
13
  )
@@ -0,0 +1,127 @@
1
+ import json
2
+ from collections.abc import Callable
3
+ from enum import StrEnum, auto
4
+
5
+ from .utils import Task
6
+
7
+
8
+ class TaskFormat(StrEnum):
9
+ PLAIN = auto()
10
+ JSON = auto()
11
+ HTML = auto()
12
+ MARKDOWN = auto()
13
+ WIKITEXT = auto()
14
+
15
+
16
+ def display_tasks(
17
+ tasks: list[dict],
18
+ format: TaskFormat,
19
+ ) -> None:
20
+ if len(tasks) == 1:
21
+ display_task(tasks[0], format=format)
22
+ get_printer(format).print_list(tasks)
23
+
24
+
25
+ def display_task(task: dict, format: TaskFormat) -> None:
26
+ get_printer(format).print(task)
27
+
28
+
29
+ class TaskPrinter:
30
+ def __init__(self, printer: Callable):
31
+ self._printer = printer
32
+
33
+ def print(self, task: dict) -> None:
34
+ pass
35
+
36
+ def print_list(self, tasks: list[dict]) -> None:
37
+ pass
38
+
39
+ def title(self, task: dict) -> str:
40
+ return f"{Task.from_int(task['id'])} {task['fields']['name']}"
41
+
42
+ def status(self, task: dict) -> str:
43
+ return f"({task['fields']['status']['name']})"
44
+
45
+
46
+ class JsonTaskPrinter(TaskPrinter):
47
+ def print(self, task: dict) -> None:
48
+ self._printer(json.dumps(task, indent=2))
49
+
50
+ def print_list(self, tasks: list[dict]) -> None:
51
+ self._printer(json.dumps(tasks, indent=2))
52
+
53
+
54
+ class MarkdownTaskPrinter(TaskPrinter):
55
+ def print(self, task: dict) -> None:
56
+ self._printer(f"* [{self.title(task)}]({task['url']}) {self.status(task)}")
57
+
58
+ def print_list(self, tasks: list[dict]) -> None:
59
+ for task in tasks:
60
+ self.print(task)
61
+
62
+
63
+ class WikitextTaskPrinter(TaskPrinter):
64
+ def print(self, task: dict) -> None:
65
+ self._printer(f"* [{task['url']} {self.title(task)}] {self.status(task)}")
66
+
67
+ def print_list(self, tasks: list[dict]) -> None:
68
+ for task in tasks:
69
+ self.print(task)
70
+
71
+
72
+ class HtmlTaskPrinter(TaskPrinter):
73
+ def print(self, task: dict) -> None:
74
+ self._printer(
75
+ f"<a href={task['url']}>{self.title(task)}</a> {self.status(task)}"
76
+ )
77
+
78
+ def print_list(self, tasks: list[dict]) -> None:
79
+ for task in tasks:
80
+ self._printer(
81
+ f"<li><a href={task['url']}>{self.title(task)}</a> {self.status(task)}</li>"
82
+ )
83
+
84
+
85
+ class PlainTaskPrinter(TaskPrinter):
86
+ def print(self, task: dict) -> None:
87
+ parent_str = self.title(task.get("parent")) if task.get("parent") else ""
88
+ print(f"URL: {task['url']}")
89
+ print(f"Task: {Task.from_int(task['id'])}")
90
+ print(f"Title: {task['fields']['name']}")
91
+ if task.get("author"):
92
+ print(f"Author: {task['author']['fields']['username']}")
93
+ if task.get("owner"):
94
+ print(f"Owner: {task['owner']}")
95
+ if task.get("tags"):
96
+ print(f"Tags: {', '.join(task['tags'])}")
97
+ print(f"Status: {task['fields']['status']['name']}")
98
+ print(f"Priority: {task['fields']['priority']['name']}")
99
+ print(f"Description: {task['fields']['description']['raw']}")
100
+ print(f"Parent: {parent_str}")
101
+ print("Subtasks:")
102
+ if task.get("subtasks"):
103
+ for subtask in task["subtasks"]:
104
+ status = f"{'[x]' if subtask['fields']['status']['value'] == 'resolved' else '[ ]'}"
105
+ print(
106
+ f"{status} - {Task.from_int(subtask['id'])} - @{subtask['owner']:<10} - {subtask['fields']['name']}"
107
+ )
108
+
109
+ def print_list(self, tasks: list[dict]) -> None:
110
+ for task in tasks:
111
+ self.print(task)
112
+ self._printer("=" * 50)
113
+
114
+
115
+ def get_printer(format: TaskFormat) -> TaskPrinter:
116
+ if format == TaskFormat.PLAIN:
117
+ return PlainTaskPrinter(print)
118
+ elif format == TaskFormat.JSON:
119
+ return JsonTaskPrinter(print)
120
+ elif format == TaskFormat.HTML:
121
+ return HtmlTaskPrinter(print)
122
+ elif format == TaskFormat.MARKDOWN:
123
+ return MarkdownTaskPrinter(print)
124
+ elif format == TaskFormat.WIKITEXT:
125
+ return WikitextTaskPrinter(print)
126
+ else:
127
+ raise ValueError(f"Unknown format: {format}")
@@ -163,9 +163,6 @@ class PhabricatorClient:
163
163
  parent = self.find_parent_task(subtask_id=task["id"])
164
164
  task["parent"] = parent
165
165
 
166
- def find_tasks_in_column(self, column_phid: str) -> list[dict[str, Any]]:
167
- return self.find_tasks(column_phid=column_phid)
168
-
169
166
  def find_tasks(
170
167
  self,
171
168
  column_phids: Optional[list[str]] = None,
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "phable-cli"
3
- version = "0.1.10"
3
+ version = "0.1.11"
4
4
  description = "Manage Phabricator tasks from the comfort of your terminal"
5
5
  authors = ["Balthazar Rouberol <br@imap.cc>"]
6
6
  license = "MIT"
@@ -1,68 +0,0 @@
1
- import json
2
- from typing import Literal, TypeAlias
3
-
4
- from .utils import Task
5
-
6
- TaskFormat: TypeAlias = Literal["plain", "json", "html", "markdown"]
7
-
8
-
9
- def display_task(task: dict, format: TaskFormat, prefix: str = "", end: str = "\n"):
10
- title = f"{Task.from_int(task['id'])} {task['fields']['name']} ({task['fields']['status']['name']})"
11
-
12
- if format == "json":
13
- print(json.dumps(task, indent=2))
14
- elif format == "html":
15
- print(
16
- f"{prefix}<a href={task['url']}>{title}</a>",
17
- end=end,
18
- )
19
- elif format == "markdown":
20
- print(
21
- f"{prefix}[{title}]({task['url']})",
22
- end=end,
23
- )
24
- else:
25
- parent_str = (
26
- f"{Task.from_int(task['parent']['id'])} {task['parent']['fields']['name']}"
27
- if task.get("parent")
28
- else ""
29
- )
30
- print(f"URL: {task['url']}")
31
- print(f"Task: {Task.from_int(task['id'])}")
32
- print(f"Title: {task['fields']['name']}")
33
- if task.get("author"):
34
- print(f"Author: {task['author']['fields']['username']}")
35
- if task.get("owner"):
36
- print(f"Owner: {task['owner']}")
37
- if task.get("tags"):
38
- print(f"Tags: {', '.join(task['tags'])}")
39
- print(f"Status: {task['fields']['status']['name']}")
40
- print(f"Priority: {task['fields']['priority']['name']}")
41
- print(f"Description: {task['fields']['description']['raw']}")
42
- print(f"Parent: {parent_str}")
43
- print("Subtasks:")
44
- if task.get("subtasks"):
45
- for subtask in task["subtasks"]:
46
- status = f"{'[x]' if subtask['fields']['status']['value'] == 'resolved' else '[ ]'}"
47
- print(
48
- f"{status} - {Task.from_int(subtask['id'])} - @{subtask['owner']:<10} - {subtask['fields']['name']}"
49
- )
50
-
51
-
52
- def display_tasks(
53
- tasks: list[dict],
54
- format: TaskFormat,
55
- separator: str = "=" * 50,
56
- ):
57
- if len(tasks) == 1:
58
- return display_task(tasks[0], format=format)
59
-
60
- if format == "json":
61
- print(json.dumps(tasks, indent=2))
62
- elif format == "markdown":
63
- for task in tasks:
64
- display_task(task, format=format, prefix="* ")
65
- else:
66
- for task in tasks:
67
- display_task(task, format=format, prefix="<li>", end="")
68
- print(separator)
File without changes
File without changes
File without changes
File without changes