Exemple #1
0
    def setUp(self):
        self.taskrc_path = "tests/test_data/.taskrc"
        self.task_dir_path = "tests/test_data/.task"
        self.assertEqual(os.path.isdir(self.taskrc_path), False)
        self.assertEqual(os.path.isdir(self.task_dir_path), False)

        # Create a sample .taskrc
        with open(self.taskrc_path, "w+") as file:
            file.write("# User Defined Attributes\n")
            file.write("uda.estimate.type=duration\n")
            file.write("uda.estimate.label=Est\n")
            file.write("# User Defined Attributes\n")
            file.write("uda.tb_estimate.type=numeric\n")
            file.write("uda.tb_estimate.label=Est\n")
            file.write("uda.tb_real.type=numeric\n")
            file.write("uda.tb_real.label=Real\n")

        # Create a sample empty .task directory
        os.makedirs(self.task_dir_path)

        self.screen = Screen(
            tw_data_dir=self.task_dir_path,
            taskrc_location=self.taskrc_path,
            scheduled="today",
        )
Exemple #2
0
    def test_prerender_task(self, screen: Screen):
        task = screen.schedule.tasks[0]
        task_buffer = screen.prerender_task(0, task, False, 11, 0, "2019-12-08")
        assert task_buffer[0][1] == 0
        assert "11" in task_buffer[0][2]

        # Glyph column
        assert task_buffer[1][1] == 5
        assert "  " in task_buffer[1][2]

        # ID column
        assert task_buffer[3][1] == 5
        assert "1" in task_buffer[3][2]

        # Time column
        assert task_buffer[4][1] == 7
        assert "00:20" in task_buffer[4][2]

        # Project column
        assert task_buffer[5][1] == 29
        assert task_buffer[5][2] == ""

        # Description column
        assert task_buffer[6][1] == 37
        assert "test_last_week" in task_buffer[6][2]
Exemple #3
0
    def main(self):
        """Initialize the screen and notifier, and start the main loop of
           the interface."""

        if self.show_notifications:
            self.notifier = Notifier(self.backend)
        else:
            self.notifier = None

        self.screen = Screen(
            self.schedule,
            scheduled_after=self.scheduled_after,
            scheduled_before=self.scheduled_before,
            hide_empty=self.hide_empty,
            hide_projects=self.hide_projects,
        )

        try:
            self.run()
        except TaskDirDoesNotExistError as err:
            print("Error: {}".format(err))
            sys.exit(1)
        except TaskrcDoesNotExistError as err:
            print("Error: {}".format(err))
            sys.exit(1)
        except KeyboardInterrupt:
            self.screen.close()
        except ValueError as err:
            self.screen.close()
            print("Error: {}".format(err))
            sys.exit(1)
        except UDADoesNotExistError as err:
            self.screen.close()
            print("Error: {}".format(err))
            sys.exit(1)
        except SoundDoesNotExistError as err:
            self.screen.close()
            print("Error: {}".format(err))
            sys.exit(1)
        else:
            try:
                self.screen.close()
            except curses_error as err:
                print(err.with_traceback)
Exemple #4
0
 def test_predender_divider(self, screen: Screen):
     divider_buffer = screen.prerender_divider("2019-12-07", 0)
     assert "──────" in divider_buffer[0][2]
     assert "Sat 07 Dec 2019" in divider_buffer[1][2]
     assert divider_buffer[1][1] == 6
     assert (
         "─────────────────────────────────────────────────────────"
         in divider_buffer[2][2]
     )
     assert divider_buffer[2][1] == 23
Exemple #5
0
 def test_prerender_buffer(self, screen: Screen):
     header_buffer = screen.prerender_headers()
     assert "ID" in header_buffer[0][2]
     assert header_buffer[0][1] == 5
     assert "Time" in header_buffer[1][2]
     assert header_buffer[1][1] == 7
     assert "Timeboxes" in header_buffer[2][2]
     assert header_buffer[2][1] == 19
     assert "Project" in header_buffer[3][2]
     assert header_buffer[3][1] == 29
     assert "Description" in header_buffer[4][2]
     assert header_buffer[4][1] == 37
