Commit c6e098e3 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Merge branch 'style-fixes'

parents 486aa7be 8ca31100
[settings]
skip=topydo/ui/CLIApplicationBase.py
known_first_party=topydo
...@@ -22,9 +22,11 @@ install: ...@@ -22,9 +22,11 @@ install:
- "pip install .[test]" - "pip install .[test]"
- "pip install pylint" - "pip install pylint"
- "pip install codecov" - "pip install codecov"
- "pip install isort"
script: script:
- "green -vvr $GREEN_OPTS" - "green -vvr $GREEN_OPTS"
- "python -m pylint --errors-only topydo test" - "python -m pylint --errors-only topydo test"
- "isort -c -rc ."
# Cache Dependencies # Cache Dependencies
after_script: after_script:
- codecov - codecov
......
from setuptools import setup, find_packages import codecs
import os import os
import re import re
import codecs
import sys
here = os.path.abspath(os.path.dirname(__file__)) from setuptools import find_packages, setup
_HERE = os.path.abspath(os.path.dirname(__file__))
def read(*parts): def read(*parts):
# intentionally *not* adding an encoding option to open # intentionally *not* adding an encoding option to open
return codecs.open(os.path.join(here, *parts), 'r').read() return codecs.open(os.path.join(_HERE, *parts), 'r').read()
def find_version(*file_paths): def find_version(*file_paths):
...@@ -24,17 +24,17 @@ WATCHDOG = 'watchdog >= 0.8.3' ...@@ -24,17 +24,17 @@ WATCHDOG = 'watchdog >= 0.8.3'
ICALENDAR = 'icalendar' ICALENDAR = 'icalendar'
setup( setup(
name = "topydo", name="topydo",
packages = find_packages(exclude=["test"]), packages=find_packages(exclude=["test"]),
version = find_version('topydo', 'lib', 'Version.py'), version=find_version('topydo', 'lib', 'Version.py'),
description = "A powerful todo.txt application for the console", description="A powerful todo.txt application for the console",
author = "Bram Schoenmakers", author="Bram Schoenmakers",
author_email = "bram@topydo.org", author_email="bram@topydo.org",
url = "https://www.topydo.org", url="https://www.topydo.org",
install_requires = [ install_requires=[
'arrow >= 0.7.0', 'arrow >= 0.7.0',
], ],
extras_require = { extras_require={
':sys_platform=="win32"': ['colorama>=0.2.5'], ':sys_platform=="win32"': ['colorama>=0.2.5'],
':python_version=="3.2"': ['backports.shutil_get_terminal_size>=1.0.0'], ':python_version=="3.2"': ['backports.shutil_get_terminal_size>=1.0.0'],
'columns': ['urwid >= 1.3.0', WATCHDOG], 'columns': ['urwid >= 1.3.0', WATCHDOG],
...@@ -43,10 +43,10 @@ setup( ...@@ -43,10 +43,10 @@ setup(
'test': ['coverage>=4.3', 'freezegun', 'green', ICALENDAR, 'pylint>=1.7.1'], 'test': ['coverage>=4.3', 'freezegun', 'green', ICALENDAR, 'pylint>=1.7.1'],
'test:python_version=="3.2"': ['mock'], 'test:python_version=="3.2"': ['mock'],
}, },
entry_points= { entry_points={
'console_scripts': ['topydo = topydo.ui.UILoader:main'], 'console_scripts': ['topydo=topydo.ui.UILoader:main'],
}, },
classifiers = [ classifiers=[
"Development Status :: 5 - Production/Stable", "Development Status :: 5 - Production/Stable",
"Environment :: Console", "Environment :: Console",
"Intended Audience :: End Users/Desktop", "Intended Audience :: End Users/Desktop",
...@@ -60,7 +60,7 @@ setup( ...@@ -60,7 +60,7 @@ setup(
"Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Utilities", "Topic :: Utilities",
], ],
long_description = """\ long_description="""\
topydo is a powerful and customizable todo.txt application for the console, inspired by the todo.txt CLI by Gina Trapani. topydo is a powerful and customizable todo.txt application for the console, inspired by the todo.txt CLI by Gina Trapani.
Highlights of the additional features it provides: Highlights of the additional features it provides:
...@@ -74,5 +74,5 @@ Highlights of the additional features it provides: ...@@ -74,5 +74,5 @@ Highlights of the additional features it provides:
* Some conveniences when adding new items (e.g. adding creation date and use relative dates) * Some conveniences when adding new items (e.g. adding creation date and use relative dates)
""", """,
test_suite = "test", test_suite="test",
) )
...@@ -15,9 +15,11 @@ ...@@ -15,9 +15,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os import os
from test.topydo_testcase import TopydoTest
from topydo.lib.Utils import escape_ansi from topydo.lib.Utils import escape_ansi
from .topydo_testcase import TopydoTest
class CommandTest(TopydoTest): class CommandTest(TopydoTest):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
......
...@@ -18,11 +18,12 @@ import unittest ...@@ -18,11 +18,12 @@ import unittest
from datetime import date from datetime import date
from io import StringIO from io import StringIO
from test.command_testcase import CommandTest
from topydo.commands import AddCommand, ListCommand from topydo.commands import AddCommand, ListCommand
from topydo.lib import TodoList from topydo.lib import TodoList
from topydo.lib.Config import config from topydo.lib.Config import config
from .command_testcase import CommandTest
# We're searching for 'mock' # We're searching for 'mock'
# pylint: disable=no-name-in-module # pylint: disable=no-name-in-module
try: try:
......
...@@ -17,10 +17,11 @@ ...@@ -17,10 +17,11 @@
import unittest import unittest
from datetime import date from datetime import date
from test.command_testcase import CommandTest
from topydo.commands.AppendCommand import AppendCommand from topydo.commands.AppendCommand import AppendCommand
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .command_testcase import CommandTest
class AppendCommandTest(CommandTest): class AppendCommandTest(CommandTest):
def setUp(self): def setUp(self):
......
...@@ -16,11 +16,12 @@ ...@@ -16,11 +16,12 @@
import unittest import unittest
from test.command_testcase import CommandTest
from test.facilities import load_file_to_todolist
from topydo.commands.ArchiveCommand import ArchiveCommand from topydo.commands.ArchiveCommand import ArchiveCommand
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .command_testcase import CommandTest
from .facilities import load_file_to_todolist
class ArchiveCommandTest(CommandTest): class ArchiveCommandTest(CommandTest):
def test_archive(self): def test_archive(self):
......
...@@ -18,11 +18,11 @@ ...@@ -18,11 +18,11 @@
import unittest import unittest
from test.topydo_testcase import TopydoTest
from topydo.lib.Color import Color from topydo.lib.Color import Color
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
from .topydo_testcase import TopydoTest
NEUTRAL_COLOR = '\033[0m' NEUTRAL_COLOR = '\033[0m'
......
...@@ -15,11 +15,11 @@ ...@@ -15,11 +15,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest import unittest
from unittest import skip
from test.topydo_testcase import TopydoTest
from topydo.lib.Config import config from topydo.lib.Config import config
from .topydo_testcase import TopydoTest
class ConfigTest(TopydoTest): class ConfigTest(TopydoTest):
def test_config01(self): def test_config01(self):
...@@ -167,7 +167,7 @@ class ConfigTest(TopydoTest): ...@@ -167,7 +167,7 @@ class ConfigTest(TopydoTest):
def test_config28(self): def test_config28(self):
""" test duplicates. """ """ test duplicates. """
keymap, keystates = config("test/data/ConfigTest7.conf").column_keymap() keymap, _ = config("test/data/ConfigTest7.conf").column_keymap()
self.assertEqual(keymap['k'], 'bar') self.assertEqual(keymap['k'], 'bar')
self.assertEqual(keymap['z'], 'foobar') self.assertEqual(keymap['z'], 'foobar')
......
...@@ -16,12 +16,13 @@ ...@@ -16,12 +16,13 @@
import unittest import unittest
from test.command_testcase import CommandTest
from topydo.commands.DeleteCommand import DeleteCommand from topydo.commands.DeleteCommand import DeleteCommand
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from topydo.lib.TodoListBase import InvalidTodoException from topydo.lib.TodoListBase import InvalidTodoException
from .command_testcase import CommandTest
def _yes_prompt(self): def _yes_prompt(self):
return "y" return "y"
......
...@@ -16,10 +16,11 @@ ...@@ -16,10 +16,11 @@
import unittest import unittest
from test.command_testcase import CommandTest
from topydo.commands.DepCommand import DepCommand from topydo.commands.DepCommand import DepCommand
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .command_testcase import CommandTest
class DepCommandTest(CommandTest): class DepCommandTest(CommandTest):
def setUp(self): def setUp(self):
......
...@@ -16,10 +16,11 @@ ...@@ -16,10 +16,11 @@
import unittest import unittest
from test.command_testcase import CommandTest
from topydo.commands.DepriCommand import DepriCommand from topydo.commands.DepriCommand import DepriCommand
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .command_testcase import CommandTest
class DepriCommandTest(CommandTest): class DepriCommandTest(CommandTest):
def setUp(self): def setUp(self):
......
...@@ -17,10 +17,11 @@ ...@@ -17,10 +17,11 @@
import unittest import unittest
from datetime import date, timedelta from datetime import date, timedelta
from test.command_testcase import CommandTest
from topydo.commands.DoCommand import DoCommand from topydo.commands.DoCommand import DoCommand
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .command_testcase import CommandTest
def _yes_prompt(self): def _yes_prompt(self):
return "y" return "y"
...@@ -100,34 +101,32 @@ class DoCommandTest(CommandTest): ...@@ -100,34 +101,32 @@ class DoCommandTest(CommandTest):
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_do_subtasks_force1(self): def test_do_subtasks_force1(self):
prompt_shown = False
def prompt(p_prompt): def prompt(p_prompt):
global prompt_shown prompt.prompt_shown = True
prompt_shown = True
prompt.prompt_shown = False
command = DoCommand(["-f", "1"], self.todolist, self.out, self.error, command = DoCommand(["-f", "1"], self.todolist, self.out, self.error,
prompt) prompt)
command.execute() command.execute()
command.execute_post_archive_actions() command.execute_post_archive_actions()
self.assertFalse(prompt_shown) self.assertFalse(prompt.prompt_shown)
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
self.assertFalse(self.todolist.todo(2).is_completed()) self.assertFalse(self.todolist.todo(2).is_completed())
def test_do_subtasks_force2(self): def test_do_subtasks_force2(self):
prompt_shown = False
def prompt(p_prompt): def prompt(p_prompt):
global prompt_shown prompt.prompt_shown = True
prompt_shown = True
prompt.prompt_shown = False
command = DoCommand(["--force", "1"], self.todolist, self.out, command = DoCommand(["--force", "1"], self.todolist, self.out,
self.error, prompt) self.error, prompt)
command.execute() command.execute()
command.execute_post_archive_actions() command.execute_post_archive_actions()
self.assertFalse(prompt_shown) self.assertFalse(prompt.prompt_shown)
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
self.assertFalse(self.todolist.todo(2).is_completed()) self.assertFalse(self.todolist.todo(2).is_completed())
......
...@@ -17,12 +17,13 @@ ...@@ -17,12 +17,13 @@
import os import os
import unittest import unittest
from test.command_testcase import CommandTest
from topydo.commands.EditCommand import EditCommand from topydo.commands.EditCommand import EditCommand
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .command_testcase import CommandTest
# We're searching for 'mock' # We're searching for 'mock'
# pylint: disable=no-name-in-module # pylint: disable=no-name-in-module
try: try:
......
...@@ -19,12 +19,12 @@ ...@@ -19,12 +19,12 @@
import unittest import unittest
from datetime import date, timedelta from datetime import date, timedelta
from test.facilities import (load_file, load_file_to_todolist,
todolist_to_string)
from test.topydo_testcase import TopydoTest
from topydo.lib import Filter from topydo.lib import Filter
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
from .facilities import load_file, load_file_to_todolist, todolist_to_string
from .topydo_testcase import TopydoTest
class FilterTest(TopydoTest): class FilterTest(TopydoTest):
def test_filter03(self): def test_filter03(self):
......
...@@ -16,18 +16,20 @@ ...@@ -16,18 +16,20 @@
import unittest import unittest
from test.topydo_testcase import TopydoTest
from topydo.Commands import get_subcommand from topydo.Commands import get_subcommand
from topydo.commands.AddCommand import AddCommand from topydo.commands.AddCommand import AddCommand
from topydo.commands.DeleteCommand import DeleteCommand from topydo.commands.DeleteCommand import DeleteCommand
from topydo.commands.ListCommand import ListCommand from topydo.commands.ListCommand import ListCommand
from topydo.commands.TagCommand import TagCommand from topydo.commands.TagCommand import TagCommand
from topydo.lib.Config import config, ConfigError from topydo.lib.Config import ConfigError, config
from .topydo_testcase import TopydoTest
class GetSubcommandTest(TopydoTest): class GetSubcommandTest(TopydoTest):
def test_normal_cmd(self): def test_normal_cmd(self):
args = ["add"] args = ["add"]
real_cmd, final_args = get_subcommand(args) real_cmd, _ = get_subcommand(args)
self.assertTrue(issubclass(real_cmd, AddCommand)) self.assertTrue(issubclass(real_cmd, AddCommand))
def test_cmd_help(self): def test_cmd_help(self):
...@@ -116,7 +118,7 @@ class GetSubcommandTest(TopydoTest): ...@@ -116,7 +118,7 @@ class GetSubcommandTest(TopydoTest):
config("test/data/aliases.conf") config("test/data/aliases.conf")
args = ["baz"] args = ["baz"]
real_cmd, final_args = get_subcommand(args) real_cmd, _ = get_subcommand(args)
self.assertEqual(real_cmd, None) self.assertEqual(real_cmd, None)
def test_alias_quotation(self): def test_alias_quotation(self):
......
...@@ -16,9 +16,10 @@ ...@@ -16,9 +16,10 @@
import unittest import unittest
from test.topydo_testcase import TopydoTest
from topydo.lib.Graph import DirectedGraph from topydo.lib.Graph import DirectedGraph
from .topydo_testcase import TopydoTest
class GraphTest(TopydoTest): class GraphTest(TopydoTest):
def setUp(self): def setUp(self):
......
...@@ -15,14 +15,16 @@ ...@@ -15,14 +15,16 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest import unittest
from datetime import date, timedelta from datetime import date
from freezegun import freeze_time from freezegun import freeze_time
from test.topydo_testcase import TopydoTest
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.Importance import importance from topydo.lib.Importance import importance
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
from .topydo_testcase import TopydoTest
@freeze_time("2015, 11, 06") @freeze_time("2015, 11, 06")
class ImportanceTest(TopydoTest): class ImportanceTest(TopydoTest):
......
...@@ -16,10 +16,11 @@ ...@@ -16,10 +16,11 @@
import unittest import unittest
from test.topydo_testcase import TopydoTest
from topydo.lib.printers.Json import JsonPrinter from topydo.lib.printers.Json import JsonPrinter
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
from .topydo_testcase import TopydoTest
class JsonPrinterTest(TopydoTest): class JsonPrinterTest(TopydoTest):
""" """
......
...@@ -15,19 +15,21 @@ ...@@ -15,19 +15,21 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import codecs import codecs
import re
import os import os
import re
import sys import sys
import unittest import unittest
from collections import namedtuple from collections import namedtuple
from freezegun import freeze_time from freezegun import freeze_time
from test.command_testcase import CommandTest
from test.facilities import load_file_to_todolist
from topydo.commands.ListCommand import ListCommand from topydo.commands.ListCommand import ListCommand
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .command_testcase import CommandTest
from .facilities import load_file_to_todolist
# We're searching for 'mock' # We're searching for 'mock'
# 'mock' was added as 'unittest.mock' in Python 3.3, but PyPy 3 is based on Python 3.2 # 'mock' was added as 'unittest.mock' in Python 3.3, but PyPy 3 is based on Python 3.2
# pylint: disable=no-name-in-module # pylint: disable=no-name-in-module
......
...@@ -16,10 +16,11 @@ ...@@ -16,10 +16,11 @@
import unittest import unittest
from test.command_testcase import CommandTest
from test.facilities import load_file_to_todolist
from topydo.commands.ListContextCommand import ListContextCommand from topydo.commands.ListContextCommand import ListContextCommand
from .command_testcase import CommandTest
from .facilities import load_file_to_todolist
class ListContextCommandTest(CommandTest): class ListContextCommandTest(CommandTest):
def test_contexts1(self): def test_contexts1(self):
......
...@@ -15,16 +15,17 @@ ...@@ -15,16 +15,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest import unittest
from collections import namedtuple from collections import namedtuple
from freezegun import freeze_time from freezegun import freeze_time
from test.command_testcase import CommandTest
from test.facilities import load_file_to_todolist
from topydo.commands.ListCommand import ListCommand from topydo.commands.ListCommand import ListCommand
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.TodoListBase import TodoListBase from topydo.lib.TodoListBase import TodoListBase
from .command_testcase import CommandTest
from .facilities import load_file_to_todolist
# We're searching for 'mock' # We're searching for 'mock'
# 'mock' was added as 'unittest.mock' in Python 3.3, but PyPy 3 is based on Python 3.2 # 'mock' was added as 'unittest.mock' in Python 3.3, but PyPy 3 is based on Python 3.2
# pylint: disable=no-name-in-module # pylint: disable=no-name-in-module
......
...@@ -16,10 +16,11 @@ ...@@ -16,10 +16,11 @@
import unittest import unittest
from test.command_testcase import CommandTest
from test.facilities import load_file_to_todolist
from topydo.commands.ListProjectCommand import ListProjectCommand from topydo.commands.ListProjectCommand import ListProjectCommand
from .command_testcase import CommandTest
from .facilities import load_file_to_todolist
class ListProjectCommandTest(CommandTest): class ListProjectCommandTest(CommandTest):
def test_projects1(self): def test_projects1(self):
......
...@@ -17,10 +17,11 @@ ...@@ -17,10 +17,11 @@
import unittest import unittest
from datetime import date, timedelta from datetime import date, timedelta
from test.command_testcase import CommandTest
from topydo.commands.PostponeCommand import PostponeCommand from topydo.commands.PostponeCommand import PostponeCommand
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .command_testcase import CommandTest
class PostponeCommandTest(CommandTest): class PostponeCommandTest(CommandTest):
def setUp(self): def setUp(self):
......
...@@ -16,10 +16,11 @@ ...@@ -16,10 +16,11 @@
import unittest import unittest
from test.command_testcase import CommandTest
from topydo.commands.PriorityCommand import PriorityCommand from topydo.commands.PriorityCommand import PriorityCommand
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .command_testcase import CommandTest
class PriorityCommandTest(CommandTest): class PriorityCommandTest(CommandTest):
def setUp(self): def setUp(self):
......
...@@ -14,15 +14,18 @@ ...@@ -14,15 +14,18 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from freezegun import freeze_time
import unittest import unittest
from test.topydo_testcase import TopydoTest from freezegun import freeze_time
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.ProgressColor import progress_color from topydo.lib.ProgressColor import progress_color
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .topydo_testcase import TopydoTest
def set_256_colors(): def set_256_colors():
config(p_overrides={('topydo', 'colors'): '256'}) config(p_overrides={('topydo', 'colors'): '256'})
......
...@@ -17,11 +17,12 @@ ...@@ -17,11 +17,12 @@
import unittest import unittest
from datetime import date, timedelta from datetime import date, timedelta
from test.topydo_testcase import TopydoTest
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.Recurrence import NoRecurrenceException, advance_recurring_todo from topydo.lib.Recurrence import NoRecurrenceException, advance_recurring_todo
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
from .topydo_testcase import TopydoTest
class RecurrenceTest(TopydoTest): class RecurrenceTest(TopydoTest):
def setUp(self): def setUp(self):
......
...@@ -16,11 +16,13 @@ ...@@ -16,11 +16,13 @@
import unittest import unittest
from datetime import date from datetime import date
from freezegun import freeze_time from freezegun import freeze_time
from test.topydo_testcase import TopydoTest
from topydo.lib.RelativeDate import relative_date_to_date from topydo.lib.RelativeDate import relative_date_to_date
from .topydo_testcase import TopydoTest
@freeze_time('2015, 11, 06') @freeze_time('2015, 11, 06')
class RelativeDateTester(TopydoTest): class RelativeDateTester(TopydoTest):
......
...@@ -17,12 +17,12 @@ ...@@ -17,12 +17,12 @@
import os import os
import tempfile import tempfile
import unittest import unittest
from datetime import date from datetime import date
from glob import glob from glob import glob
from uuid import uuid4 from uuid import uuid4
from test.command_testcase import CommandTest from freezegun import freeze_time
from topydo.commands.AddCommand import AddCommand from topydo.commands.AddCommand import AddCommand
from topydo.commands.ArchiveCommand import ArchiveCommand from topydo.commands.ArchiveCommand import ArchiveCommand
from topydo.commands.DeleteCommand import DeleteCommand from topydo.commands.DeleteCommand import DeleteCommand
...@@ -33,6 +33,35 @@ from topydo.lib.Config import config ...@@ -33,6 +33,35 @@ from topydo.lib.Config import config
from topydo.lib.TodoFile import TodoFile from topydo.lib.TodoFile import TodoFile
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .command_testcase import CommandTest
# We're searching for 'mock'
# 'mock' was added as 'unittest.mock' in Python 3.3, but PyPy 3 is based on Python 3.2
# pylint: disable=no-name-in-module
try:
from unittest import mock
except ImportError:
import mock
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 command_executer(p_cmd, p_args, p_todolist, p_archive=None, *params):
command = p_cmd(p_args, p_todolist, *params)
command.execute()
if p_archive:
archive_command = ArchiveCommand(p_todolist, p_archive)
archive_command.execute()
@freeze_time('2015, 11, 06')
class RevertCommandTest(CommandTest): class RevertCommandTest(CommandTest):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
...@@ -57,14 +86,8 @@ class RevertCommandTest(CommandTest): ...@@ -57,14 +86,8 @@ class RevertCommandTest(CommandTest):
self.archive = TodoList([]) self.archive = TodoList([])
def test_revert01(self): def test_revert01(self):
backup = ChangeSet(p_label=['do 1']) backup = BackupSimulator(self.todolist, self.archive, '1', ['do 1'])
backup.add_todolist(self.todolist) command_executer(DoCommand, ["1"], self.todolist, self.archive, self.out, self.error, None)
backup.add_archive(self.archive)
backup.timestamp = '1'
command = DoCommand(["1"], self.todolist, self.out, self.error, None)
command.execute()
archive_command = ArchiveCommand(self.todolist, self.archive)
archive_command.execute()
self.archive_file.write(self.archive.print_todos()) self.archive_file.write(self.archive.print_todos())
backup.save(self.todolist) backup.save(self.todolist)
...@@ -77,26 +100,22 @@ class RevertCommandTest(CommandTest): ...@@ -77,26 +100,22 @@ class RevertCommandTest(CommandTest):
result = TodoList(self.archive_file.read()).print_todos() result = TodoList(self.archive_file.read()).print_todos()
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
self.assertTrue(self.output.endswith("Successfully reverted: do 1\n")) self.assertTrue(self.output.endswith("Reverted to state before: do 1\n"))
self.assertEqual(result, "") self.assertEqual(result, "")
self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz") self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz")
def test_revert02(self): def test_revert02(self):
backup = ChangeSet(self.todolist, self.archive, ['do 1']) backup = BackupSimulator(self.todolist, self.archive, '1', ['do 1'])
backup.timestamp = '1' command_executer(DoCommand, ["1"], self.todolist, self.archive, self.out, self.error, None)
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()) self.archive_file.write(self.archive.print_todos())
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['do Bar']) # 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' backup.timestamp = '2'
command2 = DoCommand(["Bar"], self.todolist, self.out, self.error, None) command_executer(DoCommand, ["Bar"], self.todolist, self.archive, 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()) self.archive_file.write(self.archive.print_todos())
backup.save(self.todolist) backup.save(self.todolist)
...@@ -109,7 +128,7 @@ class RevertCommandTest(CommandTest): ...@@ -109,7 +128,7 @@ class RevertCommandTest(CommandTest):
result = TodoList(self.archive_file.read()).print_todos() result = TodoList(self.archive_file.read()).print_todos()
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
self.assertTrue(self.output.endswith("Successfully reverted: do Bar\n")) self.assertTrue(self.output.endswith("Reverted to state before: do Bar\n"))
self.assertEqual(result, "x {} Foo".format(self.today)) self.assertEqual(result, "x {} Foo".format(self.today))
self.assertEqual(self.todolist.print_todos(), "Bar\nBaz") self.assertEqual(self.todolist.print_todos(), "Bar\nBaz")
...@@ -120,51 +139,39 @@ class RevertCommandTest(CommandTest): ...@@ -120,51 +139,39 @@ class RevertCommandTest(CommandTest):
self.assertEqual(self.errors, "No backup was found for the current state of {}\n".format(config().todotxt())) self.assertEqual(self.errors, "No backup was found for the current state of {}\n".format(config().todotxt()))
def test_revert04(self): @mock.patch('topydo.lib.Config._Config.archive')
def test_revert04(self, mock_archive):
""" Test trimming of the backup_file """ """ Test trimming of the backup_file """
backup = ChangeSet(self.todolist, self.archive, ['add One']) mock_archive.return_value = '' # test for empty archive setting
backup.timestamp = '1' backup = BackupSimulator(self.todolist, self.archive, '1', ['add One'])
command1 = AddCommand(["One"], self.todolist, self.out, self.error, None) command_executer(AddCommand, ["One"], self.todolist, None, self.out, self.error, None)
command1.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['add Two']) backup = BackupSimulator(self.todolist, self.archive, '2', ['add Two'])
backup.timestamp = '2' command_executer(AddCommand, ["Two"], self.todolist, None, self.out, self.error, None)
command2 = AddCommand(["Two"], self.todolist, self.out, self.error, None)
command2.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['add Three']) backup = BackupSimulator(self.todolist, self.archive, '3', ['add Three'])
backup.timestamp = '3' command_executer(AddCommand, ["Three"], self.todolist, None, self.out, self.error, None)
command3 = AddCommand(["Three"], self.todolist, self.out, self.error, None)
command3.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['add Four']) backup = BackupSimulator(self.todolist, self.archive, '4', ['add Four'])
backup.timestamp = '4' command_executer(AddCommand, ["Four"], self.todolist, None, self.out, self.error, None)
command4 = AddCommand(["Four"], self.todolist, self.out, self.error, None)
command4.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['add Five']) backup = BackupSimulator(self.todolist, self.archive, '5', ['add Five'])
backup.timestamp = '5' command_executer(AddCommand, ["Five"], self.todolist, None, self.out, self.error, None)
command5 = AddCommand(["Five"], self.todolist, self.out, self.error, None)
command5.execute()
backup.save(self.todolist) backup.save(self.todolist)
result = len(ChangeSet().backup_dict.keys()) result = len(ChangeSet().backup_dict.keys())
self.assertEqual(result, 6) self.assertEqual(result, 6)
backup = ChangeSet(self.todolist, self.archive, ['add Six']) backup = BackupSimulator(self.todolist, self.archive, '6', ['add Six'])
backup.timestamp = '6' command_executer(AddCommand, ["Six"], self.todolist, None, self.out, self.error, None)
command6 = AddCommand(["Six"], self.todolist, self.out, self.error, None)
command6.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['add Seven']) backup = BackupSimulator(self.todolist, self.archive, '7', ['add Seven'])
backup.timestamp = '7' command_executer(AddCommand, ["Seven"], self.todolist, None, self.out, self.error, None)
command7 = AddCommand(["Seven"], self.todolist, self.out, self.error, None)
command7.execute()
backup.save(self.todolist) backup.save(self.todolist)
result = len(ChangeSet().backup_dict.keys()) result = len(ChangeSet().backup_dict.keys())
...@@ -182,94 +189,76 @@ class RevertCommandTest(CommandTest): ...@@ -182,94 +189,76 @@ class RevertCommandTest(CommandTest):
self.assertEqual(len(changesets), 4) self.assertEqual(len(changesets), 4)
self.assertEqual(result, []) self.assertEqual(result, [])
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
self.assertTrue(self.output.endswith("Successfully reverted: add Seven\n")) self.assertTrue(self.output.endswith("Reverted to state before: add Seven\n"))
def test_revert05(self): def test_revert05(self):
""" Test for possible backup collisions """ """ Test for possible backup collisions """
backup = ChangeSet(self.todolist, self.archive, ['add One']) backup = BackupSimulator(self.todolist, self.archive, '1', ['add One'])
backup.timestamp = '1' command_executer(AddCommand, ["One"], self.todolist, None, self.out, self.error, None)
command1 = AddCommand(["One"], self.todolist, self.out, self.error, None)
command1.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['add Two']) backup = BackupSimulator(self.todolist, self.archive, '2', ['add Two'])
backup.timestamp = '2' command_executer(AddCommand, ["Two"], self.todolist, None, self.out, self.error, None)
command2 = AddCommand(["Two"], self.todolist, self.out, self.error, None)
command2.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['add Three']) backup = BackupSimulator(self.todolist, self.archive, '3', ['add Three'])
backup.timestamp = '3' command_executer(AddCommand, ["Three"], self.todolist, None, self.out, self.error, None)
command3 = AddCommand(["Three"], self.todolist, self.out, self.error, None)
command3.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['delete Three']) backup = BackupSimulator(self.todolist, self.archive, '4', ['delete Three'])
backup.timestamp = '4' command_executer(DeleteCommand, ["Three"], self.todolist, None, self.out, self.error, None)
command4 = DeleteCommand(["Three"], self.todolist, self.out, self.error, None)
command4.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['add Four']) backup = BackupSimulator(self.todolist, self.archive, '5', ['add Four'])
backup.timestamp = '5' command_executer(AddCommand, ["Four"], self.todolist, None, self.out, self.error, None)
command4 = AddCommand(["Four"], self.todolist, self.out, self.error, None)
command4.execute()
backup.save(self.todolist) backup.save(self.todolist)
revert_command = RevertCommand([], self.todolist, self.out, self.error, None) revert_command = RevertCommand([], self.todolist, self.out, self.error, None)
revert_command.execute() revert_command.execute()
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
self.assertTrue(self.output.endswith("Successfully reverted: add Four\n")) self.assertTrue(self.output.endswith("Reverted to state before: add Four\n"))
self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz\n{t} One\n{t} Two".format(t=self.today)) 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 = RevertCommand([], self.todolist, self.out, self.error, None)
revert_command.execute() revert_command.execute()
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
self.assertTrue(self.output.endswith("Successfully reverted: delete Three\n")) self.assertTrue(self.output.endswith("Reverted to state before: 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)) 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 = RevertCommand([], self.todolist, self.out, self.error, None)
revert_command.execute() revert_command.execute()
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
self.assertTrue(self.output.endswith("Successfully reverted: add Three\n")) self.assertTrue(self.output.endswith("Reverted to state before: add Three\n"))
self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz\n{t} One\n{t} Two".format(t=self.today)) 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 = RevertCommand([], self.todolist, self.out, self.error, None)
revert_command.execute() revert_command.execute()
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
self.assertTrue(self.output.endswith("Successfully reverted: add Two\n")) self.assertTrue(self.output.endswith("Reverted to state before: add Two\n"))
self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz\n{t} One".format(t=self.today)) 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 = RevertCommand([], self.todolist, self.out, self.error, None)
revert_command.execute() revert_command.execute()
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
self.assertTrue(self.output.endswith("Successfully reverted: add One\n")) self.assertTrue(self.output.endswith("Reverted to state before: add One\n"))
self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz") self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz")
def test_revert06(self): def test_revert06(self):
""" Test attempt of deletion with non-existing backup key""" """ Test attempt of deletion with non-existing backup key"""
backup = ChangeSet(self.todolist, self.archive, ['add One']) backup = BackupSimulator(self.todolist, self.archive, '1', ['add One'])
backup.timestamp = '1' command_executer(AddCommand, ["One"], self.todolist, None, self.out, self.error, None)
command1 = AddCommand(["One"], self.todolist, self.out, self.error, None)
command1.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['add Two']) backup = BackupSimulator(self.todolist, self.archive, '2', ['add Two'])
backup.timestamp = '2' command_executer(AddCommand, ["Two"], self.todolist, None, self.out, self.error, None)
command2 = AddCommand(["Two"], self.todolist, self.out, self.error, None)
command2.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['add Three']) backup = BackupSimulator(self.todolist, self.archive, '3', ['add Three'])
backup.timestamp = '3' command_executer(AddCommand, ["Three"], self.todolist, None, self.out, self.error, None)
command3 = AddCommand(["Three"], self.todolist, self.out, self.error, None)
command3.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet(self.todolist, self.archive, ['delete Three']) backup = BackupSimulator(self.todolist, self.archive, '4', ['delete Three'])
backup.timestamp = '4' command_executer(DeleteCommand, ["Three"], self.todolist, None, self.out, self.error, None)
command4 = DeleteCommand(["Three"], self.todolist, self.out, self.error, None)
command4.execute()
backup.save(self.todolist) backup.save(self.todolist)
backup = ChangeSet() backup = ChangeSet()
...@@ -288,8 +277,7 @@ class RevertCommandTest(CommandTest): ...@@ -288,8 +277,7 @@ class RevertCommandTest(CommandTest):
""" Test backup when no archive file is set """ """ Test backup when no archive file is set """
backup = ChangeSet(self.todolist, None, ['add One']) backup = ChangeSet(self.todolist, None, ['add One'])
backup.timestamp = '1' backup.timestamp = '1'
command1 = AddCommand(["One"], self.todolist, self.out, self.error, None) command_executer(AddCommand, ["One"], self.todolist, None, self.out, self.error, None)
command1.execute()
backup.save(self.todolist) backup.save(self.todolist)
changesets = list(backup.backup_dict.keys()) changesets = list(backup.backup_dict.keys())
...@@ -298,6 +286,99 @@ class RevertCommandTest(CommandTest): ...@@ -298,6 +286,99 @@ class RevertCommandTest(CommandTest):
self.assertEqual(len(changesets), 1) self.assertEqual(len(changesets), 1)
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_revert_ls(self):
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 = BackupSimulator(self.todolist, self.archive, '5', ['add Four'])
command_executer(AddCommand, ["Four"], self.todolist, None, self.out, self.error, None)
backup.save(self.todolist)
command_executer(RevertCommand, ['ls'], self.todolist, None, self.out, self.error, None)
self.assertEqual(self.errors, "")
self.assertTrue(self.output.endswith(""" 1| 1970-01-01 00:00:05 | add Four
2| 1970-01-01 00:00:04 | delete Three
3| 1970-01-01 00:00:03 | add Three
4| 1970-01-01 00:00:02 | add Two
5| 1970-01-01 00:00:01 | add One\n"""))
def test_revert_08(self):
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 = BackupSimulator(self.todolist, self.archive, '5', ['add Four'])
command_executer(AddCommand, ["Four"], self.todolist, None, self.out, self.error, None)
backup.save(self.todolist)
command_executer(RevertCommand, ['3'], self.todolist, None, self.out, self.error, None)
self.assertEqual(self.errors, "")
self.assertTrue(self.output.endswith("Reverted to state before: add Three\n"))
self.assertEqual(self.todolist.print_todos(), "Foo\nBar\nBaz\n2015-11-06 One\n2015-11-06 Two")
def test_revert_invalid(self):
""" Test invalid input for revert. """
command_executer(RevertCommand, ["foo"], self.todolist, None, self.out, self.error, None)
command_executer(RevertCommand, ["ls", "foo"], self.todolist, None, self.out, self.error, None)
command_executer(RevertCommand, ["1", "foo"], self.todolist, None, self.out, self.error, None)
usage_text = RevertCommand([], self.todolist).usage() + '\n'
self.assertEqual(self.errors, usage_text*3)
def test_revert_out_of_range(self):
command_executer(RevertCommand, ["158"], self.todolist, None, self.out, self.error, None)
self.assertEqual(self.errors, "Specified index is out range\n")
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_backup_config01(self): def test_backup_config01(self):
config(p_overrides={('topydo', 'backup_count'): '1'}) config(p_overrides={('topydo', 'backup_count'): '1'})
......
...@@ -16,11 +16,12 @@ ...@@ -16,11 +16,12 @@
import unittest import unittest
from test.command_testcase import CommandTest
from test.facilities import load_file_to_todolist
from topydo.commands.SortCommand import SortCommand from topydo.commands.SortCommand import SortCommand
from topydo.lib.Config import config from topydo.lib.Config import config
from .command_testcase import CommandTest
from .facilities import load_file_to_todolist
class SortCommandTest(CommandTest): class SortCommandTest(CommandTest):
def setUp(self): def setUp(self):
......
...@@ -14,15 +14,17 @@ ...@@ -14,15 +14,17 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from freezegun import freeze_time
import unittest import unittest
from test.facilities import (load_file, load_file_to_todolist, print_view, from freezegun import freeze_time
todolist_to_string)
from test.topydo_testcase import TopydoTest
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.Sorter import Sorter from topydo.lib.Sorter import Sorter
from .facilities import (load_file, load_file_to_todolist, print_view,
todolist_to_string)
from .topydo_testcase import TopydoTest
@freeze_time("2016, 04, 25") @freeze_time("2016, 04, 25")
class SorterTest(TopydoTest): class SorterTest(TopydoTest):
......
...@@ -14,13 +14,15 @@ ...@@ -14,13 +14,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from freezegun import freeze_time
import unittest import unittest
from test.command_testcase import CommandTest from freezegun import freeze_time
from topydo.commands.TagCommand import TagCommand from topydo.commands.TagCommand import TagCommand
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .command_testcase import CommandTest
class TagCommandTest(CommandTest): class TagCommandTest(CommandTest):
def setUp(self): def setUp(self):
......
...@@ -17,9 +17,10 @@ ...@@ -17,9 +17,10 @@
import unittest import unittest
from datetime import date, timedelta from datetime import date, timedelta
from test.topydo_testcase import TopydoTest
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
from .topydo_testcase import TopydoTest
def today_date(): def today_date():
today = date.today() today = date.today()
......
...@@ -20,9 +20,10 @@ import re ...@@ -20,9 +20,10 @@ import re
import unittest import unittest
from datetime import date, timedelta from datetime import date, timedelta
from test.topydo_testcase import TopydoTest
from topydo.lib.TodoBase import TodoBase from topydo.lib.TodoBase import TodoBase
from .topydo_testcase import TopydoTest
class TodoBaseTester(TopydoTest): class TodoBaseTester(TopydoTest):
def test_parse_tag(self): def test_parse_tag(self):
......
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
import unittest import unittest
from test.facilities import load_file from .facilities import load_file
from test.topydo_testcase import TopydoTest from .topydo_testcase import TopydoTest
class TodoFileTest(TopydoTest): class TodoFileTest(TopydoTest):
......
...@@ -19,13 +19,14 @@ ...@@ -19,13 +19,14 @@
import re import re
import unittest import unittest
from test.topydo_testcase import TopydoTest
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
from topydo.lib.TodoFile import TodoFile from topydo.lib.TodoFile import TodoFile
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from topydo.lib.TodoListBase import InvalidTodoException, TodoListBase from topydo.lib.TodoListBase import InvalidTodoException, TodoListBase
from .topydo_testcase import TopydoTest
class TodoListTester(TopydoTest): class TodoListTester(TopydoTest):
def setUp(self): def setUp(self):
......
...@@ -16,9 +16,10 @@ ...@@ -16,9 +16,10 @@
import unittest import unittest
from test.topydo_testcase import TopydoTest
from topydo.lib.Utils import translate_key_to_config from topydo.lib.Utils import translate_key_to_config
from .topydo_testcase import TopydoTest
class UtilsTest(TopydoTest): class UtilsTest(TopydoTest):
def test_key_to_cfg(self): def test_key_to_cfg(self):
......
...@@ -16,13 +16,14 @@ ...@@ -16,13 +16,14 @@
import unittest import unittest
from test.facilities import load_file, print_view, todolist_to_string
from test.topydo_testcase import TopydoTest
from topydo.lib import Filter from topydo.lib import Filter
from topydo.lib.Sorter import Sorter from topydo.lib.Sorter import Sorter
from topydo.lib.TodoFile import TodoFile from topydo.lib.TodoFile import TodoFile
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
from .facilities import load_file, print_view, todolist_to_string
from .topydo_testcase import TopydoTest
class ViewTest(TopydoTest): class ViewTest(TopydoTest):
def test_view(self): def test_view(self):
......
...@@ -21,7 +21,7 @@ instance based on an argument list. ...@@ -21,7 +21,7 @@ instance based on an argument list.
import sys import sys
from topydo.lib.Config import config, ConfigError from topydo.lib.Config import ConfigError, config
SUBCOMMAND_MAP = { SUBCOMMAND_MAP = {
'add': 'AddCommand', 'add': 'AddCommand',
......
...@@ -22,9 +22,9 @@ from datetime import date ...@@ -22,9 +22,9 @@ from datetime import date
from os.path import expanduser from os.path import expanduser
from sys import stdin from sys import stdin
from topydo.lib.WriteCommand import WriteCommand
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers
from topydo.lib.WriteCommand import WriteCommand
class AddCommand(WriteCommand): class AddCommand(WriteCommand):
......
...@@ -14,12 +14,13 @@ ...@@ -14,12 +14,13 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from topydo.lib.Config import config
from topydo.lib.Command import InvalidCommandArgument from topydo.lib.Command import InvalidCommandArgument
from topydo.lib.WriteCommand import WriteCommand from topydo.lib.Config import config
from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers
from topydo.lib.TodoListBase import InvalidTodoException from topydo.lib.TodoListBase import InvalidTodoException
from topydo.lib.TodoParser import parse_line from topydo.lib.TodoParser import parse_line
from topydo.lib.WriteCommand import WriteCommand
class AppendCommand(WriteCommand): class AppendCommand(WriteCommand):
def __init__(self, p_args, p_todolist, #pragma: no branch def __init__(self, p_args, p_todolist, #pragma: no branch
......
...@@ -35,6 +35,9 @@ class DoCommand(DCommand): ...@@ -35,6 +35,9 @@ class DoCommand(DCommand):
super().__init__( super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt) p_args, p_todolist, p_out, p_err, p_prompt)
self.condition = lambda todo: not todo.is_completed()
self.condition_failed_text = "Todo has already been completed."
def get_flags(self): def get_flags(self):
""" Additional flags. """ """ Additional flags. """
opts, long_opts = super().get_flags() opts, long_opts = super().get_flags()
...@@ -75,15 +78,6 @@ class DoCommand(DCommand): ...@@ -75,15 +78,6 @@ class DoCommand(DCommand):
def prefix(self): def prefix(self):
return "Completed: " return "Completed: "
def condition(self, p_todo):
"""
An additional condition whether execute_specific should be executed.
"""
return not p_todo.is_completed()
def condition_failed_text(self):
return "Todo has already been completed."
def execute_specific(self, p_todo): def execute_specific(self, p_todo):
""" Actions specific to this command. """ """ Actions specific to this command. """
self._handle_recurrence(p_todo) self._handle_recurrence(p_todo)
......
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import codecs import codecs
import tempfile import os
import shlex import shlex
import tempfile
from subprocess import CalledProcessError, check_call from subprocess import CalledProcessError, check_call
from topydo.lib.Config import config from topydo.lib.Config import config
...@@ -25,6 +25,7 @@ from topydo.lib.MultiCommand import MultiCommand ...@@ -25,6 +25,7 @@ from topydo.lib.MultiCommand import MultiCommand
from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
def _get_file_mtime(p_file): def _get_file_mtime(p_file):
return os.stat(p_file.name).st_mtime return os.stat(p_file.name).st_mtime
...@@ -44,11 +45,11 @@ class EditCommand(MultiCommand): ...@@ -44,11 +45,11 @@ class EditCommand(MultiCommand):
def get_flags(self): def get_flags(self):
return ("dE:", []) return ("dE:", [])
def process_flag(self, p_opt, p_value): def process_flag(self, p_option, p_value):
if p_opt == '-d': if p_option == '-d':
self.edit_archive = True self.edit_archive = True
self.multi_mode = False self.multi_mode = False
elif p_opt == '-E': elif p_option == '-E':
self.editor = shlex.split(p_value) self.editor = shlex.split(p_value)
def _process_flags(self): def _process_flags(self):
...@@ -71,9 +72,10 @@ class EditCommand(MultiCommand): ...@@ -71,9 +72,10 @@ class EditCommand(MultiCommand):
return f return f
def _todos_from_temp(self, p_temp_file): @staticmethod
f = codecs.open(p_temp_file.name, encoding='utf-8') def _todos_from_temp(p_temp_file):
todos = f.read().splitlines() with codecs.open(p_temp_file.name, encoding='utf-8') as temp:
todos = temp.read().splitlines()
todo_objs = [] todo_objs = []
for todo in todos: for todo in todos:
...@@ -112,7 +114,7 @@ class EditCommand(MultiCommand): ...@@ -112,7 +114,7 @@ class EditCommand(MultiCommand):
orig_mtime = _get_file_mtime(temp_todos) orig_mtime = _get_file_mtime(temp_todos)
if not self._open_in_editor(temp_todos.name): if not self._open_in_editor(temp_todos.name):
new_todos = self._todos_from_temp(temp_todos) new_todos = EditCommand._todos_from_temp(temp_todos)
if _is_edited(orig_mtime, temp_todos): if _is_edited(orig_mtime, temp_todos):
for todo in self.todos: for todo in self.todos:
......
...@@ -14,18 +14,18 @@ ...@@ -14,18 +14,18 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import re import re
import sys import sys
import os
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.ExpressionCommand import ExpressionCommand from topydo.lib.ExpressionCommand import ExpressionCommand
from topydo.lib.Filter import HiddenTagFilter, InstanceFilter from topydo.lib.Filter import HiddenTagFilter, InstanceFilter
from topydo.lib.ListFormat import ListFormatError from topydo.lib.ListFormat import ListFormatError
from topydo.lib.printers.PrettyPrinter import pretty_printer_factory
from topydo.lib.prettyprinters.Format import PrettyPrinterFormatFilter from topydo.lib.prettyprinters.Format import PrettyPrinterFormatFilter
from topydo.lib.TodoListBase import InvalidTodoException from topydo.lib.printers.PrettyPrinter import pretty_printer_factory
from topydo.lib.Sorter import Sorter from topydo.lib.Sorter import Sorter
from topydo.lib.TodoListBase import InvalidTodoException
from topydo.lib.Utils import get_terminal_size from topydo.lib.Utils import get_terminal_size
from topydo.lib.View import View from topydo.lib.View import View
...@@ -90,7 +90,7 @@ class ListCommand(ExpressionCommand): ...@@ -90,7 +90,7 @@ class ListCommand(ExpressionCommand):
elif opt == '-N': elif opt == '-N':
# 2 lines are assumed to be taken up by printing the next prompt # 2 lines are assumed to be taken up by printing the next prompt
# display at least one item # display at least one item
self.limit = self._N_lines() self.limit = ListCommand._N_lines()
elif opt == '-n': elif opt == '-n':
try: try:
self.limit = int(value) self.limit = int(value)
...@@ -164,7 +164,8 @@ class ListCommand(ExpressionCommand): ...@@ -164,7 +164,8 @@ class ListCommand(ExpressionCommand):
return View(sorter, filters, self.todolist) return View(sorter, filters, self.todolist)
def _N_lines(self): @staticmethod
def _N_lines():
''' Determine how many lines to print, such that the number of items ''' Determine how many lines to print, such that the number of items
displayed will fit on the terminal (i.e one 'screen-ful' of items) displayed will fit on the terminal (i.e one 'screen-ful' of items)
...@@ -176,13 +177,13 @@ class ListCommand(ExpressionCommand): ...@@ -176,13 +177,13 @@ class ListCommand(ExpressionCommand):
Otherwise, it looks for a newline ('\n') in the environmental variable Otherwise, it looks for a newline ('\n') in the environmental variable
PS1. PS1.
''' '''
lines_in_prompt = 1 # prompt is assumed to take up one line, even lines_in_prompt = 1 # prompt is assumed to take up one line, even
# without any newlines in it # without any newlines in it
if "win32" in sys.platform: if "win32" in sys.platform:
lines_in_prompt += 1 # Windows will typically print a free line after lines_in_prompt += 1 # Windows will typically print a free line after
# the program output # the program output
a = re.findall('\$_', os.getenv('PROMPT', '')) a = re.findall(r'\$_', os.getenv('PROMPT', ''))
lines_in_prompt += len(a) lines_in_prompt += len(a)
else: else:
a = re.findall('\\n', os.getenv('PS1', '')) a = re.findall('\\n', os.getenv('PS1', ''))
......
...@@ -37,8 +37,8 @@ class PostponeCommand(MultiCommand): ...@@ -37,8 +37,8 @@ class PostponeCommand(MultiCommand):
def get_flags(self): def get_flags(self):
return("s", []) return("s", [])
def process_flag(self, p_opt, p_value): def process_flag(self, p_option, p_value):
if p_opt == '-s': if p_option == '-s':
self.move_start_date = True self.move_start_date = True
def _execute_multi_specific(self): def _execute_multi_specific(self):
......
# Topydo - A todo.txt client written in Python. # Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 - 2015 Bram Schoenmakers <bram@topydo.org> # Copyright (C) 2014 - 2017 Bram Schoenmakers <bram@topydo.org>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
...@@ -14,44 +14,119 @@ ...@@ -14,44 +14,119 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from topydo.lib.Command import Command import arrow
from topydo.lib import TodoFile, TodoList
from topydo.lib.ChangeSet import ChangeSet from topydo.lib.ChangeSet import ChangeSet
from topydo.lib import TodoFile from topydo.lib.Command import Command, InvalidCommandArgument
from topydo.lib import TodoList
from topydo.lib.Config import config from topydo.lib.Config import config
class RevertCommand(Command): class RevertCommand(Command):
def __init__(self, p_args, p_todolist, #pragma: no branch def __init__(self, p_args, p_todolist, # pragma: no branch
p_out=lambda a: None, p_out=lambda a: None,
p_err=lambda a: None, p_err=lambda a: None,
p_prompt=lambda a: None): p_prompt=lambda a: None):
super().__init__(p_args, p_todolist, p_out, p_err, super().__init__(p_args, p_todolist, p_out, p_err, p_prompt)
p_prompt)
self._backup = None
self._archive_file = None
self._archive = None
def execute(self): def execute(self):
if not super().execute(): if not super().execute():
return False return False
archive_file = TodoFile.TodoFile(config().archive()) self._backup = ChangeSet()
archive = TodoList.TodoList(archive_file.read()) 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())
last_change = ChangeSet() 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: try:
last_change.get_backup(self.todolist) timestamp = timestamps[position]
last_change.apply(self.todolist, archive) self._revert(timestamp)
archive_file.write(archive.print_todos()) for timestamp in timestamps[:position + 1]:
last_change.delete() self._backup.read_backup(p_timestamp=timestamp)
self._backup.delete()
except IndexError:
self.error('Specified index is out range')
self.out("Successfully reverted: " + last_change.label) def _handle_args(self, p_arg):
except (ValueError, KeyError): try:
self.error('No backup was found for the current state of ' + config().todotxt()) 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())
last_change.close() 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): def usage(self):
return """Synopsis: revert""" return """Synopsis:
revert [ls]
revert [NUMBER]"""
def help(self): def help(self):
return """Reverts the last command.""" return """\
Reverts last commands.
* ls : Lists all backups ordered and numbered chronologically (starting
with 1 for latest backup).
* [NUMBER] : revert to specific point in history specified by NUMBER.
Output example for `revert ls`:
1 | 1970-01-01 00:00:02 | add Baz
2 | 1970-01-01 00:00:01 | add Bar
3 | 1970-01-01 00:00:00 | add Foo
In such example executing `revert 2` will revert todo and archive files to the
state before execution of `add Bar`.
* `revert` without any further arguments will revert to the latest backup
available, provided that this backup matches current state of the todo file.
Topydo will refuse to revert, if any changes to todo file were made by
external application after the latest backup. To force a `revert` action use
it with a NUMBER.\
"""
# Topydo - A todo.txt client written in Python. # Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 - 2015 Bram Schoenmakers <bram@topydo.org> # Copyright (C) 2014 - 2017 Bram Schoenmakers <bram@topydo.org>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
import json import json
import time import time
import zlib import zlib
from copy import deepcopy from copy import deepcopy
from hashlib import sha1 from hashlib import sha1
from os import path from os import path
...@@ -27,6 +26,7 @@ from os import path ...@@ -27,6 +26,7 @@ from os import path
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
def hash_todolist(p_todolist): def hash_todolist(p_todolist):
""" Calculates hash for TodoList.TodoList object. """ """ Calculates hash for TodoList.TodoList object. """
todolist_hash = sha1(p_todolist.print_todos().encode('utf-8')).hexdigest() todolist_hash = sha1(p_todolist.print_todos().encode('utf-8')).hexdigest()
...@@ -43,11 +43,11 @@ def get_backup_path(): ...@@ -43,11 +43,11 @@ def get_backup_path():
class ChangeSet(object): class ChangeSet(object):
""" Class for operations related with backup management. """ """ Class for operations related with backup management. """
def __init__(self, p_todolist=None, p_archive=None, p_label=[]): def __init__(self, p_todolist=None, p_archive=None, p_label=None):
self.todolist = deepcopy(p_todolist) self.todolist = deepcopy(p_todolist)
self.archive = deepcopy(p_archive) self.archive = deepcopy(p_archive)
self.timestamp = str(int(time.time())) self.timestamp = str(time.time())
self.label = ' '.join(p_label) self.label = ' '.join(p_label if p_label else [])
try: try:
self.json_file = open(get_backup_path(), 'r+b') self.json_file = open(get_backup_path(), 'r+b')
...@@ -56,6 +56,11 @@ class ChangeSet(object): ...@@ -56,6 +56,11 @@ class ChangeSet(object):
self._read() self._read()
def __iter__(self):
items = {key: self.backup_dict[key]
for key in self.backup_dict if key != 'index'}.items()
return iter(sorted(items, reverse=True))
def _read(self): def _read(self):
""" """
Reads backup file from json_file property and sets backup_dict property Reads backup file from json_file property and sets backup_dict property
...@@ -158,15 +163,18 @@ class ChangeSet(object): ...@@ -158,15 +163,18 @@ class ChangeSet(object):
for changeset in index[backup_limit:]: for changeset in index[backup_limit:]:
self.delete(changeset[0], p_write=False) self.delete(changeset[0], p_write=False)
def get_backup(self, p_todolist): def read_backup(self, p_todolist=None, p_timestamp=None):
""" """
Retrieves a backup for p_todolist from backup file and sets todolist, Retrieves a backup for p_timestamp or p_todolist (if p_timestamp is not
archive and label attributes to appropriate data from it. specified) from backup file and sets timestamp, todolist, archive and
label attributes to appropriate data from it.
""" """
change_hash = hash_todolist(p_todolist) if not p_timestamp:
change_hash = hash_todolist(p_todolist)
index = self._get_index() index = self._get_index()
self.timestamp = index[[change[1] for change in index].index(change_hash)][0] self.timestamp = index[[change[1] for change in index].index(change_hash)][0]
else:
self.timestamp = p_timestamp
d = self.backup_dict[self.timestamp] d = self.backup_dict[self.timestamp]
...@@ -176,10 +184,10 @@ class ChangeSet(object): ...@@ -176,10 +184,10 @@ class ChangeSet(object):
def apply(self, p_todolist, p_archive): def apply(self, p_todolist, p_archive):
""" Applies backup on supplied p_todolist. """ """ Applies backup on supplied p_todolist. """
if self.todolist: if self.todolist and p_todolist:
p_todolist.replace(self.todolist.todos()) p_todolist.replace(self.todolist.todos())
if self.archive: if self.archive and p_archive:
p_archive.replace(self.archive.todos()) p_archive.replace(self.archive.todos())
def close(self): def close(self):
......
...@@ -15,17 +15,17 @@ ...@@ -15,17 +15,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import configparser import configparser
from functools import lru_cache
import os import os
import re import re
import shlex import shlex
from collections import OrderedDict from collections import OrderedDict
from functools import lru_cache
from itertools import accumulate from itertools import accumulate
from string import ascii_lowercase from string import ascii_lowercase
from topydo.lib.Color import Color from topydo.lib.Color import Color
def home_config_path(p_filename): def home_config_path(p_filename):
return os.path.join(os.path.expanduser('~'), p_filename) return os.path.join(os.path.expanduser('~'), p_filename)
......
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
import re import re
from topydo.lib.MultiCommand import MultiCommand from topydo.lib.MultiCommand import MultiCommand
from topydo.lib.printers.PrettyPrinter import PrettyPrinter
from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers
from topydo.lib.printers.PrettyPrinter import PrettyPrinter
class DCommand(MultiCommand): class DCommand(MultiCommand):
...@@ -36,13 +36,14 @@ class DCommand(MultiCommand): ...@@ -36,13 +36,14 @@ class DCommand(MultiCommand):
self.force = False self.force = False
self._delta = [] self._delta = []
self.condition = lambda _: True
self.condition_failed_text = ""
def get_flags(self): def get_flags(self):
return ("f", ["force"]) return ("f", ["force"])
def process_flag(self, p_opt, p_value): def process_flag(self, p_option, p_value):
if p_opt == "-f" or p_opt == "--force": if p_option == "-f" or p_option == "--force":
self.force = True self.force = True
def _uncompleted_children(self, p_todo): def _uncompleted_children(self, p_todo):
...@@ -93,15 +94,6 @@ class DCommand(MultiCommand): ...@@ -93,15 +94,6 @@ class DCommand(MultiCommand):
return [todo for todo in self.todolist.todos() return [todo for todo in self.todolist.todos()
if not self._uncompleted_children(todo) and todo.is_active()] if not self._uncompleted_children(todo) and todo.is_active()]
def condition(self, _):
"""
An additional condition whether execute_specific should be executed.
"""
return True
def condition_failed_text(self):
raise NotImplementedError
def execute_specific(self, _): def execute_specific(self, _):
raise NotImplementedError raise NotImplementedError
...@@ -120,7 +112,7 @@ class DCommand(MultiCommand): ...@@ -120,7 +112,7 @@ class DCommand(MultiCommand):
self._process_subtasks(todo) self._process_subtasks(todo)
self.execute_specific(todo) self.execute_specific(todo)
else: else:
self.error(self.condition_failed_text()) self.error(self.condition_failed_text)
current_active = self._active_todos() current_active = self._active_todos()
self._delta = [todo for todo in current_active self._delta = [todo for todo in current_active
......
...@@ -14,8 +14,6 @@ ...@@ -14,8 +14,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
from topydo.lib import Filter from topydo.lib import Filter
from topydo.lib.Command import Command from topydo.lib.Command import Command
from topydo.lib.Config import config from topydo.lib.Config import config
......
...@@ -16,12 +16,13 @@ ...@@ -16,12 +16,13 @@
""" Utilities for formatting output with "list_format" option.""" """ Utilities for formatting output with "list_format" option."""
import arrow
import re import re
import arrow
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.ProgressColor import progress_color from topydo.lib.ProgressColor import progress_color
from topydo.lib.Utils import get_terminal_size, escape_ansi, humanize_date from topydo.lib.Utils import escape_ansi, get_terminal_size, humanize_date
MAIN_PATTERN = (r'^({{(?P<before>.+?)}})?' MAIN_PATTERN = (r'^({{(?P<before>.+?)}})?'
r'(?P<placeholder>{ph}|\[{ph}\])' r'(?P<placeholder>{ph}|\[{ph}\])'
...@@ -205,7 +206,7 @@ class ListFormatParser(object): ...@@ -205,7 +206,7 @@ class ListFormatParser(object):
'T': lambda t: humanize_date(t.start_date()) if t.start_date() else '', 'T': lambda t: humanize_date(t.start_date()) if t.start_date() else '',
# unique text ID # unique text ID
'u': lambda t: self.todolist.uid(t), 'u': self.todolist.uid if self.todolist else lambda _ : '',
# unique text ID, padded with spaces # unique text ID, padded with spaces
'U': lambda t: _filler(self.todolist.uid(t), 'U': lambda t: _filler(self.todolist.uid(t),
......
...@@ -35,6 +35,7 @@ class MultiCommand(ExpressionCommand): ...@@ -35,6 +35,7 @@ class MultiCommand(ExpressionCommand):
self.is_expression = False self.is_expression = False
self.multi_mode = True self.multi_mode = True
# pylint: disable=no-self-use
def get_flags(self): def get_flags(self):
""" Default implementation of getting specific flags. """ """ Default implementation of getting specific flags. """
return ("", []) return ("", [])
......
...@@ -20,7 +20,6 @@ from topydo.lib.Color import Color ...@@ -20,7 +20,6 @@ from topydo.lib.Color import Color
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.Recurrence import relative_date_to_date from topydo.lib.Recurrence import relative_date_to_date
# when a todo item has not enough information to determine the length, assume # when a todo item has not enough information to determine the length, assume
# this length # this length
ASSUMED_TODO_LENGTH = 14 # days ASSUMED_TODO_LENGTH = 14 # days
......
...@@ -16,16 +16,15 @@ ...@@ -16,16 +16,15 @@
""" This module provides functionality to sort lists with todo items. """ """ This module provides functionality to sort lists with todo items. """
from collections import OrderedDict, namedtuple
from itertools import groupby
import re import re
from collections import OrderedDict, namedtuple
from datetime import date from datetime import date
from itertools import groupby
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.Importance import average_importance, importance from topydo.lib.Importance import average_importance, importance
from topydo.lib.Utils import date_string_to_date, humanize_date from topydo.lib.Utils import date_string_to_date, humanize_date
Field = namedtuple('Field', ['sort', 'group', 'label']) Field = namedtuple('Field', ['sort', 'group', 'label'])
FIELDS = { FIELDS = {
...@@ -148,9 +147,81 @@ class Sorter(object): ...@@ -148,9 +147,81 @@ class Sorter(object):
""" """
def __init__(self, p_sortstring="desc:priority", p_groupstring=""): def __init__(self, p_sortstring="desc:priority", p_groupstring=""):
self.groupfunctions = self._parse(p_groupstring, p_group=True) if p_groupstring else [] def parse(p_string, p_group):
self.pregroupfunctions = self._parse(p_groupstring, p_group=False) if p_groupstring else [] """
self.sortfunctions = self._parse(p_sortstring, p_group=False) Parses a sort/group string and returns a list of functions and the
desired order.
"""
def get_field_function(p_field, p_group=False):
"""
Turns a field, part of a sort/group string, into a lambda that
takes a todo item and returns the field value.
"""
compose = lambda i: i.sort if not p_group else (i.group, i.label)
def group_value(p_todo):
"""
Returns a value to assign the given todo to a group. Date tags
are grouped according to the relative date (1 day, 1 month,
...)
"""
result = 'No value'
if p_todo.has_tag(p_field):
if p_field == config().tag_due():
result = humanize_date(p_todo.due_date())
elif p_field == config().tag_start():
result = humanize_date(p_todo.start_date())
else:
result = p_todo.tag_value(p_field)
try:
result = humanize_date(date_string_to_date(result))
except ValueError:
pass
return result
if p_field in FIELD_MAP:
return compose(FIELDS[FIELD_MAP[p_field]])
else:
# treat it as a tag value
return compose(Field(
sort=lambda t: '0' + t.tag_value(p_field) if t.has_tag(p_field) else '1',
group=group_value,
label=p_field,
))
result = []
fields = p_string.lower().split(',')
for field in fields:
parsed_field = re.match(
r'(?P<order>(asc|desc)(ending)?:)?(?P<field>\S+)',
field)
if not parsed_field:
continue
order = parsed_field.group('order')
order = 'desc' if order and order.startswith('desc') else 'asc'
field = parsed_field.group('field')
if field:
function = get_field_function(field, p_group)
# reverse order for priority: lower characters have higher
# priority
if field in FIELD_MAP and FIELD_MAP[field] == 'priority':
order = 'asc' if order == 'desc' else 'desc'
result.append((function, order))
return result
self.groupfunctions = parse(p_groupstring, p_group=True) if p_groupstring else []
self.pregroupfunctions = parse(p_groupstring, p_group=False) if p_groupstring else []
self.sortfunctions = parse(p_sortstring, p_group=False)
def sort(self, p_todos): def sort(self, p_todos):
""" """
...@@ -197,76 +268,3 @@ class Sorter(object): ...@@ -197,76 +268,3 @@ class Sorter(object):
result[key] = self.sort(_group) result[key] = self.sort(_group)
return result return result
def _parse(self, p_string, p_group):
"""
Parses a sort/group string and returns a list of functions and the
desired order.
"""
def get_field_function(p_field, p_group=False):
"""
Turns a field, part of a sort/group string, into a lambda that
takes a todo item and returns the field value.
"""
compose = lambda i: i.sort if not p_group else (i.group, i.label)
def group_value(p_todo):
"""
Returns a value to assign the given todo to a group. Date tags
are grouped according to the relative date (1 day, 1 month,
...)
"""
result = 'No value'
if p_todo.has_tag(p_field):
if p_field == config().tag_due():
result = humanize_date(p_todo.due_date())
elif p_field == config().tag_start():
result = humanize_date(p_todo.start_date())
else:
result = p_todo.tag_value(p_field)
try:
result = humanize_date(date_string_to_date(result))
except ValueError:
pass
return result
if p_field in FIELD_MAP:
return compose(FIELDS[FIELD_MAP[p_field]])
else:
# treat it as a tag value
return compose(Field(
sort=lambda t: '0' + t.tag_value(p_field) if t.has_tag(p_field) else '1',
group=group_value,
label=p_field,
))
result = []
fields = p_string.lower().split(',')
for field in fields:
parsed_field = re.match(
r'(?P<order>(asc|desc)(ending)?:)?(?P<field>\S+)',
field)
if not parsed_field:
continue
order = parsed_field.group('order')
order = 'desc' if order and order.startswith('desc') else 'asc'
field = parsed_field.group('field')
if field:
function = get_field_function(field, p_group)
# reverse order for priority: lower characters have higher
# priority
if field in FIELD_MAP and FIELD_MAP[field] == 'priority':
order = 'asc' if order == 'desc' else 'desc'
result.append((function, order))
return result
...@@ -20,8 +20,10 @@ changes. ...@@ -20,8 +20,10 @@ changes.
""" """
import os.path import os.path
from watchdog.events import (FileCreatedEvent, FileModifiedEvent,
FileSystemEventHandler)
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler, FileModifiedEvent, FileCreatedEvent
from topydo.lib.TodoFile import TodoFile from topydo.lib.TodoFile import TodoFile
......
...@@ -116,7 +116,7 @@ class TodoList(TodoListBase): ...@@ -116,7 +116,7 @@ class TodoList(TodoListBase):
super().add_todos(p_todos) super().add_todos(p_todos)
for todo in self._todos: for todo in self._todos:
todo.parents = types.MethodType(lambda i: self.parents(i), todo) todo.parents = types.MethodType(self.parents, todo)
# only do administration when the dependency info is initialized, # only do administration when the dependency info is initialized,
# otherwise we postpone it until it's really needed (through the # otherwise we postpone it until it's really needed (through the
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
import collections import collections
class TopydoString(collections.UserString): class TopydoString(collections.UserString):
""" """
Represents a string that also contains color information. A combination of Represents a string that also contains color information. A combination of
...@@ -50,4 +51,3 @@ class TopydoString(collections.UserString): ...@@ -50,4 +51,3 @@ class TopydoString(collections.UserString):
def set_color(self, p_pos, p_color): def set_color(self, p_pos, p_color):
""" Start using a color at the given position. """ """ Start using a color at the given position. """
self.colors[p_pos] = p_color self.colors[p_pos] = p_color
...@@ -18,12 +18,12 @@ ...@@ -18,12 +18,12 @@
Various utility functions. Various utility functions.
""" """
import arrow
import re import re
from collections import namedtuple from collections import namedtuple
from datetime import date from datetime import date
import arrow
def date_string_to_date(p_date): def date_string_to_date(p_date):
""" """
...@@ -81,16 +81,14 @@ def get_terminal_size(p_getter=None): ...@@ -81,16 +81,14 @@ def get_terminal_size(p_getter=None):
except ImportError: except ImportError:
from backports.shutil_get_terminal_size import get_terminal_size as _get_terminal_size # pylint: disable=import-error from backports.shutil_get_terminal_size import get_terminal_size as _get_terminal_size # pylint: disable=import-error
sz = _get_terminal_size() size = _get_terminal_size()
except ValueError: except ValueError:
""" # This can result from the 'underlying buffer being detached', which
This can result from the 'underlying buffer being detached', which # occurs during running the unittest on Windows (but not on Linux?)
occurs during running the unittest on Windows (but not on Linux?)
"""
terminal_size = namedtuple('Terminal_Size', 'columns lines') terminal_size = namedtuple('Terminal_Size', 'columns lines')
sz = terminal_size(80, 24) size = terminal_size(80, 24)
return sz return size
get_terminal_size.getter = inner get_terminal_size.getter = inner
...@@ -114,6 +112,5 @@ def translate_key_to_config(p_key): ...@@ -114,6 +112,5 @@ def translate_key_to_config(p_key):
def humanize_date(p_datetime): def humanize_date(p_datetime):
""" Returns a relative date string from a datetime object. """ """ Returns a relative date string from a datetime object. """
now = arrow.now() now = arrow.now()
date = now.replace(day=p_datetime.day, month=p_datetime.month, year=p_datetime.year) _date = now.replace(day=p_datetime.day, month=p_datetime.month, year=p_datetime.year)
return date.humanize(now).replace('just now', 'today') return _date.humanize(now).replace('just now', 'today')
...@@ -56,4 +56,3 @@ class PrettyPrinterColorFilter(PrettyPrinterFilter): ...@@ -56,4 +56,3 @@ class PrettyPrinterColorFilter(PrettyPrinterFilter):
p_todo_str.append('', AbstractColor.NEUTRAL) p_todo_str.append('', AbstractColor.NEUTRAL)
return p_todo_str return p_todo_str
...@@ -19,8 +19,8 @@ Provides a pretty printer filter that generates a todo string based on a format ...@@ -19,8 +19,8 @@ Provides a pretty printer filter that generates a todo string based on a format
string. string.
""" """
from topydo.lib.PrettyPrinterFilter import PrettyPrinterFilter
from topydo.lib.ListFormat import ListFormatParser from topydo.lib.ListFormat import ListFormatParser
from topydo.lib.PrettyPrinterFilter import PrettyPrinterFilter
class PrettyPrinterFormatFilter(PrettyPrinterFilter): class PrettyPrinterFormatFilter(PrettyPrinterFilter):
......
...@@ -29,4 +29,3 @@ class PrettyPrinterNumbers(PrettyPrinterFilter): ...@@ -29,4 +29,3 @@ class PrettyPrinterNumbers(PrettyPrinterFilter):
def filter(self, p_todo_str, p_todo): def filter(self, p_todo_str, p_todo):
""" Prepends the number to the todo string. """ """ Prepends the number to the todo string. """
return "|{:>3}| {}".format(self.todolist.number(p_todo), p_todo_str) return "|{:>3}| {}".format(self.todolist.number(p_todo), p_todo_str)
...@@ -177,7 +177,8 @@ class CLIApplicationBase(object): ...@@ -177,7 +177,8 @@ class CLIApplicationBase(object):
self._post_archive_action = None self._post_archive_action = None
self.backup = None self.backup = None
def _usage(self): @staticmethod
def _usage():
usage() usage()
sys.exit(0) sys.exit(0)
...@@ -208,7 +209,7 @@ class CLIApplicationBase(object): ...@@ -208,7 +209,7 @@ class CLIApplicationBase(object):
elif opt in ("-v", "--version"): elif opt in ("-v", "--version"):
version() version()
else: else:
self._usage() CLIApplicationBase._usage()
if alt_config_path: if alt_config_path:
config(alt_config_path, overrides) config(alt_config_path, overrides)
...@@ -237,21 +238,17 @@ class CLIApplicationBase(object): ...@@ -237,21 +238,17 @@ class CLIApplicationBase(object):
if archive.dirty: if archive.dirty:
archive_file.write(archive.print_todos()) archive_file.write(archive.print_todos())
def _help(self, args): @staticmethod
if args is None: def is_read_only(p_command):
pass # TODO
else:
pass # TODO
def is_read_only(self, p_command):
""" Returns True when the given command class is read-only. """ """ Returns True when the given command class is read-only. """
read_only_commands = tuple(cmd for cmd read_only_commands = tuple(cmd for cmd
in ('revert', ) + READ_ONLY_COMMANDS) in ('revert', ) + READ_ONLY_COMMANDS)
return p_command.name() in read_only_commands return p_command.name() in read_only_commands
def _backup(self, p_command, p_args=[], p_label=None): def _backup(self, p_command, p_args=None, p_label=None):
if config().backup_count() > 0 and p_command and not self.is_read_only(p_command): if config().backup_count() > 0 and p_command and not CLIApplicationBase.is_read_only(p_command):
call = [p_command.name()]+ p_args p_args = p_args if p_args else []
call = [p_command.name()] + p_args
from topydo.lib.ChangeSet import ChangeSet from topydo.lib.ChangeSet import ChangeSet
label = p_label if p_label else call label = p_label if p_label else call
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime import datetime
from functools import lru_cache from functools import lru_cache
from topydo.Commands import SUBCOMMAND_MAP from topydo.Commands import SUBCOMMAND_MAP
......
...@@ -20,7 +20,7 @@ import getopt ...@@ -20,7 +20,7 @@ import getopt
import sys import sys
from topydo.ui.cli.CLI import CLIApplication from topydo.ui.cli.CLI import CLIApplication
from topydo.ui.CLIApplicationBase import MAIN_OPTS, MAIN_LONG_OPTS, error from topydo.ui.CLIApplicationBase import MAIN_LONG_OPTS, MAIN_OPTS, error
_WINDOWS = "win32" in sys.platform _WINDOWS = "win32" in sys.platform
......
...@@ -18,9 +18,10 @@ ...@@ -18,9 +18,10 @@
import sys import sys
from topydo.Commands import get_subcommand
from topydo.lib import TodoFile, TodoList
from topydo.lib.Config import ConfigError, config
from topydo.ui.CLIApplicationBase import CLIApplicationBase, error from topydo.ui.CLIApplicationBase import CLIApplicationBase, error
from topydo.lib import TodoFile
from topydo.lib.Config import config, ConfigError
# First thing is to poke the configuration and check whether it's sane # First thing is to poke the configuration and check whether it's sane
# The modules below may already read in configuration upon import, so # The modules below may already read in configuration upon import, so
...@@ -31,8 +32,6 @@ except ConfigError as config_error: ...@@ -31,8 +32,6 @@ except ConfigError as config_error:
error(str(config_error)) error(str(config_error))
sys.exit(1) sys.exit(1)
from topydo.Commands import get_subcommand
from topydo.lib import TodoList
class CLIApplication(CLIApplicationBase): class CLIApplication(CLIApplicationBase):
...@@ -57,7 +56,7 @@ class CLIApplication(CLIApplicationBase): ...@@ -57,7 +56,7 @@ class CLIApplication(CLIApplicationBase):
sys.exit(1) sys.exit(1)
if subcommand is None: if subcommand is None:
self._usage() CLIApplicationBase._usage()
if self._execute(subcommand, args) == False: if self._execute(subcommand, args) == False:
sys.exit(1) sys.exit(1)
......
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from configparser import RawConfigParser, NoOptionError from configparser import NoOptionError, RawConfigParser
from os.path import expanduser from os.path import expanduser
from topydo.lib.Config import home_config_path, config from topydo.lib.Config import config, home_config_path
def columns(p_alt_layout_path=None): def columns(p_alt_layout_path=None):
......
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import urwid
from os.path import commonprefix from os.path import commonprefix
import urwid
from topydo.ui.columns.CompletionBoxWidget import CompletionBoxWidget from topydo.ui.columns.CompletionBoxWidget import CompletionBoxWidget
......
...@@ -19,30 +19,32 @@ import getopt ...@@ -19,30 +19,32 @@ import getopt
import shlex import shlex
import sys import sys
import time import time
import urwid
from collections import namedtuple from collections import namedtuple
from string import ascii_uppercase from string import ascii_uppercase
import urwid
from topydo.Commands import get_subcommand from topydo.Commands import get_subcommand
from topydo.lib.Config import config, ConfigError from topydo.lib import TodoList
from topydo.ui.columns.ColumnCompleter import ColumnCompleter from topydo.lib.Config import ConfigError, config
from topydo.lib.Filter import (DependencyFilter, HiddenTagFilter,
RelevanceFilter, get_filter_list)
from topydo.lib.Sorter import Sorter from topydo.lib.Sorter import Sorter
from topydo.lib.Filter import get_filter_list, RelevanceFilter, DependencyFilter, HiddenTagFilter from topydo.lib.TodoFileWatched import TodoFileWatched
from topydo.lib.Utils import get_terminal_size from topydo.lib.Utils import get_terminal_size
from topydo.lib.View import View from topydo.lib.View import View
from topydo.lib.TodoFileWatched import TodoFileWatched from topydo.ui.CLIApplicationBase import (GENERIC_HELP, CLIApplicationBase,
from topydo.lib import TodoList error)
from topydo.ui.CLIApplicationBase import CLIApplicationBase, error, GENERIC_HELP from topydo.ui.columns.ColumnCompleter import ColumnCompleter
from topydo.ui.columns.ColumnLayout import columns
from topydo.ui.columns.CommandLineWidget import CommandLineWidget from topydo.ui.columns.CommandLineWidget import CommandLineWidget
from topydo.ui.columns.ConsoleWidget import ConsoleWidget from topydo.ui.columns.ConsoleWidget import ConsoleWidget
from topydo.ui.columns.KeystateWidget import KeystateWidget from topydo.ui.columns.KeystateWidget import KeystateWidget
from topydo.ui.columns.TodoWidget import TodoWidget
from topydo.ui.columns.TodoListWidget import TodoListWidget from topydo.ui.columns.TodoListWidget import TodoListWidget
from topydo.ui.columns.TodoWidget import TodoWidget
from topydo.ui.columns.Transaction import Transaction from topydo.ui.columns.Transaction import Transaction
from topydo.ui.columns.Utils import PaletteItem, to_urwid_color from topydo.ui.columns.Utils import PaletteItem, to_urwid_color
from topydo.ui.columns.ViewWidget import ViewWidget from topydo.ui.columns.ViewWidget import ViewWidget
from topydo.ui.columns.ColumnLayout import columns
class UIView(View): class UIView(View):
...@@ -196,10 +198,63 @@ class UIApplication(CLIApplicationBase): ...@@ -196,10 +198,63 @@ class UIApplication(CLIApplicationBase):
self._screen = urwid.raw_display.Screen() self._screen = urwid.raw_display.Screen()
def create_color_palette():
project_color = to_urwid_color(config().project_color())
context_color = to_urwid_color(config().context_color())
metadata_color = to_urwid_color(config().metadata_color())
link_color = to_urwid_color(config().link_color())
focus_background_color = to_urwid_color(config().focus_background_color())
marked_background_color = to_urwid_color(config().marked_background_color())
palette = [
(PaletteItem.PROJECT, '', '', '', project_color, ''),
(PaletteItem.PROJECT_FOCUS, '', 'light gray', '', project_color, focus_background_color),
(PaletteItem.CONTEXT, '', '', '', context_color, ''),
(PaletteItem.CONTEXT_FOCUS, '', 'light gray', '', context_color, focus_background_color),
(PaletteItem.METADATA, '', '', '', metadata_color, ''),
(PaletteItem.METADATA_FOCUS, '', 'light gray', '', metadata_color, focus_background_color),
(PaletteItem.LINK, '', '', '', link_color, ''),
(PaletteItem.LINK_FOCUS, '', 'light gray', '', link_color, focus_background_color),
(PaletteItem.DEFAULT_FOCUS, '', 'light gray', '', '', focus_background_color),
(PaletteItem.MARKED, '', 'light blue', '', '', marked_background_color),
]
for C in ascii_uppercase:
pri_color_cfg = config().priority_color(C)
pri_color = to_urwid_color(pri_color_cfg)
pri_color_focus = pri_color if not pri_color_cfg.is_neutral() else 'black'
palette.append((
'pri_' + C, '', '', '', pri_color, ''
))
palette.append((
'pri_' + C + '_focus', '', 'light gray', '', pri_color_focus, focus_background_color
))
return palette
def create_mono_palette():
palette = [
(PaletteItem.DEFAULT_FOCUS, 'black', 'light gray'),
(PaletteItem.PROJECT_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.CONTEXT_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.METADATA_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.LINK_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.MARKED, 'default,underline,bold', 'default'),
]
for C in ascii_uppercase:
palette.append(
('pri_' + C + '_focus', PaletteItem.DEFAULT_FOCUS)
)
return palette
if config().colors(): if config().colors():
self._screen.register_palette(self._create_color_palette()) self._screen.register_palette(create_color_palette())
else: else:
self._screen.register_palette(self._create_mono_palette()) self._screen.register_palette(create_mono_palette())
self._screen.set_terminal_properties(256) self._screen.set_terminal_properties(256)
...@@ -213,59 +268,6 @@ class UIApplication(CLIApplicationBase): ...@@ -213,59 +268,6 @@ class UIApplication(CLIApplicationBase):
self.column_mode = _APPEND_COLUMN self.column_mode = _APPEND_COLUMN
self._set_alarm_for_next_midnight_update() self._set_alarm_for_next_midnight_update()
def _create_color_palette(self):
project_color = to_urwid_color(config().project_color())
context_color = to_urwid_color(config().context_color())
metadata_color = to_urwid_color(config().metadata_color())
link_color = to_urwid_color(config().link_color())
focus_background_color = to_urwid_color(config().focus_background_color())
marked_background_color = to_urwid_color(config().marked_background_color())
palette = [
(PaletteItem.PROJECT, '', '', '', project_color, ''),
(PaletteItem.PROJECT_FOCUS, '', 'light gray', '', project_color, focus_background_color),
(PaletteItem.CONTEXT, '', '', '', context_color, ''),
(PaletteItem.CONTEXT_FOCUS, '', 'light gray', '', context_color, focus_background_color),
(PaletteItem.METADATA, '', '', '', metadata_color, ''),
(PaletteItem.METADATA_FOCUS, '', 'light gray', '', metadata_color, focus_background_color),
(PaletteItem.LINK, '', '', '', link_color, ''),
(PaletteItem.LINK_FOCUS, '', 'light gray', '', link_color, focus_background_color),
(PaletteItem.DEFAULT_FOCUS, '', 'light gray', '', '', focus_background_color),
(PaletteItem.MARKED, '', 'light blue', '', '', marked_background_color),
]
for C in ascii_uppercase:
pri_color_cfg = config().priority_color(C)
pri_color = to_urwid_color(pri_color_cfg)
pri_color_focus = pri_color if not pri_color_cfg.is_neutral() else 'black'
palette.append((
'pri_' + C, '', '', '', pri_color, ''
))
palette.append((
'pri_' + C + '_focus', '', 'light gray', '', pri_color_focus, focus_background_color
))
return palette
def _create_mono_palette(self):
palette = [
(PaletteItem.DEFAULT_FOCUS, 'black', 'light gray'),
(PaletteItem.PROJECT_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.CONTEXT_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.METADATA_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.LINK_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.MARKED, 'default,underline,bold', 'default'),
]
for C in ascii_uppercase:
palette.append(
('pri_' + C + '_focus', PaletteItem.DEFAULT_FOCUS)
)
return palette
def _set_alarm_for_next_midnight_update(self): def _set_alarm_for_next_midnight_update(self):
def callback(p_loop, p_data): def callback(p_loop, p_data):
TodoWidget.wipe_cache() TodoWidget.wipe_cache()
......
...@@ -207,6 +207,7 @@ class TodoListWidget(urwid.LineBox): ...@@ -207,6 +207,7 @@ class TodoListWidget(urwid.LineBox):
p_row, p_row,
p_focus) p_focus)
# pylint: disable=no-self-use
def selectable(self): def selectable(self):
return True return True
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import re import re
import urwid import urwid
from topydo.lib.Config import config from topydo.lib.Config import config
...@@ -91,7 +92,6 @@ class TodoWidget(urwid.WidgetWrap): ...@@ -91,7 +92,6 @@ class TodoWidget(urwid.WidgetWrap):
priority_widget = urwid.Text(priority_text) priority_widget = urwid.Text(priority_text)
self.text_widget = urwid.Text(txt_markup) self.text_widget = urwid.Text(txt_markup)
progress = to_urwid_color(progress_color(p_todo)) if config().colors() else PaletteItem.DEFAULT
self.progress_bar = urwid.AttrMap( self.progress_bar = urwid.AttrMap(
urwid.SolidFill(' '), urwid.SolidFill(' '),
{}, {},
...@@ -117,13 +117,15 @@ class TodoWidget(urwid.WidgetWrap): ...@@ -117,13 +117,15 @@ class TodoWidget(urwid.WidgetWrap):
super().__init__(self.widget) super().__init__(self.widget)
def keypress(self, p_size, p_key): # pylint: disable=no-self-use
def keypress(self, _, p_key):
""" """
Override keypress to prevent the wrapped Columns widget to Override keypress to prevent the wrapped Columns widget to
receive any key. receive any key.
""" """
return p_key return p_key
# pylint: disable=no-self-use
def selectable(self): def selectable(self):
# make sure that ListBox will highlight this widget # make sure that ListBox will highlight this widget
return True return True
...@@ -196,4 +198,3 @@ class TodoWidget(urwid.WidgetWrap): ...@@ -196,4 +198,3 @@ class TodoWidget(urwid.WidgetWrap):
def wipe_cache(p_class): def wipe_cache(p_class):
""" Wipes the cache """ """ Wipes the cache """
p_class.cache = {} p_class.cache = {}
...@@ -18,6 +18,7 @@ import urwid ...@@ -18,6 +18,7 @@ import urwid
from topydo.lib.Config import config from topydo.lib.Config import config
class ViewWidget(urwid.LineBox): class ViewWidget(urwid.LineBox):
def __init__(self, p_todolist): def __init__(self, p_todolist):
self._todolist = p_todolist self._todolist = p_todolist
......
...@@ -16,16 +16,18 @@ ...@@ -16,16 +16,18 @@
""" Entry file for the topydo Prompt interface (CLI). """ """ Entry file for the topydo Prompt interface (CLI). """
import os.path
import shlex import shlex
import sys import sys
from topydo.ui.CLIApplicationBase import CLIApplicationBase, error, GENERIC_HELP
from topydo.ui.prompt.PromptCompleter import PromptCompleter
from prompt_toolkit.shortcuts import prompt
from prompt_toolkit.history import InMemoryHistory from prompt_toolkit.history import InMemoryHistory
from prompt_toolkit.shortcuts import prompt
from topydo.lib.Config import config, ConfigError from topydo.Commands import get_subcommand
from topydo.lib.Config import ConfigError, config
from topydo.lib.TodoFileWatched import TodoFileWatched
from topydo.ui.CLIApplicationBase import (GENERIC_HELP, CLIApplicationBase,
error)
from topydo.ui.prompt.PromptCompleter import PromptCompleter
# First thing is to poke the configuration and check whether it's sane # First thing is to poke the configuration and check whether it's sane
# The modules below may already read in configuration upon import, so # The modules below may already read in configuration upon import, so
...@@ -36,9 +38,6 @@ except ConfigError as config_error: ...@@ -36,9 +38,6 @@ except ConfigError as config_error:
error(str(config_error)) error(str(config_error))
sys.exit(1) sys.exit(1)
from topydo.Commands import get_subcommand
from topydo.lib.TodoFileWatched import TodoFileWatched
from topydo.lib import TodoList
class PromptApplication(CLIApplicationBase): class PromptApplication(CLIApplicationBase):
......
...@@ -22,9 +22,10 @@ by the prompt toolkit. ...@@ -22,9 +22,10 @@ by the prompt toolkit.
import re import re
from prompt_toolkit.completion import Completer, Completion from prompt_toolkit.completion import Completer, Completion
from topydo.ui.CompleterBase import CompleterBase, date_suggestions
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.RelativeDate import relative_date_to_date from topydo.lib.RelativeDate import relative_date_to_date
from topydo.ui.CompleterBase import CompleterBase, date_suggestions
def _dates(p_word_before_cursor): def _dates(p_word_before_cursor):
...@@ -52,15 +53,14 @@ class PromptCompleter(CompleterBase, Completer): ...@@ -52,15 +53,14 @@ class PromptCompleter(CompleterBase, Completer):
for candidate in candidates: for candidate in candidates:
yield Completion(candidate, -len(p_word)) yield Completion(candidate, -len(p_word))
def get_completions(self, p_document, _): def get_completions(self, p_word, p_is_first_word=False):
# include all characters except whitespaces (for + and @) # include all characters except whitespaces (for + and @)
word_before_cursor = p_document.get_word_before_cursor(True) word_before_cursor = p_word.get_word_before_cursor(True)
is_first_word = not re.match(r'\s*\S+\s', is_first_word = not re.match(r'\s*\S+\s',
p_document.current_line_before_cursor) p_word.current_line_before_cursor)
if word_before_cursor.startswith(config().tag_due() + ':'): if word_before_cursor.startswith(config().tag_due() + ':'):
return _dates(word_before_cursor) return _dates(word_before_cursor)
elif word_before_cursor.startswith(config().tag_start() + ':'): elif word_before_cursor.startswith(config().tag_start() + ':'):
return _dates(word_before_cursor) return _dates(word_before_cursor)
else:
return self._completion_generator(word_before_cursor, is_first_word) return self._completion_generator(word_before_cursor, is_first_word)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment