2018-04-03 10:56:07 +00:00
|
|
|
import re
|
|
|
|
import sys
|
|
|
|
from datetime import datetime
|
|
|
|
from typing import Any, Dict
|
2019-09-04 14:42:23 +00:00
|
|
|
from consolemd import Renderer
|
2018-04-03 10:56:07 +00:00
|
|
|
|
2018-07-22 09:45:15 +00:00
|
|
|
import base32_crockford as base32
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
import api.stories
|
|
|
|
from config import Config
|
|
|
|
from util import print_wrap
|
2019-09-04 14:42:23 +00:00
|
|
|
from . import COLOR_HEADER, COLOR_TITLE, COLOR_WHITE, _format_state, _get_persons
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
|
2018-07-22 09:45:15 +00:00
|
|
|
def __print_story(story: Dict[str, Any]) -> None:
|
2018-04-03 10:56:07 +00:00
|
|
|
"""
|
|
|
|
Prints the title, the current state and the estimate of the story, if
|
|
|
|
available.
|
|
|
|
|
|
|
|
TODO: Split up in functions.
|
|
|
|
"""
|
2019-09-04 14:42:23 +00:00
|
|
|
COLOR_TITLE.print(story["name"])
|
|
|
|
print(story["url"], end="\n\n")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
if "current_state" in story:
|
|
|
|
state = _format_state(story["current_state"])
|
|
|
|
COLOR_HEADER.print("State:", state, end="")
|
2018-07-22 09:45:15 +00:00
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
if story["current_state"] == "accepted":
|
|
|
|
print(f" (at {story['accepted_at']})", end="")
|
2018-07-22 09:45:15 +00:00
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
print(end="\n\n")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
if "estimate" in story:
|
|
|
|
COLOR_HEADER.print("Estimate: ", end="")
|
|
|
|
print(story["estimate"], "points", end="")
|
|
|
|
if len(story.get("owner_ids", [])) > 1:
|
|
|
|
points = story["estimate"] / len(story["owner_ids"])
|
|
|
|
print(f" ({points} each)", end="")
|
|
|
|
print(end="\n\n")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
def __print_owners(story: Dict[str, Any], persons: Dict[int, Any]) -> None:
|
|
|
|
"""Prints the owners of the story, if available."""
|
2019-09-04 14:42:23 +00:00
|
|
|
if story.get("owner_ids"):
|
|
|
|
COLOR_HEADER.print("Owners:")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
owners = []
|
2019-09-04 14:42:23 +00:00
|
|
|
for owner_id in story["owner_ids"]:
|
|
|
|
name = persons[owner_id]["name"]
|
|
|
|
initials = persons[owner_id]["initials"]
|
|
|
|
owners.append(f" - {name} ({initials})")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
print("\n".join(owners), end="\n\n")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
def __print_description(story: Dict[str, Any]) -> None:
|
|
|
|
"""Prints the description of the story, if available."""
|
2019-09-04 14:42:23 +00:00
|
|
|
COLOR_HEADER.print("Description:")
|
|
|
|
if "description" in story:
|
|
|
|
description = story["description"].strip()
|
|
|
|
Renderer().render(description, width=80)
|
|
|
|
print()
|
2018-04-03 10:56:07 +00:00
|
|
|
else:
|
2019-09-04 14:42:23 +00:00
|
|
|
print(" (No description)", end="\n\n")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
def __print_labels(story: Dict[str, Any]) -> None:
|
|
|
|
"""Prints the labels of the story, if available."""
|
2019-09-04 14:42:23 +00:00
|
|
|
if not story.get("labels"):
|
2018-04-03 10:56:07 +00:00
|
|
|
return
|
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
COLOR_HEADER.print("Labels:")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
template = "\033[97;48;5;22m {} \033[0m"
|
|
|
|
labels = " ".join(template.format(label["name"]) for label in story["labels"])
|
2018-04-03 10:56:07 +00:00
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
print(f" {labels}", end="\n\n")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
|
2019-02-05 15:10:31 +00:00
|
|
|
def __print_tasks(project_id: int, story_id: int) -> None:
|
2018-04-03 10:56:07 +00:00
|
|
|
"""Prints the tasks of the story, if available."""
|
2019-02-05 15:10:31 +00:00
|
|
|
tasks = api.stories.get_tasks(project_id, story_id)
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
if tasks:
|
2019-09-04 14:42:23 +00:00
|
|
|
COLOR_HEADER.print("Tasks:")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
for task in tasks:
|
2019-09-04 14:42:23 +00:00
|
|
|
print(end=" ")
|
|
|
|
print("[X]" if task["complete"] else "[ ]", end=" \033[34m")
|
|
|
|
print(base32.encode(task["id"]), end=":\033[0m ")
|
|
|
|
print(task["description"])
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
def __print_comments(
|
|
|
|
project_id: int, story_id: int, persons: Dict[int, Dict[str, Any]]
|
|
|
|
) -> None:
|
2018-04-03 10:56:07 +00:00
|
|
|
"""Prints the comments on the story, if available."""
|
2019-02-05 15:10:31 +00:00
|
|
|
comments = api.stories.get_comments(project_id, story_id)
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
if comments:
|
2019-09-04 14:42:23 +00:00
|
|
|
COLOR_HEADER.print("Comments:")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
for comment in comments:
|
2019-09-04 14:42:23 +00:00
|
|
|
text = comment.get("text", "[Empty comment]").strip()
|
|
|
|
print_wrap(text, indent=" ")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
person_id = comment["person_id"]
|
|
|
|
name = persons[person_id]["name"]
|
|
|
|
COLOR_WHITE.print(" -", name, end=" ")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
date = datetime.strptime(comment["created_at"], "%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
date_str = date.strftime("on %a %Y-%m-%d at %H:%M")
|
|
|
|
print(date_str, end="\n\n")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
|
2019-02-05 15:10:31 +00:00
|
|
|
def __print_blockers(project_id: int, story_id: int) -> None:
|
2018-04-03 10:56:07 +00:00
|
|
|
"""Prints the stories that block this story, if available."""
|
2019-02-05 15:10:31 +00:00
|
|
|
blockers = api.stories.get_blockers(project_id, story_id)
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
if blockers:
|
2019-09-04 14:42:23 +00:00
|
|
|
COLOR_HEADER.print("Blockers:")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
def blocker_repl(matchgroup: Any) -> str:
|
|
|
|
id = int(matchgroup.group(1))
|
2018-07-22 09:45:15 +00:00
|
|
|
code = base32.encode(id)
|
2018-04-03 10:56:07 +00:00
|
|
|
return COLOR_HEADER.format(code)
|
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
pattern = re.compile(r"#(\d+)")
|
2018-04-03 10:56:07 +00:00
|
|
|
for blocker in blockers:
|
2019-09-04 14:42:23 +00:00
|
|
|
resolved = "X" if blocker["resolved"] else " "
|
|
|
|
desc = pattern.sub(blocker_repl, blocker["description"])
|
|
|
|
print(f" [{resolved}] {desc}")
|
2018-04-03 10:56:07 +00:00
|
|
|
|
|
|
|
|
2019-02-05 13:29:33 +00:00
|
|
|
def stories_info(story_b32: str) -> None:
|
|
|
|
story_id = base32.decode(story_b32)
|
2019-02-05 15:10:31 +00:00
|
|
|
story = api.stories.get(story_id)
|
2018-04-03 10:56:07 +00:00
|
|
|
|
2019-09-04 14:42:23 +00:00
|
|
|
project_id = story["project_id"]
|
2019-02-05 15:10:31 +00:00
|
|
|
persons = _get_persons(project_id)
|
2018-04-03 10:56:07 +00:00
|
|
|
|
2018-07-22 09:45:15 +00:00
|
|
|
__print_story(story)
|
2018-04-03 10:56:07 +00:00
|
|
|
__print_owners(story, persons)
|
|
|
|
__print_description(story)
|
|
|
|
__print_labels(story)
|
2019-02-05 15:10:31 +00:00
|
|
|
__print_tasks(project_id, story_id)
|
|
|
|
__print_comments(project_id, story_id, persons)
|
|
|
|
__print_blockers(project_id, story_id)
|