def __init(self):
        self.__add_command_parsers()

        if self.storage_type == TaskStorageType.xml:
            logging.debug("using xml file: " + self.task_file_location)
            self.task_loader = TaskLoaderXml(self.task_file_location)
        elif self.storage_type == TaskStorageType.pickle:
            logging.debug("using pickle file: " + self.task_file_location)
            self.task_loader = ObjectLoaderPickle(self.task_file_location)
        else:
            logging.error("Unrecognized storage type: '" +
                          str(self.storage_type.name) +
                          "' - using xml instead")
            self.task_loader = TaskLoaderXml(self.task_file_location)

        try:
            self.tasks = self.task_loader.get()
        except FileNotFoundError:
            logging.info(self.task_file_location + " - file doesn't exist")
            self.tasks = []
        except TaskFileBrokenException as e:
            print('Task file broken. Not saving changes!')
            logging.error(self.task_file_location +
                          ' - file broken. Not saving changes!')
            self.tasks = e.args[0]
            self.allowedToSaveTasks = False
        self.periodic_task_generator = PeriodicTaskGenerator(
            self.periodic_tasks_location, self.events_location)
        self.periodic_task_progress_handler = PeriodicTaskProgressHandler(
            self.periodic_tasks_location, self.events_location)
Exemple #2
0
class PeriodicTaskProgressHandler:
    def __init__(self, periodic_filename, event_filename):
        self.periodic_file = periodic_filename
        self.event_filename = event_filename
        self.tasks_prefix = 'periodic_tasks_data_'
        self.periodic_task_generator = PeriodicTaskGenerator(self.periodic_file, self.event_filename)

    def set_done(self, task_id):
        return_value = False
        self.__handle_date_progression()
        tasks = self.periodic_task_generator.get_tasks()
        try:
            if 0 <= task_id < len(tasks):
                return_value = SharedPreferences().put(self.__generate_preference_string(tasks[task_id].name),
                                                       str(tasks[task_id].date_to_finish))
        except TypeError:   # len(tasks) is Null
            pass
        return return_value

    def set_undone(self, task_id):
        return_value = None
        self.__handle_date_progression()
        tasks = self.periodic_task_generator.get_tasks()
        try:
            if 0 <= task_id < len(tasks):
                return_value = SharedPreferences().clear_key(self.__generate_preference_string(tasks[task_id].name))
        except TypeError:  # len(tasks) is Null
            pass
        return return_value

    def is_done(self, task_id):
        return_value = False
        self.__handle_date_progression()
        tasks = self.periodic_task_generator.get_tasks()
        try:
            if 0 <= task_id < len(tasks):
                if SharedPreferences().get(self.__generate_preference_string(tasks[task_id].name)):
                    return_value = True
        except TypeError:  # len(tasks) is Null
            pass
        return return_value

    def reset(self):
        SharedPreferences().clear_starting_pattern(self.tasks_prefix)

    def __handle_date_progression(self):
        tasks = self.periodic_task_generator.get_tasks()
        for task_id, task in enumerate(tasks):
            saved_value = SharedPreferences().get(self.__generate_preference_string(task.name))
            if saved_value is not None:
                if task.type is TaskType.event:
                    if datetime.datetime.strptime(saved_value[:], '%Y-%m-%d').date().year < DateHelper.get_today_short().year:
                        SharedPreferences().clear_key(self.__generate_preference_string(task.name))
                else:
                    if task.date_to_finish > datetime.datetime.strptime(saved_value[:], '%Y-%m-%d').date():
                        SharedPreferences().clear_key(self.__generate_preference_string(task.name))

    def __generate_preference_string(self, value):
        return self.tasks_prefix + str(value)
    def test_get_events_without_shift_ok(self, mock_load):
        ptg = PeriodicTaskGenerator('test', 'test2')
        returned_tasks = ptg.get_tasks(0)

        self.assertTrue(self.mock_date_short.called)
        self.assertTrue(self.mock_date_long.called)
        self.assertEqual(mock_load.call_count, 1)
        self.assertEqual(len(returned_tasks), 1)
    def test_get_tasks_with_shift_7_ok(self, mock_load):
        ptg = PeriodicTaskGenerator('test', 'test2')
        returned_tasks = ptg.get_tasks(7)

        self.assertTrue(self.mock_date_short.called)
        self.assertTrue(self.mock_date_long.called)
        self.assertEqual(mock_load.call_count, 1)
        self.assertEqual(len(returned_tasks), 2)

        self.assertEqual(returned_tasks[0].name, 'task1')
        self.assertEqual(returned_tasks[1].name, 'task4')
    def test_get_list_next_occurrence_ok(self, mock_load):
        ptg = PeriodicTaskGenerator('test', 'test2')
        returned_tasks = ptg.get_list_next_occurrence()

        self.assertTrue(self.mock_date_short.called)
        self.assertTrue(self.mock_date_long.called)
        self.assertEqual(mock_load.call_count, 1)
        self.assertEqual(len(returned_tasks), 4)

        self.assertEqual(returned_tasks[0].date_to_finish, DateTimeHelper.get_fixed_date(4))
        self.assertEqual(returned_tasks[1].date_to_finish, DateTimeHelper.get_fixed_date(10))
        self.assertEqual(returned_tasks[2].date_to_finish, DateTimeHelper.get_fixed_date(9))
        self.assertEqual(returned_tasks[3].date_to_finish, DateTimeHelper.get_fixed_date(8))
 def __command_undone(self, args):
     try:
         if int(args[0]) > len(self.tasks):
             raise IndexError
         if int(args[0]) < 0:
             return_value = self.periodic_task_progress_handler.set_undone(
                 self.__translate_periodic_task_id(int(args[0])))
             if return_value is None:
                 return MumjolandiaResponseObject(
                     status=MumjolandiaReturnValue.
                     task_done_wrong_parameter,
                     arguments=args)
             else:
                 return_name = PeriodicTaskGenerator(
                     self.periodic_tasks_location,
                     self.events_location).get_tasks()[
                         self.__translate_periodic_task_id(args[0])].name
         else:
             self.tasks[int(args[0])].status = TaskStatus.not_done
             self.__save_if_allowed()
             return_name = self.tasks[int(args[0])].name
         return MumjolandiaResponseObject(
             status=MumjolandiaReturnValue.task_undone_ok,
             arguments=[return_name])
     except (IndexError, ValueError):
         return MumjolandiaResponseObject(
             status=MumjolandiaReturnValue.task_done_wrong_parameter,
             arguments=args)
 def __command_periodic(self, args):
     # todo: done/undone information not included in print; dont add here, but create class to handle
     #  (see comment in self.task_get)
     return_list = []
     return_indexes = []
     tasks = PeriodicTaskGenerator(
         self.periodic_tasks_location,
         self.events_location).get_list_next_occurrence()
     for n, t in enumerate(tasks):
         return_list.append(t)
         return_indexes.append(self.__translate_periodic_task_id(n))
     return MumjolandiaResponseObject(
         status=MumjolandiaReturnValue.task_get,
         arguments=[return_indexes, return_list])
