def execute(self): if not super().execute(): return False self._backup = ChangeSet() archive_path = config().archive() if archive_path: self._archive_file = TodoFile.TodoFile(config().archive()) self._archive = TodoList.TodoList(self._archive_file.read()) if len(self.args) > 1: self.error(self.usage()) else: try: arg = self.argument(0) self._handle_args(arg) except InvalidCommandArgument: try: self._revert_last() except (ValueError, KeyError): self.error( 'No backup was found for the current state of ' + config().todotxt()) self._backup.close()
def test_revert02(self): backup = ChangeSet(self.todolist, self.archive, ['do 1']) backup.timestamp = '1' command1 = DoCommand(["1"], self.todolist, self.out, self.error, None) command1.execute() archive_command1 = ArchiveCommand(self.todolist, self.archive) archive_command1.execute() self.archive_file.write(self.archive.print_todos()) backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['do Bar']) backup.timestamp = '2' command2 = DoCommand(["Bar"], self.todolist, self.out, self.error, None) command2.execute() archive_command2 = ArchiveCommand(self.todolist, self.archive) archive_command2.execute() self.archive_file.write(self.archive.print_todos()) backup.save(self.todolist) self.assertEqual(self.archive.print_todos(), "x {t} Foo\nx {t} Bar".format(t=self.today)) self.assertEqual(self.todolist.print_todos(), "Baz") revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() result = TodoList(self.archive_file.read()).print_todos() self.assertEqual(self.errors, "") self.assertTrue( self.output.endswith("Successfully reverted: do Bar\n")) self.assertEqual(result, "x {} Foo".format(self.today)) self.assertEqual(self.todolist.print_todos(), "Bar\nBaz")
def _backup(self, p_command, p_args=[], p_label=None): if config().backup_count() > 0 and p_command and not self.is_read_only(p_command): call = [p_command.name()]+ p_args from topydo.lib.ChangeSet import ChangeSet label = p_label if p_label else call self.backup = ChangeSet(self.todolist, p_label=label)
class BackupSimulator(object): def __init__(self, p_todolist, p_archive, p_timestamp, p_label): self.backup = ChangeSet(p_todolist, p_archive, p_label) self.backup.timestamp = p_timestamp def save(self, p_todolist): self.backup.save(p_todolist)
def test_revert07(self): """ Test backup when no archive file is set """ backup = ChangeSet(self.todolist, None, ['add One']) backup.timestamp = '1' command1 = AddCommand(["One"], self.todolist, self.out, self.error, None) command1.execute() backup.save(self.todolist) changesets = list(backup.backup_dict.keys()) changesets.remove('index') self.assertEqual(len(changesets), 1) self.assertEqual(self.errors, "")
def test_revert07(self): """ Test backup when no archive file is set """ backup = ChangeSet(self.todolist, None, ['add One']) backup.timestamp = '1' command_executer(AddCommand, ["One"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) changesets = list(backup.backup_dict.keys()) changesets.remove('index') self.assertEqual(len(changesets), 1) self.assertEqual(self.errors, "")
def test_revert02(self): backup = ChangeSet(self.todolist, self.archive, ['do 1']) backup.timestamp = '1' command1 = DoCommand(["1"], self.todolist, self.out, self.error, None) command1.execute() archive_command1 = ArchiveCommand(self.todolist, self.archive) archive_command1.execute() self.archive_file.write(self.archive.print_todos()) backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['do Bar']) backup.timestamp = '2' command2 = DoCommand(["Bar"], self.todolist, self.out, self.error, None) command2.execute() archive_command2 = ArchiveCommand(self.todolist, self.archive) archive_command2.execute() self.archive_file.write(self.archive.print_todos()) backup.save(self.todolist) self.assertEqual(self.archive.print_todos(), "x {t} Foo\nx {t} Bar".format(t=self.today)) self.assertEqual(self.todolist.print_todos(), "Baz") revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() result = TodoList(self.archive_file.read()).print_todos() self.assertEqual(self.errors, "") self.assertTrue(self.output.endswith("Successfully reverted: do Bar\n")) self.assertEqual(result, "x {} Foo".format(self.today)) self.assertEqual(self.todolist.print_todos(), "Bar\nBaz")
def test_revert_no_todolist(self): """ Test attempt of revert with todolist missing """ backup = BackupSimulator(self.todolist, self.archive, '1', ['add One']) command_executer(AddCommand, ["One"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = BackupSimulator(self.todolist, self.archive, '2', ['add Two']) command_executer(AddCommand, ["Two"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = BackupSimulator(self.todolist, self.archive, '3', ['add Three']) command_executer(AddCommand, ["Three"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = BackupSimulator(self.todolist, self.archive, '4', ['delete Three']) command_executer(DeleteCommand, ["Three"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) command_executer(RevertCommand, ['1'], None, None, self.out, self.error, None) result = len(ChangeSet().backup_dict.keys()) self.assertEqual(result, 4)
def test_revert06(self): """ Test attempt of deletion with non-existing backup key""" backup = BackupSimulator(self.todolist, self.archive, '1', ['add One']) command_executer(AddCommand, ["One"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = BackupSimulator(self.todolist, self.archive, '2', ['add Two']) command_executer(AddCommand, ["Two"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = BackupSimulator(self.todolist, self.archive, '3', ['add Three']) command_executer(AddCommand, ["Three"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = BackupSimulator(self.todolist, self.archive, '4', ['delete Three']) command_executer(DeleteCommand, ["Three"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = ChangeSet() backup.delete('Foo') changesets = list(backup.backup_dict.keys()) changesets.remove('index') index_timestamps = [change[0] for change in backup._get_index()] result = list(set(index_timestamps) - set(changesets)) self.assertEqual(len(changesets), 4) self.assertEqual(result, []) self.assertEqual(self.errors, "")
def test_revert02(self): backup = BackupSimulator(self.todolist, self.archive, '1', ['do 1']) command_executer(DoCommand, ["1"], self.todolist, self.archive, self.out, self.error, None) self.archive_file.write(self.archive.print_todos()) backup.save(self.todolist) # Use add_todolist and add_archive to also cover them backup = ChangeSet(p_label=['do Bar']) backup.add_todolist(self.todolist) backup.add_archive(self.archive) backup.timestamp = '2' command_executer(DoCommand, ["Bar"], self.todolist, self.archive, self.out, self.error, None) self.archive_file.write(self.archive.print_todos()) backup.save(self.todolist) self.assertEqual(self.archive.print_todos(), "x {t} Foo\nx {t} Bar".format(t=self.today)) self.assertEqual(self.todolist.print_todos(), "Baz") revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() result = TodoList(self.archive_file.read()).print_todos() self.assertEqual(self.errors, "") self.assertTrue( self.output.endswith("Reverted to state before: do Bar\n")) self.assertEqual(result, "x {} Foo".format(self.today)) self.assertEqual(self.todolist.print_todos(), "Bar\nBaz")
def _backup(self, p_command, p_args=None, p_label=None): if config().backup_count() > 0 and p_command and not CLIApplicationBase.is_read_only(p_command): p_args = p_args if p_args else [] call = [p_command.name()] + p_args from topydo.lib.ChangeSet import ChangeSet label = p_label if p_label else call self.backup = ChangeSet(self.todolist, p_label=label)
def execute(self): if not super().execute(): return False archive_file = TodoFile.TodoFile(config().archive()) archive = TodoList.TodoList(archive_file.read()) last_change = ChangeSet() try: last_change.get_backup(self.todolist) last_change.apply(self.todolist, archive) archive_file.write(archive.print_todos()) last_change.delete() self.out("Successfully reverted: " + last_change.label) except (ValueError, KeyError): self.error('No backup was found for the current state of ' + config().todotxt()) last_change.close()
def _execute(self, p_command, p_args): """ Execute a subcommand with arguments. p_command is a class (not an object). """ if config().backup_count() > 0 and p_command and not self.is_read_only(p_command): call = [p_command.__module__.lower()[16:-7]] + p_args # strip "topydo.commands" and "Command" from topydo.lib.ChangeSet import ChangeSet self.backup = ChangeSet(self.todolist, p_call=call) command = p_command( p_args, self.todolist, lambda o: write(sys.stdout, o), error, input) if command.execute() != False: return True return False
def execute(self): if not super().execute(): return False self._backup = ChangeSet() archive_path = config().archive() if archive_path: self._archive_file = TodoFile.TodoFile(config().archive()) self._archive = TodoList.TodoList(self._archive_file.read()) if len(self.args) > 1: self.error(self.usage()) else: try: arg = self.argument(0) self._handle_args(arg) except InvalidCommandArgument: try: self._revert_last() except (ValueError, KeyError): self.error('No backup was found for the current state of ' + config().todotxt()) self._backup.close()
def test_revert05(self): """ Test for possible backup collisions """ backup = ChangeSet(self.todolist, self.archive, ['add One']) backup.timestamp = '1' command1 = AddCommand(["One"], self.todolist, self.out, self.error, None) command1.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Two']) backup.timestamp = '2' command2 = AddCommand(["Two"], self.todolist, self.out, self.error, None) command2.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Three']) backup.timestamp = '3' command3 = AddCommand(["Three"], self.todolist, self.out, self.error, None) command3.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['delete Three']) backup.timestamp = '4' command4 = DeleteCommand(["Three"], self.todolist, self.out, self.error, None) command4.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Four']) backup.timestamp = '5' command4 = AddCommand(["Four"], self.todolist, self.out, self.error, None) command4.execute() backup.save(self.todolist) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() self.assertEqual(self.errors, "") self.assertTrue(self.output.endswith("Successfully reverted: add Four\n")) self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz\n{t} One\n{t} Two".format(t=self.today)) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() self.assertEqual(self.errors, "") self.assertTrue(self.output.endswith("Successfully reverted: delete Three\n")) self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz\n{t} One\n{t} Two\n{t} Three".format(t=self.today)) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() self.assertEqual(self.errors, "") self.assertTrue(self.output.endswith("Successfully reverted: add Three\n")) self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz\n{t} One\n{t} Two".format(t=self.today)) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() self.assertEqual(self.errors, "") self.assertTrue(self.output.endswith("Successfully reverted: add Two\n")) self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz\n{t} One".format(t=self.today)) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() self.assertEqual(self.errors, "") self.assertTrue(self.output.endswith("Successfully reverted: add One\n")) self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz")
def test_revert04(self): """ Test trimming of the backup_file """ backup = ChangeSet(self.todolist, self.archive, ['add One']) backup.timestamp = '1' command1 = AddCommand(["One"], self.todolist, self.out, self.error, None) command1.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Two']) backup.timestamp = '2' command2 = AddCommand(["Two"], self.todolist, self.out, self.error, None) command2.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Three']) backup.timestamp = '3' command3 = AddCommand(["Three"], self.todolist, self.out, self.error, None) command3.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Four']) backup.timestamp = '4' command4 = AddCommand(["Four"], self.todolist, self.out, self.error, None) command4.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Five']) backup.timestamp = '5' command5 = AddCommand(["Five"], self.todolist, self.out, self.error, None) command5.execute() backup.save(self.todolist) result = len(ChangeSet().backup_dict.keys()) self.assertEqual(result, 6) backup = ChangeSet(self.todolist, self.archive, ['add Six']) backup.timestamp = '6' command6 = AddCommand(["Six"], self.todolist, self.out, self.error, None) command6.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Seven']) backup.timestamp = '7' command7 = AddCommand(["Seven"], self.todolist, self.out, self.error, None) command7.execute() backup.save(self.todolist) result = len(ChangeSet().backup_dict.keys()) self.assertEqual(result, 6) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() backup = ChangeSet() changesets = list(backup.backup_dict.keys()) changesets.remove('index') index_timestamps = [change[0] for change in backup._get_index()] result = list(set(index_timestamps) - set(changesets)) self.assertEqual(len(changesets), 4) self.assertEqual(result, []) self.assertEqual(self.errors, "") self.assertTrue(self.output.endswith("Successfully reverted: add Seven\n"))
def test_revert04(self, mock_archive): """ Test trimming of the backup_file """ mock_archive.return_value = '' # test for empty archive setting backup = BackupSimulator(self.todolist, self.archive, '1', ['add One']) command_executer(AddCommand, ["One"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = BackupSimulator(self.todolist, self.archive, '2', ['add Two']) command_executer(AddCommand, ["Two"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = BackupSimulator(self.todolist, self.archive, '3', ['add Three']) command_executer(AddCommand, ["Three"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = BackupSimulator(self.todolist, self.archive, '4', ['add Four']) command_executer(AddCommand, ["Four"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = BackupSimulator(self.todolist, self.archive, '5', ['add Five']) command_executer(AddCommand, ["Five"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) result = len(ChangeSet().backup_dict.keys()) self.assertEqual(result, 6) backup = BackupSimulator(self.todolist, self.archive, '6', ['add Six']) command_executer(AddCommand, ["Six"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) backup = BackupSimulator(self.todolist, self.archive, '7', ['add Seven']) command_executer(AddCommand, ["Seven"], self.todolist, None, self.out, self.error, None) backup.save(self.todolist) result = len(ChangeSet().backup_dict.keys()) self.assertEqual(result, 6) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() backup = ChangeSet() changesets = list(backup.backup_dict.keys()) changesets.remove('index') index_timestamps = [change[0] for change in backup._get_index()] result = list(set(index_timestamps) - set(changesets)) self.assertEqual(len(changesets), 4) self.assertEqual(result, []) self.assertEqual(self.errors, "") self.assertTrue( self.output.endswith("Reverted to state before: add Seven\n"))
class RevertCommand(Command): def __init__(self, p_args, p_todolist, # pragma: no branch p_out=lambda a: None, p_err=lambda a: None, p_prompt=lambda a: None): super().__init__(p_args, p_todolist, p_out, p_err, p_prompt) self._backup = None self._archive_file = None self._archive = None def execute(self): if not super().execute(): return False self._backup = ChangeSet() archive_path = config().archive() if archive_path: self._archive_file = TodoFile.TodoFile(config().archive()) self._archive = TodoList.TodoList(self._archive_file.read()) if len(self.args) > 1: self.error(self.usage()) else: try: arg = self.argument(0) self._handle_args(arg) except InvalidCommandArgument: try: self._revert_last() except (ValueError, KeyError): self.error('No backup was found for the current state of ' + config().todotxt()) self._backup.close() def _revert(self, p_timestamp=None): self._backup.read_backup(self.todolist, p_timestamp) self._backup.apply(self.todolist, self._archive) if self._archive: self._archive_file.write(self._archive.print_todos()) self.out("Reverted to state before: " + self._backup.label) def _revert_last(self): self._revert() self._backup.delete() def _revert_to_specific(self, p_position): timestamps = [timestamp for timestamp, _ in self._backup] position = int(p_position) - 1 # numbering in UI starts with 1 try: timestamp = timestamps[position] self._revert(timestamp) for timestamp in timestamps[:position + 1]: self._backup.read_backup(p_timestamp=timestamp) self._backup.delete() except IndexError: self.error('Specified index is out range') def _handle_args(self, p_arg): try: if p_arg == 'ls': self._handle_ls() elif p_arg.isdigit(): self._revert_to_specific(p_arg) else: raise InvalidCommandArgument except InvalidCommandArgument: self.error(self.usage()) def _handle_ls(self): num = 1 for timestamp, change in self._backup: label = change[2] time = arrow.get(timestamp).format('YYYY-MM-DD HH:mm:ss') self.out('{0: >3}| {1} | {2}'.format(str(num), time, label)) num += 1 def usage(self): return """Synopsis: revert [ls] revert [NUMBER]""" def help(self): return """\
def test_revert05(self): """ Test for possible backup collisions """ backup = ChangeSet(self.todolist, self.archive, ['add One']) backup.timestamp = '1' command1 = AddCommand(["One"], self.todolist, self.out, self.error, None) command1.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Two']) backup.timestamp = '2' command2 = AddCommand(["Two"], self.todolist, self.out, self.error, None) command2.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Three']) backup.timestamp = '3' command3 = AddCommand(["Three"], self.todolist, self.out, self.error, None) command3.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['delete Three']) backup.timestamp = '4' command4 = DeleteCommand(["Three"], self.todolist, self.out, self.error, None) command4.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Four']) backup.timestamp = '5' command4 = AddCommand(["Four"], self.todolist, self.out, self.error, None) command4.execute() backup.save(self.todolist) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() self.assertEqual(self.errors, "") self.assertTrue( self.output.endswith("Successfully reverted: add Four\n")) self.assertEqual( self.todolist.print_todos(), "Foo\nBar\nBaz\n{t} One\n{t} Two".format(t=self.today)) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() self.assertEqual(self.errors, "") self.assertTrue( self.output.endswith("Successfully reverted: delete Three\n")) self.assertEqual( self.todolist.print_todos(), "Foo\nBar\nBaz\n{t} One\n{t} Two\n{t} Three".format(t=self.today)) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() self.assertEqual(self.errors, "") self.assertTrue( self.output.endswith("Successfully reverted: add Three\n")) self.assertEqual( self.todolist.print_todos(), "Foo\nBar\nBaz\n{t} One\n{t} Two".format(t=self.today)) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() self.assertEqual(self.errors, "") self.assertTrue( self.output.endswith("Successfully reverted: add Two\n")) self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz\n{t} One".format(t=self.today)) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() self.assertEqual(self.errors, "") self.assertTrue( self.output.endswith("Successfully reverted: add One\n")) self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz")
class CLIApplicationBase(object): """ Base class for a Command Line Interfaces (CLI) for topydo. Examples are the original CLI and the Prompt interface. Handles input/output of the various subcommands. """ def __init__(self): self.todolist = TodoList.TodoList([]) self.todofile = None self.do_archive = True self._post_archive_action = None self.backup = None @staticmethod def _usage(): usage() sys.exit(0) def _process_flags(self): args = sys.argv[1:] try: opts, args = getopt.getopt(args, MAIN_OPTS, MAIN_LONG_OPTS) except getopt.GetoptError as e: error(str(e)) sys.exit(1) alt_config_path = None overrides = {} for opt, value in opts: if opt == "-a": self.do_archive = False elif opt == "-c": alt_config_path = value elif opt == "-C": overrides[('topydo', 'force_colors')] = '1' overrides[('topydo', 'colors')] = value elif opt == "-t": overrides[('topydo', 'filename')] = value elif opt == "-d": overrides[('topydo', 'archive_filename')] = value elif opt in ("-v", "--version"): version() else: CLIApplicationBase._usage() if alt_config_path: config(alt_config_path, overrides) elif len(overrides): config(p_overrides=overrides) return args def _archive(self): """ Performs an archive action on the todolist. This means that all completed tasks are moved to the archive file (defaults to done.txt). """ archive, archive_file = _retrieve_archive() if self.backup: self.backup.add_archive(archive) if archive: from topydo.commands.ArchiveCommand import ArchiveCommand command = ArchiveCommand(self.todolist, archive) command.execute() if archive.dirty: archive_file.write(archive.print_todos()) @staticmethod def is_read_only(p_command): """ Returns True when the given command class is read-only. """ read_only_commands = tuple(cmd for cmd in ('revert', ) + READ_ONLY_COMMANDS) return p_command.name() in read_only_commands def _backup(self, p_command, p_args=None, p_label=None): if config().backup_count() > 0 and p_command and not CLIApplicationBase.is_read_only(p_command): p_args = p_args if p_args else [] call = [p_command.name()] + p_args from topydo.lib.ChangeSet import ChangeSet label = p_label if p_label else call self.backup = ChangeSet(self.todolist, p_label=label) def _execute(self, p_command, p_args): """ Execute a subcommand with arguments. p_command is a class (not an object). """ self._backup(p_command, p_args) command = p_command( p_args, self.todolist, output, error, input) if command.execute() != False: self._post_archive_action = command.execute_post_archive_actions return True return False def _post_execute(self): """ Should be called when executing the user requested command has been completed. It will do some maintenance and write out the final result to the todo.txt file. """ if self.todolist.dirty: # do not archive when the value of the filename is an empty string # (i.e. explicitly left empty in the configuration if self.do_archive and config().archive(): self._archive() elif config().archive() and self.backup: archive = _retrieve_archive()[0] self.backup.add_archive(archive) self._post_archive_action() if config().keep_sorted(): from topydo.commands.SortCommand import SortCommand self._execute(SortCommand, []) if self.backup: self.backup.save(self.todolist) self.todofile.write(self.todolist.print_todos()) self.todolist.dirty = False self.backup = None def run(self): raise NotImplementedError
def _backup(self, p_command, p_args): if config().backup_count() > 0 and p_command and not self.is_read_only(p_command): call = [p_command.__module__.lower()[16:-7]] + p_args # strip "topydo.commands" and "Command" from topydo.lib.ChangeSet import ChangeSet self.backup = ChangeSet(self.todolist, p_call=call)
def test_revert06(self): """ Test attempt of deletion with non-existing backup key""" backup = ChangeSet(self.todolist, self.archive, ['add One']) backup.timestamp = '1' command1 = AddCommand(["One"], self.todolist, self.out, self.error, None) command1.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Two']) backup.timestamp = '2' command2 = AddCommand(["Two"], self.todolist, self.out, self.error, None) command2.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Three']) backup.timestamp = '3' command3 = AddCommand(["Three"], self.todolist, self.out, self.error, None) command3.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['delete Three']) backup.timestamp = '4' command4 = DeleteCommand(["Three"], self.todolist, self.out, self.error, None) command4.execute() backup.save(self.todolist) backup = ChangeSet() backup.delete('Foo') changesets = list(backup.backup_dict.keys()) changesets.remove('index') index_timestamps = [change[0] for change in backup._get_index()] result = list(set(index_timestamps) - set(changesets)) self.assertEqual(len(changesets), 4) self.assertEqual(result, []) self.assertEqual(self.errors, "")
def test_revert04(self): """ Test trimming of the backup_file """ backup = ChangeSet(self.todolist, self.archive, ['add One']) backup.timestamp = '1' command1 = AddCommand(["One"], self.todolist, self.out, self.error, None) command1.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Two']) backup.timestamp = '2' command2 = AddCommand(["Two"], self.todolist, self.out, self.error, None) command2.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Three']) backup.timestamp = '3' command3 = AddCommand(["Three"], self.todolist, self.out, self.error, None) command3.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Four']) backup.timestamp = '4' command4 = AddCommand(["Four"], self.todolist, self.out, self.error, None) command4.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Five']) backup.timestamp = '5' command5 = AddCommand(["Five"], self.todolist, self.out, self.error, None) command5.execute() backup.save(self.todolist) result = len(ChangeSet().backup_dict.keys()) self.assertEqual(result, 6) backup = ChangeSet(self.todolist, self.archive, ['add Six']) backup.timestamp = '6' command6 = AddCommand(["Six"], self.todolist, self.out, self.error, None) command6.execute() backup.save(self.todolist) backup = ChangeSet(self.todolist, self.archive, ['add Seven']) backup.timestamp = '7' command7 = AddCommand(["Seven"], self.todolist, self.out, self.error, None) command7.execute() backup.save(self.todolist) result = len(ChangeSet().backup_dict.keys()) self.assertEqual(result, 6) revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command.execute() backup = ChangeSet() changesets = list(backup.backup_dict.keys()) changesets.remove('index') index_timestamps = [change[0] for change in backup._get_index()] result = list(set(index_timestamps) - set(changesets)) self.assertEqual(len(changesets), 4) self.assertEqual(result, []) self.assertEqual(self.errors, "") self.assertTrue( self.output.endswith("Successfully reverted: add Seven\n"))
class RevertCommand(Command): def __init__( self, p_args, p_todolist, # pragma: no branch p_out=lambda a: None, p_err=lambda a: None, p_prompt=lambda a: None): super().__init__(p_args, p_todolist, p_out, p_err, p_prompt) self._backup = None self._archive_file = None self._archive = None def execute(self): if not super().execute(): return False self._backup = ChangeSet() archive_path = config().archive() if archive_path: self._archive_file = TodoFile.TodoFile(config().archive()) self._archive = TodoList.TodoList(self._archive_file.read()) if len(self.args) > 1: self.error(self.usage()) else: try: arg = self.argument(0) self._handle_args(arg) except InvalidCommandArgument: try: self._revert_last() except (ValueError, KeyError): self.error( 'No backup was found for the current state of ' + config().todotxt()) self._backup.close() def _revert(self, p_timestamp=None): self._backup.read_backup(self.todolist, p_timestamp) self._backup.apply(self.todolist, self._archive) if self._archive: self._archive_file.write(self._archive.print_todos()) self.out("Reverted to state before: " + self._backup.label) def _revert_last(self): self._revert() self._backup.delete() def _revert_to_specific(self, p_position): timestamps = [timestamp for timestamp, _ in self._backup] position = int(p_position) - 1 # numbering in UI starts with 1 try: timestamp = timestamps[position] self._revert(timestamp) for timestamp in timestamps[:position + 1]: self._backup.read_backup(p_timestamp=timestamp) self._backup.delete() except IndexError: self.error('Specified index is out range') def _handle_args(self, p_arg): try: if p_arg == 'ls': self._handle_ls() elif p_arg.isdigit(): self._revert_to_specific(p_arg) else: raise InvalidCommandArgument except InvalidCommandArgument: self.error(self.usage()) def _handle_ls(self): num = 1 for timestamp, change in self._backup: label = change[2] time = arrow.get(float(timestamp)).format('YYYY-MM-DD HH:mm:ss') self.out('{0: >3}| {1} | {2}'.format(str(num), time, label)) num += 1 def usage(self): return """Synopsis: revert [ls] revert [NUMBER]""" def help(self): return """\
def __init__(self, p_todolist, p_archive, p_timestamp, p_label): self.backup = ChangeSet(p_todolist, p_archive, p_label) self.backup.timestamp = p_timestamp
class CLIApplicationBase(object): """ Base class for a Command Line Interfaces (CLI) for topydo. Examples are the original CLI and the Prompt interface. Handles input/output of the various subcommands. """ def __init__(self): self.todolist = TodoList.TodoList([]) self.todofile = None self.do_archive = True self._post_archive_action = None self.backup = None @staticmethod def _usage(): usage() sys.exit(0) def _process_flags(self): args = sys.argv[1:] try: opts, args = getopt.getopt(args, MAIN_OPTS, MAIN_LONG_OPTS) except getopt.GetoptError as e: error(str(e)) sys.exit(1) alt_config_path = None overrides = {} for opt, value in opts: if opt == "-a": self.do_archive = False elif opt == "-c": alt_config_path = value elif opt == "-C": overrides[('topydo', 'force_colors')] = '1' overrides[('topydo', 'colors')] = value elif opt == "-t": overrides[('topydo', 'filename')] = value elif opt == "-d": overrides[('topydo', 'archive_filename')] = value elif opt in ("-v", "--version"): version() else: CLIApplicationBase._usage() if alt_config_path: config(alt_config_path, overrides) elif len(overrides): config(p_overrides=overrides) return args def _archive(self): """ Performs an archive action on the todolist. This means that all completed tasks are moved to the archive file (defaults to done.txt). """ archive, archive_file = _retrieve_archive() if self.backup: self.backup.add_archive(archive) if archive: command = ArchiveCommand(self.todolist, archive) command.execute() if archive.dirty: archive_file.write(archive.print_todos()) @staticmethod def is_read_only(p_command): """ Returns True when the given command class is read-only. """ read_only_commands = tuple( cmd for cmd in ('revert', ) + READ_ONLY_COMMANDS) return p_command.name() in read_only_commands def _backup(self, p_command, p_args=None, p_label=None): if config().backup_count( ) > 0 and p_command and not CLIApplicationBase.is_read_only(p_command): p_args = p_args if p_args else [] call = [p_command.name()] + p_args from topydo.lib.ChangeSet import ChangeSet label = p_label if p_label else call self.backup = ChangeSet(self.todolist, p_label=label) def _execute(self, p_command, p_args): """ Execute a subcommand with arguments. p_command is a class (not an object). """ self._backup(p_command, p_args) if p_command == None: usage() return False if p_command.name() != 'archive': command = p_command(p_args, self.todolist, output, error, input) if command.execute() != False: self._post_archive_action = command.execute_post_archive_actions return True else: self.todolist.dirty = True self.do_archive = True return True return False def _post_execute(self): """ Should be called when executing the user requested command has been completed. It will do some maintenance and write out the final result to the todo.txt file. """ if self.todolist.dirty: # do not archive when the value of the filename is an empty string # (i.e. explicitly left empty in the configuration if self.do_archive and config().archive(): self._archive() elif config().archive() and self.backup: archive = _retrieve_archive()[0] self.backup.add_archive(archive) try: self._post_archive_action() except TypeError: pass if config().keep_sorted(): from topydo.commands.SortCommand import SortCommand self._execute(SortCommand, []) if self.backup: self.backup.save(self.todolist) self.todofile.write(self.todolist.print_todos()) self.todolist.dirty = False self.backup = None def run(self): raise NotImplementedError
class CLIApplicationBase(object): """ Base class for a Command Line Interfaces (CLI) for topydo. Examples are the original CLI and the Prompt interface. Handles input/output of the various subcommands. """ def __init__(self): self.todolist = TodoList.TodoList([]) self.todofile = None self.do_archive = True self.backup = None def _usage(self): usage() sys.exit(0) def _process_flags(self): args = sys.argv[1:] try: opts, args = getopt.getopt(args, MAIN_OPTS) except getopt.GetoptError as e: error(str(e)) sys.exit(1) alt_config_path = None overrides = {} for opt, value in opts: if opt == "-a": self.do_archive = False elif opt == "-c": alt_config_path = value elif opt == "-t": overrides[('topydo', 'filename')] = value elif opt == "-d": overrides[('topydo', 'archive_filename')] = value elif opt == "-v": version() else: self._usage() if alt_config_path: config(alt_config_path, overrides) elif len(overrides): config(p_overrides=overrides) return args def _archive(self): """ Performs an archive action on the todolist. This means that all completed tasks are moved to the archive file (defaults to done.txt). """ archive_file = TodoFile.TodoFile(config().archive()) archive = TodoListBase.TodoListBase(archive_file.read()) if self.backup: self.backup.add_archive(archive) if archive: from topydo.commands.ArchiveCommand import ArchiveCommand command = ArchiveCommand(self.todolist, archive) command.execute() if archive.is_dirty(): archive_file.write(archive.print_todos()) def _help(self, args): if args is None: pass # TODO else: pass # TODO def is_read_only(self, p_command): """ Returns True when the given command class is read-only. """ read_only_commands = tuple(cmd + 'Command' for cmd in ('Revert', ) + READ_ONLY_COMMANDS) return p_command.__module__.endswith(read_only_commands) def _execute(self, p_command, p_args): """ Execute a subcommand with arguments. p_command is a class (not an object). """ if config().backup_count() > 0 and p_command and not self.is_read_only(p_command): call = [p_command.__module__.lower()[16:-7]] + p_args # strip "topydo.commands" and "Command" from topydo.lib.ChangeSet import ChangeSet self.backup = ChangeSet(self.todolist, p_call=call) command = p_command( p_args, self.todolist, lambda o: write(sys.stdout, o), error, input) if command.execute() != False: return True return False def _post_execute(self): """ Should be called when executing the user requested command has been completed. It will do some maintenance and write out the final result to the todo.txt file. """ if self.todolist.is_dirty(): # do not archive when the value of the filename is an empty string # (i.e. explicitly left empty in the configuration if self.do_archive and config().archive(): self._archive() if config().keep_sorted(): from topydo.commands.SortCommand import SortCommand self._execute(SortCommand, []) if self.backup: self.backup.save(self.todolist) self.todofile.write(self.todolist.print_todos()) self.backup = None def run(self): raise NotImplementedError