Skip to content

Commit

Permalink
Warn user about new project or tag creation
Browse files Browse the repository at this point in the history
Add `--confirm-new-project` and `--confirm-new-tag` options to `start`, `add` and `edit` commands (jazzband#275)
  • Loading branch information
medwig authored and jmaupetit committed May 2, 2019
1 parent 2d861a4 commit 9f9f005
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add support for first day of the week configuration in reports and logs (#240)
- Python 3.7 support (#241)
- Add `start --no-gap` and `stop --at` options (#254)
- Add `--confirm-new-project` and `--confirm-new-tag` options to `start`, `add` and `edit` commands (#275)

### Changed

Expand Down
8 changes: 7 additions & 1 deletion docs/user-guide/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Flag | Help
-----|-----
`-f, --from DATE` | Date and time of start of tracked activity [required]
`-t, --to DATE` | Date and time of end of tracked activity [required]
`-c, --confirm-new-project` | Confirm addition of new project.
`-b, --confirm-new-tag` | Confirm creation of new tag.
`--help` | Show this message and exit.

## `aggregate`
Expand Down Expand Up @@ -163,6 +165,8 @@ to `vim`, `nano` or `vi` (first one found) on all other systems.

Flag | Help
-----|-----
`-c, --confirm-new-project` | Confirm addition of new project.
`-b, --confirm-new-tag` | Confirm creation of new tag.
`--help` | Show this message and exit.

## `frames`
Expand Down Expand Up @@ -591,6 +595,8 @@ Example:
Flag | Help
-----|-----
`-g, --gap / -G, --no-gap` | (Don't) leave gap between end time of previous project and start time of the current.
`-c, --confirm-new-project` | Confirm addition of new project.
`-b, --confirm-new-tag` | Confirm creation of new tag.
`--help` | Show this message and exit.

## `status`
Expand Down Expand Up @@ -642,7 +648,7 @@ Example:


$ watson stop --at 13:37
Stopping project apollo11, started an hour ago and stopped 30 minutes ago. (id: e9ccd52)
Stopping project apollo11, started an hour ago and stopped 30 minutes ago. (id: e9ccd52) # noqa: E501

### Options

Expand Down
60 changes: 57 additions & 3 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,24 @@
from StringIO import StringIO
except ImportError:
from io import StringIO

try:
from unittest.mock import patch
except ImportError:
from mock import patch
import pytest
from click.exceptions import Abort
from dateutil.tz import tzutc

from watson.utils import (apply_weekday_offset, get_start_time_for_period,
make_json_writer, safe_save, parse_tags, PY2)
from watson.utils import (
apply_weekday_offset,
confirm_project,
confirm_tags,
get_start_time_for_period,
make_json_writer,
safe_save,
parse_tags,
PY2,
)
from . import mock_datetime


Expand Down Expand Up @@ -174,3 +186,45 @@ def failing_writer(f):
def test_parse_tags(args, parsed_tags):
tags = parse_tags(args)
assert tags == parsed_tags


def test_confirm_project_existing_project_returns_true():
project = 'foo'
watson_projects = ['foo', 'bar']
assert confirm_project(project, watson_projects)


@patch('click.confirm', return_value=True)
def test_confirm_project_accept_returns_true(confirm):
project = 'baz'
watson_projects = ['foo', 'bar']
assert confirm_project(project, watson_projects)


@patch('watson.utils.click.confirm', side_effect=Abort)
def test_confirm_project_reject_raises_abort(confirm):
project = 'baz'
watson_projects = ['foo', 'bar']
with pytest.raises(Abort):
confirm_project(project, watson_projects)


def test_confirm_tags_existing_tag_returns_true():
tags = ['a']
watson_tags = ['a', 'b']
assert confirm_tags(tags, watson_tags)


@patch('click.confirm', return_value=True)
def test_confirm_tags_accept_returns_true(confirm):
tags = ['c']
watson_tags = ['a', 'b']
assert confirm_tags(tags, watson_tags)


@patch('click.confirm', side_effect=Abort)
def test_confirm_tags_reject_raises_abort(confirm):
tags = ['c']
watson_tags = ['a', 'b']
with pytest.raises(Abort):
confirm_project(tags, watson_tags)
61 changes: 55 additions & 6 deletions watson/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,18 @@

from . import watson as _watson
from .frames import Frame
from .utils import (apply_weekday_offset, format_timedelta,
get_frame_from_argument, get_start_time_for_period,
options, safe_save, sorted_groupby, style, parse_tags)
from .utils import (
apply_weekday_offset,
confirm_project,
confirm_tags,
format_timedelta,
get_frame_from_argument,
get_start_time_for_period,
options, safe_save,
sorted_groupby,
style,
parse_tags,
)


class MutuallyExclusiveOption(click.Option):
Expand Down Expand Up @@ -155,9 +164,13 @@ def _start(watson, project, tags, restart=False, gap=True):
help=("(Don't) leave gap between end time of previous project "
"and start time of the current."))
@click.argument('args', nargs=-1)
@click.option('-c', '--confirm-new-project', is_flag=True, default=False,
help="Confirm addition of new project.")
@click.option('-b', '--confirm-new-tag', is_flag=True, default=False,
help="Confirm creation of new tag.")
@click.pass_obj
@click.pass_context
def start(ctx, watson, args, gap_=True):
def start(ctx, watson, confirm_new_project, confirm_new_tag, args, gap_=True):
"""
Start monitoring time for the given project.
You can add tags indicating more specifically what you are working on with
Expand All @@ -180,9 +193,19 @@ def start(ctx, watson, args, gap_=True):
itertools.takewhile(lambda s: not s.startswith('+'), args)
)

# 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)

# 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)

if project and watson.is_started and not gap_:
current = watson.current
errmsg = ("Project {} is already started and '--no-gap' is passed. "
Expand Down Expand Up @@ -997,8 +1020,12 @@ def frames(watson):
help="Date and time of start of tracked activity")
@click.option('-t', '--to', required=True, type=Date,
help="Date and time of end of tracked activity")
@click.option('-c', '--confirm-new-project', is_flag=True, default=False,
help="Confirm addition of new project.")
@click.option('-b', '--confirm-new-tag', is_flag=True, default=False,
help="Confirm creation of new tag.")
@click.pass_obj
def add(watson, args, from_, to):
def add(watson, args, from_, to, confirm_new_project, confirm_new_tag):
"""
Add time for project with tag(s) that was not tracked live.
Expand All @@ -1013,9 +1040,19 @@ def add(watson, args, from_, to):
itertools.takewhile(lambda s: not s.startswith('+'), args)
)

# 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)

# 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)

# add a new frame, call watson save to update state files
frame = watson.add(project=project, tags=tags, from_date=from_, to_date=to)
click.echo(
Expand All @@ -1031,9 +1068,13 @@ def add(watson, args, from_, to):


@cli.command(context_settings={'ignore_unknown_options': True})
@click.option('-c', '--confirm-new-project', is_flag=True, default=False,
help="Confirm addition of new project.")
@click.option('-b', '--confirm-new-tag', is_flag=True, default=False,
help="Confirm creation of new tag.")
@click.argument('id', required=False)
@click.pass_obj
def edit(watson, id):
def edit(watson, confirm_new_project, confirm_new_tag, id):
"""
Edit a frame.
Expand Down Expand Up @@ -1093,7 +1134,15 @@ def edit(watson, id):
try:
data = json.loads(output)
project = data['project']
# 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)
tags = data['tags']
# 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)
start = arrow.get(data['start'], datetime_format).replace(
tzinfo=local_tz).to('utc')
stop = arrow.get(data['stop'], datetime_format).replace(
Expand Down
23 changes: 23 additions & 0 deletions watson/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,29 @@
text_type = str


def confirm_project(project, watson_projects):
"""
Ask user to confirm creation of a new project
Returns True on accept and raises click.exceptions.Abort on reject
"""
if project not in watson_projects:
msg = "Project '%s' does not exist yet. Create it?" % project
click.confirm(msg, abort=True)
return True


def confirm_tags(tags, watson_tags):
"""
Ask user to confirm creation of new tags (each separately)
Returns True if all accepted and raises click.exceptions.Abort on reject
"""
for tag in tags:
if tag not in watson_tags:
msg = "Tag '%s' does not exist yet. Create it?" % tag
click.confirm(msg, abort=True)
return True


def style(name, element):
def _style_tags(tags):
if not tags:
Expand Down

0 comments on commit 9f9f005

Please sign in to comment.