def get_home() -> Tuple[Path, str]: # try from TD_CLI_HOME environment variable td_cli_env: Optional[str] = os.environ.get("TD_CLI_HOME") if td_cli_env: td_cli_env_dir: Path = Path.expanduser(Path(td_cli_env)) if not Path.exists(td_cli_env_dir): raise TodoException( f'TD_CLI_HOME environment variable set to "{td_cli_env_dir}", but directory does not exist' ) return (td_cli_env_dir, "") # try from XDG_CONFIG_HOME environment variable xdg_config_home: Optional[str] = os.environ.get("XDG_CONFIG_HOME") if xdg_config_home: xdg_config_home_dir: Path = Path.expanduser(Path(xdg_config_home)) if not Path.exists(xdg_config_home_dir): raise TodoException( f'XDG_CONFIG_HOME environment variable set to "{xdg_config_home}", but directory does not exist' ) config_dir = Path.joinpath(xdg_config_home_dir, "td-cli") if not config_dir.exists(): Path.mkdir(config_dir) return (config_dir, "") # fallback to home directory return (Path.home(), ".")
def _get_parser(self, args): command = self.parser.parse_known_args(args[:1])[0].command if command is None: return ListTodosParser() if command.isdigit() and len(command) <= 6: return TodoParser() parser = self._subparsers.get(command) if parser is None: if command == "list_groups": raise TodoException( "`{bold}list_groups{reset}` is deprecated, use `{bold}list-groups{reset} instead", type="DeprecatedException", ) if command == "add_group": raise TodoException( "`{bold}add_group{reset}` is deprecated, use `{bold}add-group{reset} instead", type="DeprecatedException", ) raise TodoException("Unknown command `{bold}td %s{reset}`" % " ".join(args), type="UsageError") return parser(command)
def run(self, args): try: group_name = self.service.group.add(args.name) RenderOutput("Created group {blue}{group_name}").render( group_name=group_name) except IntegrityError as e: raise TodoException( "`{bold}<Group: %s>{reset}` already exists." % args.name, e) except Error as e: raise TodoException("Error occurred, could not create a new group", e)
def run(self, args): if args.interactive: return ListInteractive(self.service).run(args) if args.group is None: group = self.service.group.get_active_group() else: group = self.service.group.get(args.group) if group is None: raise TodoException( "<Group: {name}> not found".format(name=args.group)) todos = self.service.todo.get_all(group[0], args.state) RenderOutput( "{subsequent_indent}{bold}{blue}{group_name}{reset}\n").render( subsequent_indent=" " * 4, group_name=group[0] or "global") for todo in todos: RenderOutputWithTextwrap("{completed} {bold}{todo_id}{reset}: ", "{name}").render( completed="✓" if todo[3] else "x", name=todo[1], todo_id=todo[0]) RenderOutput( "{prefix}{grey}{items} item{singular_or_plural}: {completed} completed, {uncompleted} left" ).render( prefix="\n" if group[1] > 0 else "", items=group[1], singular_or_plural=singular_or_plural(group[1]), uncompleted=group[2], completed=group[3], )
def _get_todo_or_raise(self, id): group = self.service.group.get_active_group() todo = self.service.todo.get(id, group[0]) if todo is None: raise TodoException("{bold}<Todo: %s>{reset} not found" % id) return todo
def __init__(self): self.stdscr = curses.initscr() try: self._setup_screen() except Exception as e: self._reset_screen() raise TodoException("Error occurred, could not initialize menu", e) self.color = self.Color()
def __init__(self): os.environ.setdefault("ESCDELAY", "25") self.cols, _ = get_terminal_size() self.stdscr = curses.initscr() try: self._setup_screen() except Exception as e: self._reset_screen() raise TodoException("Error occurred, could not initialize menu", e) self.color = self.Color()
def run(self, args): try: todo = self._get_todo_or_raise(args.id) self.service.todo.uncomplete(todo[0]) RenderOutput( "{bold}{red}x {reset}{todo_id}{normal}: {name}").render( todo_id=todo[0], name=todo[2]) except Error as e: raise TodoException( "Error occurred, could not uncomplete <Todo: %s>" % args.id, e)
def run(self, args): if args.group is None: group = self.service.group.get_active_group() else: group = self.service.group.get(args.group) if group is None: raise TodoException("<Group: {name}> not found".format(name=args.group)) todos = self.service.todo.get_all(group[0], args.state) RenderOutput("{count}").render(count=len(todos))
def delete(self, name): group_name = self._interpret_group_name(name) if group_name is None: raise TodoException("`{bold}<Group: %s>{reset}` can't be deleted." % GLOBAL) self.cursor.execute( """ DELETE FROM "group" WHERE name = ?; """, (group_name,), ) self.connection.commit()
def add(self, name): group_name = self._interpret_group_name(name) if group_name is None: raise TodoException("`{bold}<Group: %s>{reset}` already exists." % GLOBAL) self.cursor.execute( """ INSERT INTO "group" (name) VALUES (?); """, (group_name,), ) self.connection.commit() return group_name
def run(self, args): try: group = self._get_group_or_raise(args.name) if group[0] is None or group[0] == "global": raise TodoException( "Can't delete `{bold}<Group: global>{reset}`. It must always exist" ) if not args.skip_prompt: todo_count = group[2] + group[1] post_text = "" if todo_count > 0: RenderOutput( "By deleting group {blue}{group_name}{reset}, " "you'll also delete {bold}{todo_count}{normal} todo{singular_or_plural} in that group" ).render( group_name=args.name, todo_count=todo_count, singular_or_plural=singular_or_plural(todo_count), ) post_text = ", and {todo_count} todo{singular_or_plural}" choice = RenderInput( "[?] Are you sure you want to delete group {blue}{group_name}{reset}? [Y|n] " ).render(group_name=group[0]) if choice not in ("y", "yes", ""): return RenderOutput("Abort!").render() self.service.group.delete(group[0]) RenderOutput("{red}Deleted{reset} {bold}{group_name}{normal}" + post_text).render( group_name=group[0], todos=post_text, singular_or_plural=singular_or_plural(todo_count), todo_count=todo_count, ) except Error as e: raise TodoException( "Error occurred, could not delete `{bold}<Group: %s>{reset}`" % args.name, e )
def run(self, args): try: todo = self._get_todo_or_raise(args.id) if not args.skip_prompt: choice = RenderInput( "[?] Are you sure you want to delete todo {bold}{todo_id}{normal}? [Y|n] " ).render(todo_id=todo[0]) if choice not in ("", "y", "ye", "yes"): return RenderOutput("Abort!").render() self.service.todo.delete(todo[0]) RenderOutputWithTextwrap( "{red}Deleted{reset} {bold}{todo_id}{reset}: ", "{name}" ).render(name=todo[2], todo_id=todo[0], subsequent_indent=" " * 16) except Error as e: raise TodoException("Error occurred, could not delete <Todo: %s>" % args.id, e)
def run(self, args): try: if args.edit: details = get_user_input(config["editor"]) else: details = args.details or args.name if args.group is None: group = self.service.group.get_active_group() else: group = self.service.group.get(args.group) todo_id = self.service.todo.add(args.name, details, group[0], completed=args.state) RenderOutput("Created todo {bold}{todo_id}").render(todo_id=todo_id) except Error as e: raise TodoException("Error occurred, could not create a new todo", e)
def run(self, args): try: todo = self._get_todo_or_raise(args.id) RenderOutput( "{subsequent_indent}{bold}{blue}{group_name}{reset}\n").render( group_name=todo[1] or "UNGROUPED", subsequent_indent=" " * 4) RenderOutput("{details}").render(details=todo[3]) RenderOutputWithTextwrap( "\n{grey}{completed} {bold}{todo_id}{normal}: ", "{details}").render(details=todo[2], completed="✓" if todo[4] else "x", todo_id=todo[0]) except Error as e: raise TodoException( "Error occurred, could not get {bold}<Todo: %s>{reset}" % args.id, e)
def use(self, name): group = self.get(name) if group is None: raise TodoException("<Group: {name}> not found".format(name=name)) self.cursor.execute(""" UPDATE "group" SET in_use = 0 """) self.cursor.execute( """ UPDATE "group" SET in_use = 1 WHERE name = ?; """, (group[0], ), ) self.connection.commit()
def run(self, args): try: todo = self._get_todo_or_raise(args.id) if not (args.name or args.details or args.group): details = get_user_input(config["editor"], str.encode(todo[3])) self.service.todo.edit_details(todo[0], details) else: if args.group: group = self._get_group_or_raise(args.group) self.service.todo.set_group(todo[0], group[0]) if args.name: self.service.todo.edit_name(todo[0], args.name) if args.details: self.service.todo.edit_details(todo[0], args.details) RenderOutput("Edited {bold}{todo_id}{reset}: {name}").render( todo_id=todo[0], name=args.name or todo[2]) except Error as e: raise TodoException( "Error occurred, could not edit <Todo: %s>" % args.id, e)
def get_active_group(self): if config["group"]: group = self.get(config["group"]) if group is None: raise TodoException( "{bold}<Group: %s>{reset} does not exist, falling back to currently active group" % config["group"], "Your config file at {bold}%s{reset} tries to\noverride the " % (get_project_config() or "~") + "default group with `{bold}%s{reset}`, but " % config["group"] + "{bold}<Group: %s>{reset} does not exist." % config["group"], "WARNING", ) return group self.cursor.execute(""" SELECT name FROM "group" WHERE in_use = 1; """) active_group = self.cursor.fetchone() or (None, ) return self.get(*active_group)
def run(self, args): # noqa: C901 try: from todo.utils.menu import Menu except ImportError as e: raise TodoException( "Sorry! The interactive mode is not supported by your system", e) if args.group is None: group = self.service.group.get_active_group() else: group = self.service.group.get(args.group) if group is None: raise TodoException( "<Group: {name}> not found".format(name=args.group)) todos = self.service.todo.get_all(group[0], args.state) with Menu() as menu: menu.clear() tracker = VerticalTracker(todos, group) while True: menu.refresh() menu.render_header("{group_name}".format( group_name=tracker.group.name or "global")) menu.render_subheader( "{items} item{singular_or_plural}: {completed} completed, {uncompleted} left" .format( items=tracker.group.items, singular_or_plural=singular_or_plural( tracker.group.items), completed=tracker.group.completed, uncompleted=tracker.group.uncompleted, )) self._render_todos(menu, tracker) mode = self._get_mode(tracker) if mode == COMMAND_MODES.EMPTY: menu.render_commands(tracker.commands_offset, mode=COMMAND_MODES.EMPTY) elif mode == COMMAND_MODES.DELETE: menu.render_commands(tracker.commands_offset, mode=COMMAND_MODES.DELETE) else: menu.render_commands(tracker.commands_offset) command = self._interpret_command(menu.get_command(), mode) if command == COMMANDS.DOWN: tracker.move_down() elif command == COMMANDS.UP: tracker.move_up() elif command == COMMANDS.RECOVER: tracker.recover() elif command == COMMANDS.TOGGLE: tracker.toggle(self.service.todo) elif command == COMMANDS.ADD: # add empty line tracker.add(("??????", "", "", None)) # rerender todos self._render_todos(menu, tracker) # render add commands menu.render_commands(tracker.commands_offset, mode=COMMAND_MODES.ADD) new_todo_name = menu.edit_text("", tracker.index) if new_todo_name is not None: tracker.update(new_todo_name, self.service.todo) else: tracker.remove() menu.clear() elif command == COMMANDS.EDIT: menu.render_commands(tracker.commands_offset, mode=COMMAND_MODES.EDIT) new_todo_name = menu.edit_text(tracker.current_todo.name, tracker.index) tracker.edit(new_todo_name, self.service.todo) elif command == COMMANDS.DELETE: tracker.mark_deleted() elif command == COMMANDS.QUIT: break tracker.delete_todos(self.service.todo)
def _get_group_or_raise(self, name): group = self.service.group.get(name) if group is None and name != "global": raise TodoException("<Group: {name}> not found".format(name=name)) return group
def _get_todo_or_raise(self, id): todo = self.service.todo.get(id) if todo is None: raise TodoException("{bold}<Todo: %s>{reset} not found" % id) return todo