Change from argparse to pocoo click

This commit is contained in:
Sijmen 2018-07-21 14:36:02 +02:00
parent 37346015a0
commit 70f00be2c8
7 changed files with 83 additions and 124 deletions

View File

@ -130,13 +130,13 @@ def __print_blockers(token: str, project_id: int, story_id: int) -> None:
print(f' [{resolved}] {desc}') print(f' [{resolved}] {desc}')
def _stories_info(args: argparse.Namespace) -> None: def _stories_info(story: str) -> None:
try: try:
token = Config['user']['api_token'] token = Config['user']['api_token']
except KeyError: except KeyError:
sys.exit(1) sys.exit(1)
story_id = base32_crockford.decode(args.story) story_id = base32_crockford.decode(story)
story = api.stories.get(token, story_id) story = api.stories.get(token, story_id)
project_id = story['project_id'] project_id = story['project_id']

5
commands/cli.py Normal file
View File

@ -0,0 +1,5 @@
import click
@click.group()
def cli():
pass

View File

@ -1,16 +1,19 @@
import argparse import click
import getpass
import api.me import api.me
from config import Config from config import Config
from .cli import cli
def login(arguments: argparse.Namespace) -> None: @cli.command('login')
username = input('E-mail: ') @click.option('--email', prompt=True)
password = getpass.getpass() @click.option('--password', prompt=True, hide_input=True)
def login(email: str, password: str) -> None:
user = api.me.get(email, password)
me = api.me.get(username, password) print()
print(f"Logged in successfully as {user['name']} (@{user['username']}).")
Config['user']['api_token'] = me['api_token'] Config['user']['api_token'] = user['api_token']
Config['user']['initials'] = me['initials'] Config['user']['initials'] = user['initials']
Config.write() Config.write()

View File

@ -1,17 +1,25 @@
import argparse
import sys import sys
from typing import Dict from typing import Dict
import base32_crockford import base32_crockford
import click
import tabulate import tabulate
import api.projects import api.projects
from config import Config from config import Config
from util import require_login from util import require_login
from .cli import cli
@cli.group('projects', invoke_without_command=True)
@click.pass_context
@require_login @require_login
def list_projects(arguments: argparse.Namespace) -> None: def projects(context: click.Context) -> None:
if context.invoked_subcommand is not None:
# click calls this function when a subcommand is
# invoked as well. In this case, do nothing.
return
projects = api.projects.get(Config['user']['api_token']) projects = api.projects.get(Config['user']['api_token'])
projects.sort(key=lambda project: project['name']) projects.sort(key=lambda project: project['name'])
@ -28,24 +36,36 @@ def list_projects(arguments: argparse.Namespace) -> None:
print(tabulate.tabulate(table, headers=('Code', 'Name', 'Alias'))) print(tabulate.tabulate(table, headers=('Code', 'Name', 'Alias')))
def alias(arguments: argparse.Namespace) -> None: @projects.group('alias')
project_id = base32_crockford.decode(arguments.code) def alias() -> None:
Config['project_aliases'][arguments.alias] = str(project_id) pass
@alias.command('add')
@click.argument('code')
@click.argument('name')
def alias_add(code: str, name: str) -> None:
project_id = base32_crockford.decode(code)
Config['project_aliases'][name] = str(project_id)
Config.write() Config.write()
def rmalias(arguments: argparse.Namespace) -> None: @alias.command('rm')
del Config['project_aliases'][arguments.alias] @click.argument('name')
def alias_rm(name: str) -> None:
del Config['project_aliases'][name]
Config.write() Config.write()
def info(arguments: argparse.Namespace) -> None: @projects.command('info')
@click.argument('name')
def info(name: str) -> None:
try: try:
token = Config['user']['api_token'] token = Config['user']['api_token']
project_id = int(Config['project_aliases'][arguments.alias]) project_id = int(Config['project_aliases'][name])
except KeyError: except KeyError:
print(f'unknown alias {arguments.alias}') print(f'unknown alias {name}')
sys.exit(1) sys.exit(1)
projects = api.projects.get_project(token, project_id).items() project_info = api.projects.get_project(token, project_id)
print(tabulate.tabulate(projects)) print(tabulate.tabulate(project_info.items()))

View File

