def test_clear_cache(self, schedule: Schedule): tasks = schedule.tasks assert tasks schedule.clear_cache() with pytest.raises(KeyError): schedule.__dict__["tasks"] tasks = schedule.tasks assert tasks
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