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

Introduce options to configure the ordering of the projects and tags commands #362

Open
wants to merge 1 commit into
base: master
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions tests/test_watson.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,35 +681,49 @@ def json(self):
# projects

def test_projects(watson):
i = 0
for name in ('foo', 'bar', 'bar', 'bar', 'foo', 'lol'):
watson.frames.add(name, 4000, 4000)
watson.frames.add(name, 4000 + i, 4000 + i)
i += 1

assert watson.projects == ['bar', 'foo', 'lol']
watson.frames.add("oldest", 2000, 2005)
watson.frames.add("recent", 5000, 5005)

assert watson.projects() == ['bar', 'foo', 'lol', 'oldest', 'recent']
assert (watson.projects(orderby='start')
== ['oldest', 'bar', 'foo', 'lol', 'recent'])
assert (watson.projects(orderby='start', descending=True)
== ['recent', 'lol', 'foo', 'bar', 'oldest'])


def test_projects_no_frames(watson):
assert watson.projects == []
assert watson.projects() == []


# tags

def test_tags(watson):
samples = (
samples = [
('foo', ('A', 'D')),
('bar', ('A', 'C')),
('foo', ('B', 'C')),
('lol', ()),
('bar', ('C'))
)
]

i = 0
for name, tags in samples:
watson.frames.add(name, 4000, 4000, tags)
watson.frames.add(name, 4000 + i, 4000 + i, tags)
i += 1

assert watson.tags == ['A', 'B', 'C', 'D']
assert watson.tags() == ['A', 'B', 'C', 'D']
assert watson.tags(orderby='start') == ['D', 'A', 'B', 'C']
assert (watson.tags(orderby='start', descending=True)
== ['C', 'B', 'A', 'D'])


def test_tags_no_frames(watson):
assert watson.tags == []
assert watson.tags() == []


# merge
Expand Down
4 changes: 2 additions & 2 deletions watson/autocompletion.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def prepend_plus(tag_suggestions):
def get_projects(ctx, args, incomplete):
"""Function to return all projects matching the prefix."""
watson = _bypass_click_bug_to_ensure_watson(ctx)
for cur_project in watson.projects:
for cur_project in watson.projects():
if cur_project.startswith(incomplete):
yield cur_project

Expand Down Expand Up @@ -98,7 +98,7 @@ def get_rename_types(ctx, args, incomplete):
def get_tags(ctx, args, incomplete):
"""Function to return all tags matching the prefix."""
watson = _bypass_click_bug_to_ensure_watson(ctx)
for cur_tag in watson.tags:
for cur_tag in watson.tags():
if cur_tag.startswith(incomplete):
yield cur_tag

Expand Down
50 changes: 41 additions & 9 deletions watson/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,15 +215,15 @@ def start(ctx, watson, confirm_new_project, confirm_new_tag, args, gap_=True):
# Confirm creation of new project if that option is set
if (watson.config.getboolean('options', 'confirm_new_project') or
confirm_new_project):
confirm_project(project, watson.projects)
confirm_project(project, watson.projects())

# Parse all the tags
tags = parse_tags(args)

# Confirm creation of new tag(s) if that option is set
if (watson.config.getboolean('options', 'confirm_new_tag') or
confirm_new_tag):
confirm_tags(tags, watson.tags)
confirm_tags(tags, watson.tags())

if project and watson.is_started and not gap_:
current = watson.current
Expand Down Expand Up @@ -1060,9 +1060,13 @@ def _final_print(lines):


@cli.command()
@click.option('-b', '--by', 'orderby', default='name',
help="Sort the ouput by either 'name' or 'time'")
@click.option('-r', '--reverse', 'descending', default=False, is_flag=True,
help="Reverse the output (sort by descending order)")
@click.pass_obj
@catch_watson_error
def projects(watson):
def projects(watson, orderby, descending):
"""
Display the list of all the existing projects.

Expand All @@ -1075,14 +1079,30 @@ def projects(watson):
voyager1
voyager2
"""
for project in watson.projects:
if orderby == 'name':
orderby = 'project'
elif orderby == 'time':
orderby = 'start'
else:
raise click.ClickException(style(
'error',
u'--by option can be either "name" or "time". '
u'You supplied "%s"' % orderby
))

projects = watson.projects(orderby=orderby, descending=descending)
for project in projects:
click.echo(style('project', project))