@ -1,7 +1,8 @@
import argparse import argparse
import click
from collections import defaultdict from collections import defaultdict
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Any, DefaultDict, Dict, List, Sequence, Tuple from typing import Any, DefaultDict, Dict, List, Sequence, Tuple, Optional
import base32_crockford import base32_crockford
import tabulate import tabulate
@ -13,6 +14,7 @@ from util import require_login
from . import COLOR_HEADER, _format_state, _get_persons from . import COLOR_HEADER, _format_state, _get_persons
from ._stories_info import _stories_info from ._stories_info import _stories_info
from .cli import cli
STATES = 'unstarted', 'planned', 'started', 'finished', 'delivered', 'accepted' STATES = 'unstarted', 'planned', 'started', 'finished', 'delivered', 'accepted'
@ -126,34 +128,50 @@ def __print_burndown(token: str, iteration: Dict[str, Any], persons: Persons,
print() print()
def _stories_current(arguments: argparse.Namespace) -> None: def _stories_current(project: str, scope: str, show_accepted: bool) -> None:
try: try:
project_id = int(Config['project_aliases'][arguments.project]) project_id = int(Config['project_aliases'][project])
except KeyError: except KeyError:
project_id = base32_crockford.decode(arguments.project) project_id = base32_crockford.decode(project)
token = Config['user']['api_token'] token = Config['user']['api_token']
iterations = api.projects.get_iterations( iterations = api.projects.get_iterations(
token, project_id, scope=arguments.scope) token, project_id, scope=scope)
if not iterations: if not iterations:
print('No current iteration.') print('No current iteration.')
return return
iteration = iterations[0]
persons = _get_persons(token, project_id=project_id) persons = _get_persons(token, project_id=project_id)
totals: DefaultDict[int, Dict[str, int]] = \ totals: DefaultDict[int, Dict[str, int]] = \
defaultdict(lambda: dict((state, 0) for state in STATES)) defaultdict(lambda: dict((state, 0) for state in STATES))
iteration = iterations[0]
__print_stories(iteration['stories'], persons, totals) __print_stories(iteration['stories'], persons, totals)
__print_totals(totals, persons) __print_totals(totals, persons)
__print_burndown(token, iteration, persons, arguments.hide_accepted) __print_burndown(token, iteration, persons, not show_accepted)
def _set_story_state(story: str, state: str) -> None:
token = Config['user']['api_token']
story_id = base32_crockford.decode(story)
api.stories.put_story(token, story_id, current_state=state)
@cli.command('stories')
@click.argument('project')
@click.argument('story', required=False)
@click.option('--scope', default='current')
@click.option('--show-accepted/--hide-accepted', default=True)
@click.option('--set-state', type=click.Choice([
'started', 'finished', 'delivered', 'rejected', 'accepted']))
@require_login @require_login
def stories(arguments: argparse.Namespace) -> None: def stories(project: str, story: Optional[str], scope: str,
if arguments.story: show_accepted: bool, set_state: str) -> None:
_stories_info(arguments) if story is not None:
if set_state is not None:
_set_story_state(story, set_state)
else:
_stories_info(story)
else: else:
_stories_current(arguments) _stories_current(project, scope, show_accepted)

View File

@ -1,96 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse from commands.cli import cli
import commands.login as cmd_login
import commands.projects as cmd_projects
import commands.stories as cmd_stories
import api.stories
from config import Config from config import Config
from base32_crockford import decode as b32_decode
def start_story(args) -> None:
story_set_state(args.story, 'finished')
def finish_story(args) -> None:
story_set_state(args.story, 'finished')
def deliver_story(args) -> None:
story_set_state(args.story, 'delivered')
def accept_story(args) -> None:
story_set_state(args.story, 'accepted')
def story_set_state(args, state: str) -> None:
token = Config['user']['api_token']
story_id = b32_decode(args.story)
api.stories.put_story(token, story_id, current_state=state)
def parse_arguments() -> None:
parser = argparse.ArgumentParser()
parser.set_defaults(func=lambda _: parser.print_help())
commands = parser.add_subparsers(title='commands')
login_parser = commands.add_parser('login')
login_parser.set_defaults(func=cmd_login.login)
projects_parser = commands.add_parser('projects')
projects_parser.set_defaults(func=cmd_projects.list_projects)
projects_commands = projects_parser.add_subparsers(title='commands')
projects_list_parser = projects_commands.add_parser('list')
projects_list_parser.set_defaults(func=cmd_projects.list_projects)
projects_alias_parser = projects_commands.add_parser('alias')
projects_alias_parser.add_argument('code', type=str)
projects_alias_parser.add_argument('alias', type=str)
projects_alias_parser.set_defaults(func=cmd_projects.alias)
projects_rmalias_parser = projects_commands.add_parser('rmalias')
projects_rmalias_parser.add_argument('alias', type=str)
projects_rmalias_parser.set_defaults(func=cmd_projects.rmalias)
projects_info_parser = projects_commands.add_parser('info')
projects_info_parser.add_argument('alias', type=str)
projects_info_parser.set_defaults(func=cmd_projects.info)
stories_parser = commands.add_parser('stories', description='story stuff')
stories_parser.set_defaults(func=cmd_stories.stories)
stories_parser.add_argument('project', type=str)
stories_parser.add_argument('story', type=str, nargs='?', default=None)
stories_parser.add_argument('--scope', type=str, default='current')
stories_parser.add_argument('--hide-accepted', nargs='?', type=bool,
const=True, default=False)
story_start_parser = commands.add_parser('start')
story_start_parser.set_defaults(
func=lambda args: story_set_state(args, 'started'))
story_start_parser.add_argument('story', type=str)
story_finish_parser = commands.add_parser('finish')
story_finish_parser.set_defaults(
func=lambda args: story_set_state(args, 'finished'))
story_finish_parser.add_argument('story', type=str)
story_deliver_parser = commands.add_parser('deliver')
story_deliver_parser.set_defaults(
func=lambda args: story_set_state(args, 'delivered'))
story_deliver_parser.add_argument('story', type=str)
story_accept_parser = commands.add_parser('accept')
story_accept_parser.set_defaults(
func=lambda args: story_set_state(args, 'accepted'))
story_accept_parser.add_argument('story', type=str)
args = parser.parse_args()
args.func(args)
if __name__ == '__main__': if __name__ == '__main__':
Config.read() Config.read()
parse_arguments() cli()

View File

@ -35,3 +35,4 @@ urllib3==1.22
urwid==2.0.1 urwid==2.0.1
wcwidth==0.1.7 wcwidth==0.1.7
wrapt==1.10.11 wrapt==1.10.11
click=6.7