Exemple #8
0
 def __init__(self, periodic_filename, event_filename):
     self.periodic_file = periodic_filename
     self.event_filename = event_filename
     self.tasks_prefix = 'periodic_tasks_data_'
     self.periodic_task_generator = PeriodicTaskGenerator(self.periodic_file, self.event_filename)
class TaskSupervisor(MumjolandiaSupervisor):
    def __init__(self, storage_type=TaskStorageType.xml):
        super().__init__()
        self.storage_type = storage_type
        self.periodic_tasks_location = ConfigLoader.get_mumjolandia_location(
        ) + "data/periodic_tasks.xml"
        self.periodic_task_generator = None
        self.periodic_task_progress_handler = None
        self.events_location = ConfigLoader.get_mumjolandia_location(
        ) + "data/events.xml"
        self.task_file_location = ConfigLoader.get_mumjolandia_location(
        ) + "data/tasks." + self.storage_type.name.lower()
        self.allowedToSaveTasks = True  # if loaded tasks are broken they wont be overwritten to not loose them
        self.task_loader = None
        self.tasks = None
        self.__init()

    def __init(self):
        self.__add_command_parsers()

        if self.storage_type == TaskStorageType.xml:
            logging.debug("using xml file: " + self.task_file_location)
            self.task_loader = TaskLoaderXml(self.task_file_location)
        elif self.storage_type == TaskStorageType.pickle:
            logging.debug("using pickle file: " + self.task_file_location)
            self.task_loader = ObjectLoaderPickle(self.task_file_location)
        else:
            logging.error("Unrecognized storage type: '" +
                          str(self.storage_type.name) +
                          "' - using xml instead")
            self.task_loader = TaskLoaderXml(self.task_file_location)

        try:
            self.tasks = self.task_loader.get()
        except FileNotFoundError:
            logging.info(self.task_file_location + " - file doesn't exist")
            self.tasks = []
        except TaskFileBrokenException as e:
            print('Task file broken. Not saving changes!')
            logging.error(self.task_file_location +
                          ' - file broken. Not saving changes!')
            self.tasks = e.args[0]
            self.allowedToSaveTasks = False
        self.periodic_task_generator = PeriodicTaskGenerator(
            self.periodic_tasks_location, self.events_location)
        self.periodic_task_progress_handler = PeriodicTaskProgressHandler(
            self.periodic_tasks_location, self.events_location)

    def __get_today(self):
        return datetime.datetime.today()

    def __save_if_allowed(self):
        if self.allowedToSaveTasks:
            logging.debug("saving tasks to: '" + self.task_file_location + "'")
            self.task_loader.save(self.tasks)

    def __add_command_parsers(self):
        self.command_parsers['add'] = self.__command_add
        self.command_parsers['bump'] = self.__command_bump
        self.command_parsers['b'] = self.__command_bump
        self.command_parsers['done'] = self.__command_done
        self.command_parsers['edit'] = self.__command_edit
        self.command_parsers['e'] = self.__command_edit
        self.command_parsers['find'] = self.__command_find
        self.command_parsers['f'] = self.__command_find
        self.command_parsers['help'] = self.__command_help
        self.command_parsers['ls'] = self.__command_get
        self.command_parsers['periodic'] = self.__command_periodic
        self.command_parsers['rm'] = self.__command_delete
        self.command_parsers['set'] = self.__command_set
        self.command_parsers['undone'] = self.__command_undone

    def __command_add(self, args):
        if len(args) < 1:
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_name_not_given)
        else:
            name = ' '.join(args[0:])
            try:
                self.tasks.append(
                    TaskFactory.get_task(
                        name,
                        date_to_finish=str(DateHelper.get_today_long()),
                    ))
            except IncorrectDateFormatException:
                logging.warning("Task '" + name +
                                "' not added - incorrect date format")
                return MumjolandiaResponseObject(
                    status=MumjolandiaReturnValue.task_incorrect_date_format)
            logging.debug("Added task '" + name + "'")
            self.__save_if_allowed()
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_added,
                arguments=[name, len(self.tasks) - 1])

    def __command_get(
        self, args
    ):  # 'get' - return today, 'get 1' - return tomorrow,get '0' - return all
        return_list = []
        return_indexes = []
        return_status = MumjolandiaReturnValue.task_get
        day_amount = 0
        today = self.__get_today()
        if args:
            try:
                day_amount = int(args[0])
            except ValueError:
                if args[0] != 'x':
                    return MumjolandiaResponseObject(
                        status=MumjolandiaReturnValue.task_get_wrong_data,
                        arguments=args)
            if args[0] == 'x':  # task get x  ### every task that has finish date not set
                for i, t in enumerate(self.tasks):
                    if t.date_to_finish is None:
                        if t.status is not TaskStatus.done:
                            return_list.append(t)
                            return_indexes.append(i)
            elif int(args[0]) == 0:  # task get 0
                for i, t in enumerate(self.tasks):
                    return_list.append(t)
                    return_indexes.append(i)

            else:  # task get [number]
                for i, t in enumerate(self.tasks):
                    target = datetime.datetime.combine(
                        self.__get_today() +
                        datetime.timedelta(days=day_amount),
                        datetime.datetime.min.time())
                    if t.date_to_finish is not None:
                        if t.date_to_finish.year == target.year and \
                                t.date_to_finish.month == target.month and \
                                t.date_to_finish.day == target.day:
                            return_list.append(t)
                            return_indexes.append(i)
        else:  # task get
            for i, t in enumerate(self.tasks):
                # first tasks for today
                # won't show task for today if it's 7 am and task was added at ie. 9 am
                # this is expected behaviour so "task set x" sets hour and minute to 00:00, so everything is showed
                if t.date_to_finish is not None and t.status is not TaskStatus.done:
                    if (t.date_to_finish - today).days * 24 + (
                            t.date_to_finish -
                            today).seconds // 3600 <= t.reminder * 24:
                        return_list.append(t)
                        return_indexes.append(i)
                        continue
                    # now not finished tasks from previous days
                    if t.status == TaskStatus.not_done and t.date_to_finish <= today:
                        return_list.append(t)
                        return_indexes.append(i)
                # tasks from previous days and finished today
                if t.date_finished is not None:
                    if t.status == TaskStatus.done:
                        if t.date_finished.year == today.year and \
                                t.date_finished.month == today.month and \
                                t.date_finished.day == today.day:
                            return_list.append(t)
                            return_indexes.append(i)
            try:
                return_indexes, return_list = zip(
                    *self.__sort_ls(list(zip(return_indexes, return_list))))
                # zip returned tuples
                return_list = list(return_list)
                return_indexes = list(return_indexes)
            except ValueError:
                # it occurs when lists are empty: ValueError: not enough values to unpack (expected 2, got 0)
                pass
        if args and args[0] == 'x':
            return MumjolandiaResponseObject(
                status=return_status, arguments=[return_indexes, return_list])
        # todo: create class that will return periodic tasks for today; do not loop over tasks form
        #  PeriodicTasksGenerator and parse them here
        tasks = self.periodic_task_generator.get_tasks(day_amount)
        for n, t in enumerate(tasks):
            if self.periodic_task_progress_handler.is_done(
                    self.__translate_periodic_task_id(
                        self.__translate_periodic_task_id(n))):
                t.status = TaskStatus.done
            else:
                t.status = TaskStatus.not_done
            if t.type is TaskType.event and t.status is TaskStatus.done:
                if today.date() > t.date_to_finish:
                    continue
            return_list.insert(0, t)
            return_indexes.insert(0, self.__translate_periodic_task_id(n))
        return MumjolandiaResponseObject(
            status=return_status, arguments=[return_indexes, return_list])

    def __command_help(self, args):
        return MumjolandiaResponseObject(
            status=MumjolandiaReturnValue.task_help,
            arguments=[
                'add [name]\n'
                '[b]ump\n'
                'done\n'
                'edit [id] [name]\n'
                '[f]ind] [str]\n'
                'ls (show today and previous uncompleted\n'
                'ls 0 (show all tasks\n'
                'ls x (show tasks without date)\n'
                'ls [delta] (show tasks for given day)\n'
                'ls [name || id]\n'
                'periodic\n'
                'set [id] [delta_from_today/none]\n'
                'undone\n'
            ])

    def __command_delete(self, args):
        try:
            task_id = args[0]
        except IndexError:
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_delete_incorrect_index,
                arguments=['none'])
        # parameter comes as string. If we can parse it to int then we remove by id. If not, then by name
        try:
            tid = int(task_id)
            try:
                self.tasks.pop(tid)
                self.__save_if_allowed()
                return MumjolandiaResponseObject(
                    status=MumjolandiaReturnValue.task_delete_success,
                    arguments=[task_id, str(1)])
            except IndexError:  # wrong index
                return MumjolandiaResponseObject(
                    status=MumjolandiaReturnValue.task_delete_incorrect_index,
                    arguments=[task_id])
        except ValueError:  # parameter type is not int
            deleted_counter = 0
            for t in reversed(
                    self.tasks
            ):  # reversing allows to remove elements on fly without breaking ids
                if t.name == task_id:
                    self.tasks.remove(t)
                    deleted_counter += 1
            if deleted_counter == 0:
                return MumjolandiaResponseObject(
                    status=MumjolandiaReturnValue.task_delete_incorrect_name,
                    arguments=[task_id])
            else:
                self.__save_if_allowed()
                return MumjolandiaResponseObject(
                    status=MumjolandiaReturnValue.task_delete_success,
                    arguments=[task_id, str(deleted_counter)])

    def __command_edit(self, args):
        try:
            task_id = int(args[0])
        except IndexError:  # first argument not given
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_edit_wrong_index,
                arguments=['None'])
        try:
            if len(args) < 2:
                raise IndexError
            new_task = TaskFactory.get_task(name=' '.join(args[1:]),
                                            date_to_finish=self.tasks[int(
                                                args[0])].date_to_finish)
            self.tasks[task_id] = new_task
            self.__save_if_allowed()
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_edit_ok,
                arguments=[str(task_id)])
        except IndexError:
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_edit_wrong_index,
                arguments=[str(task_id)])

    def __command_set(self, args):
        try:
            if int(args[0]) > len(self.tasks) or int(args[0]) < 0:
                raise IndexError
            # parsing first argument
            if args[1].lower() == 'None'.lower():
                self.tasks[int(args[0])].date_to_finish = None
            else:  # set is for day not hour so part of date is removed
                self.tasks[int(args[0])].date_to_finish = self.__get_today().replace(microsecond=0,
                                                                                     second=0,
                                                                                     minute=0,
                                                                                     hour=0) + \
                                                          datetime.timedelta(days=int(args[1]))
            # parsing second argument
            if len(args) > 2:
                self.tasks[int(args[0])].reminder = int(args[2])
            self.__save_if_allowed()
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_set_ok,
                arguments=[self.tasks[int(args[0])].name, args[1]])
        except IndexError:
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_set_incorrect_parameter,
                arguments=args)
        except ValueError:
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_set_incorrect_parameter,
                arguments=args)

    def __command_done(self, args):
        try:
            if int(args[0]) > len(self.tasks):
                raise IndexError
            if int(args[0]) < 0:
                return_value = self.periodic_task_progress_handler.set_done(
                    self.__translate_periodic_task_id(int(args[0])))
                if return_value is not True:
                    return MumjolandiaResponseObject(
                        status=MumjolandiaReturnValue.
                        task_done_wrong_parameter,
                        arguments=args)
                else:
                    return_name = PeriodicTaskGenerator(
                        self.periodic_tasks_location,
                        self.events_location).get_tasks()[
                            self.__translate_periodic_task_id(args[0])].name
            else:
                self.tasks[int(args[0])].status = TaskStatus.done
                self.tasks[int(args[0])].date_finished = self.__get_today()
                self.__save_if_allowed()
                return_name = self.tasks[int(args[0])].name
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_done_ok,
                arguments=[return_name])
        except (IndexError, ValueError):
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_done_wrong_parameter,
                arguments=args)

    def __command_undone(self, args):
        try:
            if int(args[0]) > len(self.tasks):
                raise IndexError
            if int(args[0]) < 0:
                return_value = self.periodic_task_progress_handler.set_undone(
                    self.__translate_periodic_task_id(int(args[0])))
                if return_value is None:
                    return MumjolandiaResponseObject(
                        status=MumjolandiaReturnValue.
                        task_done_wrong_parameter,
                        arguments=args)
                else:
                    return_name = PeriodicTaskGenerator(
                        self.periodic_tasks_location,
                        self.events_location).get_tasks()[
                            self.__translate_periodic_task_id(args[0])].name
            else:
                self.tasks[int(args[0])].status = TaskStatus.not_done
                self.__save_if_allowed()
                return_name = self.tasks[int(args[0])].name
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_undone_ok,
                arguments=[return_name])
        except (IndexError, ValueError):
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_done_wrong_parameter,
                arguments=args)

    def __command_periodic(self, args):
        # todo: done/undone information not included in print; dont add here, but create class to handle
        #  (see comment in self.task_get)
        return_list = []
        return_indexes = []
        tasks = PeriodicTaskGenerator(
            self.periodic_tasks_location,
            self.events_location).get_list_next_occurrence()
        for n, t in enumerate(tasks):
            return_list.append(t)
            return_indexes.append(self.__translate_periodic_task_id(n))
        return MumjolandiaResponseObject(
            status=MumjolandiaReturnValue.task_get,
            arguments=[return_indexes, return_list])

    def __command_bump(self, args):
        try:
            if int(args[0]) > len(self.tasks) or int(args[0]) < 0:
                raise IndexError
            self.tasks.append(self.tasks.pop(int(args[0])))
            self.__save_if_allowed()
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_bump_ok,
                arguments=[str(len(self.tasks) - 1), self.tasks[-1].name])
        except (IndexError, ValueError):
            return MumjolandiaResponseObject(
                status=MumjolandiaReturnValue.task_bump_nook, arguments=args)

    # todo: add ut
    def __command_find(self, args):
        return_indexes = []
        return_list = []
        if len(args) > 0:
            for i, t in enumerate(self.tasks):
                if str(args[0]).lower() in t.name.lower():
                    return_list.append(t)
                    return_indexes.append(i)
        return MumjolandiaResponseObject(
            status=MumjolandiaReturnValue.task_find,
            arguments=[return_indexes, return_list])

    def __translate_periodic_task_id(self, task_id):
        return -(int(task_id) + 1)

    def __sort_ls(self, tasks_tuple):
        try:
            tasks_tuple.sort(key=lambda tup: tup[1].date_to_finish,
                             reverse=True)
        except TypeError:
            # This error occurs if task is set to today, then marked as done, and then 'set none' command is excuted, so
            # it stays visible in 'task ls', but has no date_to_finish
            # todo: handle such a case gently
            logging.error('Object has on date_to_finish set, but is sorted!')
        return_tuple = []
        for obj in tasks_tuple:
            if obj[1].status == TaskStatus.done:
                return_tuple.append(obj)
        for obj in tasks_tuple:

            if obj[1].status != TaskStatus.done:
                return_tuple.append(obj)
        return return_tuple
    def test_get_tasks_without_shift_ok(self, mock_load):
        ptg = PeriodicTaskGenerator('test', 'test2')
        returned_tasks = ptg.get_tasks()

        self.assertEqual(mock_load.call_count, 1)
        self.assertEqual(len(returned_tasks), 0)