@cli.command()
@click.option('-b', '--by', 'orderby', default='name',
help="Sort the ouput by either 'name' or 'time'")
@click.option('-r', '--reverse', 'descending', default=False, is_flag=True,
help="Reverse the output (sort by descending order)")
@click.pass_obj
@catch_watson_error
def tags(watson):
def tags(watson, orderby, descending):
"""
Display the list of all the tags.

Expand All @@ -1103,7 +1123,19 @@ def tags(watson):
transmission
wheels
"""
for tag in watson.tags:
if orderby == 'name':
orderby = 'project'
elif orderby == 'time':
orderby = 'start'
else:
raise click.ClickException(style(
'error',
u'--by option can be either "name" or "time". '
u'You supplied "%s"' % orderby
))

tags = watson.tags(orderby=orderby, descending=descending)
for tag in tags:
click.echo(style('tag', tag))


Expand Down Expand Up @@ -1160,15 +1192,15 @@ def add(watson, args, from_, to, confirm_new_project, confirm_new_tag):
# Confirm creation of new project if that option is set
if (watson.config.getboolean('options', 'confirm_new_project') or
confirm_new_project):
confirm_project(project, watson.projects)
confirm_project(project, watson.projects())

# Parse all the tags
tags = parse_tags(args)

# Confirm creation of new tag(s) if that option is set
if (watson.config.getboolean('options', 'confirm_new_tag') or
confirm_new_tag):
confirm_tags(tags, watson.tags)
confirm_tags(tags, watson.tags())

# add a new frame, call watson save to update state files
frame = watson.add(project=project, tags=tags, from_date=from_, to_date=to)
Expand Down Expand Up @@ -1260,7 +1292,7 @@ def edit(watson, confirm_new_project, confirm_new_tag, id):
# Confirm creation of new tag(s) if that option is set
if (watson.config.getboolean('options', 'confirm_new_tag') or
confirm_new_tag):
confirm_tags(tags, watson.tags)
confirm_tags(tags, watson.tags())
start = arrow.get(data['start'], datetime_format).replace(
tzinfo=local_tz).to('utc')
stop = arrow.get(data['stop'], datetime_format).replace(
Expand Down
53 changes: 43 additions & 10 deletions watson/watson.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-

from collections import OrderedDict
import datetime
from functools import reduce
import json
Expand Down Expand Up @@ -303,19 +304,51 @@ def cancel(self):
self.current = None
return old_current

@property
def projects(self):
def projects(self, orderby="project", descending=False):
"""
Return the list of all the existing projects, sorted by name.
Return the list of all the existing projects.
"""
return sorted(set(self.frames['project']))

@property
def tags(self):
ordered_frames = sorted(self.frames, key=operator.attrgetter(orderby))
ordered_projects = list(map(operator.attrgetter("project"),
ordered_frames))

# Keep latest occurence only, so we feed a reversed list
# to the ordered set.
ordered_projects.reverse()

# Use OrderedDict as a set to remove duplicates by keep order.
result = list(OrderedDict.fromkeys(ordered_projects))

if not descending:
result.reverse()

return result

def tags(self, orderby="tag", descending=False):
"""
Return the list of the tags, sorted by name.
Return the list of the tags.
"""
return sorted(set(tag for tags in self.frames['tags'] for tag in tags))
if orderby == "tag":
tags = [tag for tags in self.frames['tags'] for tag in tags]
return sorted(set(tags))
else:
ordered_frames = sorted(self.frames,
key=operator.attrgetter(orderby))
ordered_tags = map(operator.attrgetter("tags"), ordered_frames)
flat_tags = [tag for tags in ordered_tags for tag in tags]

# Keep latest occurence only, so we feed a reversed list
# to the ordered set.
flat_tags.reverse()

result = list(OrderedDict.fromkeys(flat_tags))

if not descending:
result.reverse()

# Use OrderedDict as a set to remove duplicates keep order.
return result

def _get_request_info(self, route):
config = self.config
Expand Down Expand Up @@ -542,7 +575,7 @@ def report(self, from_, to, current=None, projects=None, tags=None,

def rename_project(self, old_project, new_project):
"""Rename a project in all affected frames."""
if old_project not in self.projects:
if old_project not in self.projects():
raise WatsonError(u'Project "%s" does not exist' % old_project)

updated_at = arrow.utcnow()
Expand All @@ -559,7 +592,7 @@ def rename_project(self, old_project, new_project):

def rename_tag(self, old_tag, new_tag):
"""Rename a tag in all affected frames."""
if old_tag not in self.tags:
if old_tag not in self.tags():
raise WatsonError(u'Tag "%s" does not exist' % old_tag)

updated_at = arrow.utcnow()
Expand Down