Exemple #6
0
class ScreenTest(unittest.TestCase):
    def setUp(self):
        self.taskrc_path = "tests/test_data/.taskrc"
        self.task_dir_path = "tests/test_data/.task"
        self.assertEqual(os.path.isdir(self.taskrc_path), False)
        self.assertEqual(os.path.isdir(self.task_dir_path), False)

        # Create a sample .taskrc
        with open(self.taskrc_path, "w+") as file:
            file.write("# User Defined Attributes\n")
            file.write("uda.estimate.type=duration\n")
            file.write("uda.estimate.label=Est\n")
            file.write("# User Defined Attributes\n")
            file.write("uda.tb_estimate.type=numeric\n")
            file.write("uda.tb_estimate.label=Est\n")
            file.write("uda.tb_real.type=numeric\n")
            file.write("uda.tb_real.label=Real\n")

        # Create a sample empty .task directory
        os.makedirs(self.task_dir_path)

        self.screen = Screen(
            tw_data_dir=self.task_dir_path,
            taskrc_location=self.taskrc_path,
            scheduled="today",
        )

    def tearDown(self):
        try:
            os.remove(self.taskrc_path)
        except FileNotFoundError:
            pass

        try:
            shutil.rmtree(self.task_dir_path)
        except FileNotFoundError:
            pass

        # Attempt to gracefully quit curses mode if it is active
        # to prevent messing up terminal
        try:
            self.screen.close()
        except:
            try:
                curses.endwin()
            except:
                pass

    # TODO Move to /tests/functional/
    # def test_screen_refresh_buffer_hide_empty_lines(self):
    #     self.assertEqual(self.screen.buffer, [])

    #     taskwarrior = TaskWarrior(
    #         data_location=self.task_dir_path,
    #         create=True,
    #         taskrc_location=self.taskrc_path)
    #     Task(taskwarrior, description='test_yesterday',
    #          schedule='yesterday', estimate='20min').save()
    #     Task(taskwarrior, description='test_9:00_to_10:11',
    #          schedule='today+9hr', estimate='71min', project='test').save()

    #     self.screen.hide_empty = False
    #     self.screen.refresh_buffer()
    #     self.assertEqual(len(self.screen.buffer), 61)

    # TODO Move to /tests/functional/
    # def test_screen_refresh_buffer_first_time_fills_buffer(self):
    #     self.assertEqual(self.screen.buffer, [])

    #     taskwarrior = TaskWarrior(
    #         data_location=self.task_dir_path,
    #         create=True,
    #         taskrc_location=self.taskrc_path)
    #     Task(taskwarrior, description='test_yesterday',
    #          schedule='yesterday', estimate='20min').save()
    #     Task(taskwarrior, description='test_9:00_to_10:11',
    #          schedule='today+9hr', estimate='71min', project='test').save()

    #     self.screen.refresh_buffer()
    #     self.assertEqual(len(self.screen.buffer), 15)

    # TODO Move to /tests/functional/
    # def test_screen_refresh_buffer_no_tasks(self):
    #     self.assertEqual(self.screen.buffer, [])
    #     self.screen.refresh_buffer()
    #     self.assertEqual(self.screen.buffer, [])

    def test_screen_draw_no_tasks_to_display(self):
        self.screen.draw()

    def test_screen_draw(self):
        taskwarrior = TaskWarrior(
            data_location=self.task_dir_path,
            create=True,
            taskrc_location=self.taskrc_path,
        )
        Task(
            taskwarrior,
            description="test_yesterday",
            schedule="yesterday",
            estimate="20min",
        ).save()
        Task(
            taskwarrior,
            description="test_9:00_to_10:11",
            schedule="today+9hr",
            estimate="71min",
            project="test",
        ).save()

        self.screen.draw()
        self.screen.refresh_buffer()
        Task(
            taskwarrior,
            description="test_14:00_to_16:00",
            schedule="today+14hr",
            estimate="2hr",
        ).save()
        time.sleep(0.1)
        self.screen.draw()
        self.screen.refresh_buffer()
        Task(
            taskwarrior,
            description="test_tomorrow",
            schedule="tomorrow",
            estimate="24min",
        ).save()
        time.sleep(0.1)
        self.screen.draw()
        self.screen.refresh_buffer()

    def test_screen_scroll_up_at_top_is_blocked(self):
        current_scroll_level = self.screen.scroll_level
        self.screen.scroll(-1)
        self.assertEqual(current_scroll_level, self.screen.scroll_level)

    def test_screen_scroll_down_and_up(self):
        current_scroll_level = self.screen.scroll_level
        self.screen.scroll(1)
        self.assertEqual(current_scroll_level + 1, self.screen.scroll_level)

        current_scroll_level = self.screen.scroll_level
        self.screen.scroll(-1)
        self.assertEqual(current_scroll_level - 1, self.screen.scroll_level)
