Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Add support for categories. To fix #10 #323

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Authors are listed in alphabetical order.
* Anubha Agrawal <anubhakannu@gmail.com>
* Benjamin Frank <ben+gitlab@pipsfrank.de>
* Christian Geier <geier@lostpackets.de>
* Christof Schulze <christof@christofschulze.com>
* Doron Behar <me@doronbehar.com>
* Guilhem Saurel <guilhem.saurel@gmail.com>
* Hugo Osvaldo Barrera <hugo@barrera.io>
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ New features
* Increment sequence number upon edits.
* Print a descriptive message when no lists are found.
* Add full support for locations.
* Add full support for categories.

Packaging changes
~~~~~~~~~~~~~~~~~
Expand Down
23 changes: 23 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,29 @@ def test_empty_list(tmpdir, runner, create):

assert expected in result.output

def test_show_categories(tmpdir, runner, create):
create('test.ics', 'SUMMARY:harhar\n', 'CATEGORIES:Online\n')
christf marked this conversation as resolved.
Show resolved Hide resolved

result = runner.invoke(cli, ['show', '1'])
assert not result.exception
assert 'Online' in result.output


def test_categories(runner):
result = runner.invoke(cli, [
'new', '-l', 'default', '--categories', 'Offline', 'Event Name'
])

assert not result.exception
assert 'Offline' in result.output

def test_categories(runner):
result = runner.invoke(cli, [
'new', '-l', 'default', '--categories', 'Offline', '--categories', 'Phone', 'Event Name'
])

assert not result.exception
assert 'Offline,Phone' in result.output

def test_show_location(tmpdir, runner, create):
create('test.ics', 'SUMMARY:harhar\n' 'LOCATION:Boston\n')
Expand Down
30 changes: 27 additions & 3 deletions todoman/cli.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import functools
import glob
import locale
Expand All @@ -14,6 +15,7 @@
from todoman.interactive import TodoEditor
from todoman.model import cached_property, Database, Todo

logger = logging.getLogger(name=__name__)

click_log.basic_config()

Expand Down Expand Up @@ -119,6 +121,13 @@ def _validate_todos(ctx, param, val):
with handle_error():
return [ctx.db.todo(int(id)) for id in val]

def _validate_categories(ctx, param, val):
ctx = ctx.find_object(AppContext)
try:
return ctx.formatter.parse_category(val)
except ValueError as e:
raise click.BadParameter(e)


