Пример #1
0
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(), ".")
Пример #2
0
    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)
Пример #3
0
    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)
Пример #4
0
    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],
        )
Пример #5
0
    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
Пример #6
0
 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()
Пример #7
0
 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()
Пример #8
0
 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)
Пример #9
0
    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))
Пример #10
0
 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()
Пример #11
0
    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
Пример #12
0
    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
            )
Пример #13
0
    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)
Пример #14
0
    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)
Пример #15
0
    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)
Пример #16
0
    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()
Пример #17
0
    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)
Пример #18
0
 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)
Пример #19
0
    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)
Пример #20
0
    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
Пример #21
0
    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