Exemple #7
0
def main(argv):
    """Display a schedule report for taskwarrior."""
    parser = argparse.ArgumentParser(
        description="""Display a schedule report for taskwarrior.""")
    parser.add_argument('-r',
                        '--refresh',
                        help="refresh every n seconds",
                        type=int,
                        default=1)
    parser.add_argument('--from',
                        help="scheduled from date: ex. 'today', 'tomorrow'",
                        type=str,
                        dest='after')
    parser.add_argument('--until',
                        help="scheduled until date: ex. 'today', 'tomorrow'",
                        type=str,
                        dest='before')
    parser.add_argument('-s',
                        '--scheduled',
                        help="""scheduled date: ex. 'today', 'tomorrow'
                (overrides --from and --until)""",
                        type=str)
    parser.add_argument('-a',
                        '--all',
                        help="show all hours, even if empty",
                        action='store_true',
                        default=False)
    parser.add_argument('-c',
                        '--completed',
                        help="hide completed tasks",
                        action='store_false',
                        default=True)
    parser.add_argument('-p',
                        '--project',
                        help="hide project column",
                        action='store_true',
                        default=False)
    args = parser.parse_args(argv)

    hide_empty = not args.all

    if args.scheduled:
        if args.before or args.after:
            print('Error: The --scheduled option can not be used together '
                  'with --until and/or --from.')
            sys.exit(1)
    else:
        if args.before and not args.after or not args.before and args.after:
            print('Error: Either both --until and --from or neither options '
                  'must be used.')
            sys.exit(1)

        if not args.before and not args.after:
            args.scheduled = 'today'
        elif not args.before:
            args.before = 'tomorrow'
        elif not args.after:
            args.after = 'yesterday'

    screen = Screen(hide_empty=hide_empty,
                    scheduled_before=args.before,
                    scheduled_after=args.after,
                    scheduled=args.scheduled,
                    completed=args.completed,
                    hide_projects=args.project)

    last_refresh_time = 0
    try:
        while True:
            key = screen.stdscr.getch()
            if key == 113:
                break

            if (key == KEY_RESIZE
                    or time.time() > last_refresh_time + args.refresh):
                last_refresh_time = time.time()
                screen.refresh_buffer()
                screen.draw()

            napms(50)
    except KeyboardInterrupt:
        pass
    finally:
        screen.close()
Exemple #8
0
class Main:
    def __init__(self, argv):
        self.home_dir = os.path.expanduser("~")

        self.parse_args(argv)
        self.check_files()

        task_command_args = ["task", "status.not:deleted"]

        task_command_args.append(f"scheduled.after:{self.scheduled_after}")
        task_command_args.append(f"scheduled.before:{self.scheduled_before}")

        if not self.show_completed:
            task_command_args.append(f"status.not:{self.show_completed}")

        self.backend = PatchedTaskWarrior(
            data_location=self.data_location,
            create=False,
            taskrc_location=self.taskrc_location,
            task_command=" ".join(task_command_args),
        )

        self.schedule = Schedule(
            self.backend,
            scheduled_after=self.scheduled_after,
            scheduled_before=self.scheduled_before,
        )

    def check_files(self):
        """Check if the required files, directories and settings are present."""
        # Create a temporary taskwarrior instance to read the config
        taskwarrior = TaskWarrior(
            data_location=self.data_location,
            create=False,
            taskrc_location=self.taskrc_location,
        )

        # Disable _forcecolor because it breaks tw config output
        taskwarrior.overrides.update({"_forcecolor": "off"})

        # Check taskwarrior directory and taskrc
        if os.path.isdir(self.data_location) is False:
            raise TaskDirDoesNotExistError(".task directory not found")
        if os.path.isfile(self.taskrc_location) is False:
            raise TaskrcDoesNotExistError(".taskrc not found")

        # Check if required UDAs exist
        if taskwarrior.config.get("uda.estimate.type") is None:
            raise UDADoesNotExistError(
                ("uda.estimate.type does not exist " "in .taskrc")
            )
        if taskwarrior.config.get("uda.estimate.label") is None:
            raise UDADoesNotExistError(
                ("uda.estimate.label does not exist " "in .taskrc")
            )

        # Check sound file
        sound_file = self.home_dir + "/.taskschedule/hooks/drip.wav"
        if self.show_notifications and os.path.isfile(sound_file) is False:
            raise SoundDoesNotExistError(
                f"The specified sound file does not exist: {sound_file}"
            )

        # Create user directory if it does not exist
        taskschedule_dir = self.home_dir + "/.taskschedule"
        hooks_directory = self.home_dir + "/.taskschedule/hooks"
        if not os.path.isdir(taskschedule_dir):
            os.mkdir(taskschedule_dir)
        if not os.path.isdir(hooks_directory):
            os.mkdir(hooks_directory)

    def parse_args(self, argv):
        parser = argparse.ArgumentParser(
            description="""Display a schedule report for taskwarrior."""
        )
        parser.add_argument(
            "-r", "--refresh", help="refresh every n seconds", type=int, default=1
        )
        parser.add_argument(
            "--from",
            help="scheduled from date: ex. 'today', 'tomorrow'",
            type=str,
            dest="after",
            default="today-1s",
        )
        parser.add_argument(
            "--to",
            "--until",
            help="scheduled until date: ex. 'today', 'tomorrow'",
            type=str,
            dest="before",
            default="tomorrow",
        )
        parser.add_argument(
            "-d",
            "--data-location",
            help="""data location (e.g. ~/.task)""",
            type=str,
            dest="data_location",
            default=f"{self.home_dir}/.task",
        )
        parser.add_argument(
            "-t",
            "--taskrc-location",
            help="""taskrc location (e.g. ~/.taskrc)""",
            type=str,
            dest="taskrc_location",
            default=f"{self.home_dir}/.taskrc",
        )
        parser.add_argument(
            "-a",
            "--all",
            help="show all hours, even if empty",
            action="store_true",
            default=False,
        )
        parser.add_argument(
            "-c",
            "--completed",
            help="hide completed tasks",
            action="store_false",
            default=True,
        )
        parser.add_argument(
            "-p",
            "--project",
            help="hide project column",
            action="store_true",
            default=False,
        )
        parser.add_argument(
            "--no-notifications",
            help="disable notifications",
            action="store_false",
            default=True,
            dest="notifications",
        )
        args = parser.parse_args(argv)

        if args.before and not args.after or not args.before and args.after:
            print(
                "Error: Either both --until and --from or neither options must be used."
            )
            sys.exit(1)

        self.data_location = args.data_location
        self.taskrc_location = args.taskrc_location

        # Parse schedule date range
        self.scheduled_after: datetime = calculate_datetime(args.after)
        self.scheduled_before: datetime = calculate_datetime(args.before)

        self.show_completed = args.completed
        self.hide_empty = not args.all
        self.hide_projects = args.project
        self.refresh_rate = args.refresh
        self.show_notifications = args.notifications

    def main(self):
        """Initialize the screen and notifier, and start the main loop of
           the interface."""

        if self.show_notifications:
            self.notifier = Notifier(self.backend)
        else:
            self.notifier = None

        self.screen = Screen(
            self.schedule,
            scheduled_after=self.scheduled_after,
            scheduled_before=self.scheduled_before,
            hide_empty=self.hide_empty,
            hide_projects=self.hide_projects,
        )

        try:
            self.run()
        except TaskDirDoesNotExistError as err:
            print("Error: {}".format(err))
            sys.exit(1)
        except TaskrcDoesNotExistError as err:
            print("Error: {}".format(err))
            sys.exit(1)
        except KeyboardInterrupt:
            self.screen.close()
        except ValueError as err:
            self.screen.close()
            print("Error: {}".format(err))
            sys.exit(1)
        except UDADoesNotExistError as err:
            self.screen.close()
            print("Error: {}".format(err))
            sys.exit(1)
        except SoundDoesNotExistError as err:
            self.screen.close()
            print("Error: {}".format(err))
            sys.exit(1)
        else:
            try:
                self.screen.close()
            except curses_error as err:
                print(err.with_traceback)

    def run(self):
        """The main loop of the interface."""

        filename = f"{self.data_location}/pending.data"
        cached_stamp = 0.0

        last_refresh_time = 0.0
        while True:
            key = self.screen.stdscr.getch()
            if key == 113:  # q
                break
            elif key == 65 or key == 107:  # Up / k
                self.screen.scroll(-1)
                last_refresh_time = time.time()
            elif key == 66 or key == 106:  # Down / j
                self.screen.scroll(1)
                last_refresh_time = time.time()
            elif key == 54:  # Page down
                max_y, max_x = self.screen.get_maxyx()
                self.screen.scroll(max_y - 4)
                last_refresh_time = time.time()
            elif key == 53:  # Page up
                max_y, max_x = self.screen.get_maxyx()
                self.screen.scroll(-(max_y - 4))
                last_refresh_time = time.time()
            elif key == KEY_RESIZE:
                last_refresh_time = time.time()
                self.screen.refresh_buffer()
                self.screen.draw()
            elif time.time() > last_refresh_time + self.refresh_rate:
                if self.notifier:
                    self.notifier.send_notifications()

                # Redraw if task data has changed
                stamp = os.stat(filename).st_mtime
                if stamp != cached_stamp:
                    cached_stamp = stamp
                    self.schedule.clear_cache()
                    self.screen.refresh_buffer()
                    self.screen.draw()

                last_refresh_time = time.time()

            napms(1)

            if self.refresh_rate < 0:
                break
Exemple #9
0
 def test_prerender_empty_line(self, screen: Screen):
     empty_line_buffer = screen.prerender_empty_line(True, 0, 22, "2019-12-08")
     assert "  " in empty_line_buffer[0][2]
     assert empty_line_buffer[0][1] == 5
     assert empty_line_buffer[1][2] == "22"
     assert empty_line_buffer[1][1] == 0
Exemple #10
0
 def test_prerender_footnote(self, screen: Screen):
     footnote = screen.prerender_footnote()
     count = len(screen.schedule.tasks)
     assert f"{count} tasks" in footnote
Exemple #11
0
def screen(tw, schedule):
    screen = Screen(schedule, schedule.scheduled_after,
                    schedule.scheduled_before)
    yield screen