def _sort_callback(ctx, param, val):
fields = val.split(',') if val else []
Expand Down Expand Up @@ -165,6 +174,13 @@ def _todo_property_options(command):
'--location', help=('The location where '
'this todo takes place.')
)(command)
click.option(
'--categories',
default='',
multiple=True,
callback=_validate_categories,
help=('A category. May be used multiple times.')
)(command)
click.option(
'--due',
'-d',
Expand All @@ -187,7 +203,7 @@ def _todo_property_options(command):
def command_wrap(*a, **kw):
kw['todo_properties'] = {
key: kw.pop(key)
for key in ('due', 'start', 'location', 'priority')
for key in ('due', 'start', 'location', 'priority', 'categories')
}
return command(*a, **kw)

Expand Down Expand Up @@ -355,6 +371,7 @@ def new(ctx, summary, list, todo_properties, read_description, interactive):

for key, value in todo_properties.items():
if value:
logger.debug("property: %s value: %s type: %s", key, ','.join(value), type(value) )
setattr(todo, key, value)
todo.summary = ' '.join(summary)

Expand Down Expand Up @@ -551,7 +568,7 @@ def move(ctx, list, ids):
@pass_ctx
@click.argument('lists', nargs=-1, callback=_validate_lists_param)
@click.option('--location', help='Only show tasks with location containg TEXT')
@click.option('--category', help='Only show tasks with category containg TEXT')
@click.option('--categories', help='Only show tasks with categories containg TEXT')
@click.option('--grep', help='Only show tasks with message containg TEXT')
@click.option(
'--sort',
Expand All @@ -576,6 +593,13 @@ def move(ctx, list, ids):
'hours',
type=int
)
@click.option(
'--categories',
default=None,
help='Only show tasks with categories.',
type=str,
callback=_validate_categories
)
@click.option(
'--priority',
default=None,
Expand Down Expand Up @@ -615,7 +639,7 @@ def list(ctx, **kwargs):
List tasks. Filters any completed or cancelled tasks by default.

If no arguments are provided, all lists will be displayed, and only
incomplete tasks are show. Otherwise, only todos for the specified list
incomplete tasks are shown. Otherwise, only todos for the specified list
christf marked this conversation as resolved.
Show resolved Hide resolved
will be displayed.

eg:
Expand Down
18 changes: 18 additions & 0 deletions todoman/formatters.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import datetime
import json
from time import mktime
Expand All @@ -9,6 +10,7 @@
from dateutil.tz import tzlocal
from tabulate import tabulate

logger = logging.getLogger(name=__name__)

def rgb_to_ansi(colour):
"""
Expand Down Expand Up @@ -104,6 +106,8 @@ def detailed(self, todo):
extra_rows = []
if todo.description:
extra_rows += self._columnize('Description', todo.description)
if todo.categories:
extra_rows += self._columnize('Categories', ','.join(todo.categories))
if todo.location:
extra_rows += self._columnize('Location', todo.location)

Expand All @@ -121,6 +125,19 @@ def format_datetime(self, dt):
elif isinstance(dt, datetime.date):
return dt.strftime(self.date_format)

def format_categories(self, categories):
if not categories:
return ""
else:
return ','.join(categories)

def parse_category(self, categories):
if categories is None or categories == '':
return None
else:
logger.debug("categories: value: %s type: %s", str(list(categories)), str(type(categories)) )
return (c for c in str(categories).split(','))

def parse_priority(self, priority):
if priority is None or priority is '':
return None
Expand Down Expand Up @@ -217,6 +234,7 @@ def _todo_as_dict(self, todo):
percent=todo.percent_complete,
summary=todo.summary,
priority=todo.priority,
categories=todo.categories,
location=todo.location,
)

Expand Down
11 changes: 10 additions & 1 deletion todoman/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def __init__(self, todo, lists, formatter):
("Summary", self._summary),
("Description", self._description),
("Location", self._location),
("Categories", self._categories),
("Start", self._dtstart),
("Due", self._due),
("Completed", self._completed),
Expand Down Expand Up @@ -75,6 +76,10 @@ def _init_basic_fields(self):
parent=self,
edit_text=self.todo.location,
)
self._categories = widgets.ExtendedEdit(
parent=self,
edit_text=self.formatter.format_categories(self.todo.categories),
)
self._due = widgets.ExtendedEdit(
parent=self,
edit_text=self.formatter.format_datetime(self.todo.due),
Expand Down Expand Up @@ -159,6 +164,7 @@ def _save_inner(self):
self.todo.summary = self.summary
self.todo.description = self.description
self.todo.location = self.location
self.todo.categories = [c.strip() for c in self.categories.split(',')]
self.todo.due = self.formatter.parse_datetime(self.due)
self.todo.start = self.formatter.parse_datetime(self.dtstart)
if not self.todo.is_completed and self._completed.get_state():
Expand All @@ -168,7 +174,6 @@ def _save_inner(self):
self.todo.completed_at = None
self.todo.priority = self.priority

# TODO: categories
# TODO: comment

# https://tools.ietf.org/html/rfc5545#section-3.8
Expand Down Expand Up @@ -197,6 +202,10 @@ def location(self):
def due(self):
return self._due.edit_text

@property
def categories(self):
return self._categories.edit_text

@property
def dtstart(self):
return self._dtstart.edit_text
Expand Down