Skip to content

Commit 9b3b40d

Browse files
davidagjmaupetit
authored andcommitted
Unifies all command datetime inputs
Fixes #292 - Rename DateParamType to DateTimeParamType because it now supports times in the following formats: 'HH:mm' and 'HH:mm:ss'. - Remove TimeParamType by supporting its formats in DateTimeParamType. - Add the new valid formats to tests (previously invalid). - Tests for the 'stop' command now support the same dates/times than the other commands.
1 parent 11bf2fd commit 9b3b40d

File tree

2 files changed

+62
-59
lines changed

2 files changed

+62
-59
lines changed

tests/test_cli.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import arrow
33
from itertools import combinations
44
from datetime import datetime, timedelta
5-
from dateutil.tz import tzlocal
65

6+
from dateutil.tz import tzlocal
77
import pytest
88

99
from watson import cli
@@ -32,6 +32,18 @@
3232
('2018-04-10T12:30', '2018-04-10 12:30:00'),
3333
('2018-04-10 12', '2018-04-10 12:00:00'),
3434
('2018-04-10T12', '2018-04-10 12:00:00'),
35+
(
36+
'14:05:12',
37+
arrow.now()
38+
.replace(hour=14, minute=5, second=12)
39+
.format('YYYY-MM-DD HH:mm:ss')
40+
),
41+
(
42+
'14:05',
43+
arrow.now()
44+
.replace(hour=14, minute=5, second=0)
45+
.format('YYYY-MM-DD HH:mm:ss')
46+
),
3547
]
3648

3749
INVALID_DATES_DATA = [
@@ -49,9 +61,7 @@
4961
('tomorrow'),
5062
('14:05:12.000'), # Times alone are not allowed
5163
('140512.000'),
52-
('14:05:12'),
5364
('140512'),
54-
('14:05'),
5565
('14.05'),
5666
('2018-04-10T'),
5767
('2018-04-10T12:30:43.'),

watson/cli.py

+49-56
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import itertools
55
import json
66
import operator
7-
import re
87

98
from dateutil import tz
109
from functools import reduce, wraps
@@ -70,15 +69,17 @@ def _raise_exclusive_error(self):
7069
['`--{}`'.format(_) for _ in self.mutually_exclusive]))))
7170

7271

73-
class DateParamType(click.ParamType):
74-
name = 'date'
72+
class DateTimeParamType(click.ParamType):
73+
name = 'datetime'
7574

7675
def convert(self, value, param, ctx):
7776
if value:
78-
try:
79-
date = arrow.get(value)
80-
except (ValueError, TypeError) as e:
81-
raise click.UsageError(str(e))
77+
date = self._parse_multiformat(value)
78+
if date is None:
79+
raise click.UsageError(
80+
"Could not match value '{}' to any supported date format"
81+
.format(value)
82+
)
8283
# When we parse a date, we want to parse it in the timezone
8384
# expected by the user, so that midnight is midnight in the local
8485
# timezone, not in UTC. Cf issue #16.
@@ -92,34 +93,26 @@ def convert(self, value, param, ctx):
9293
start_time=date, week_start=week_start)
9394
return date
9495

95-
96-
class TimeParamType(click.ParamType):
97-
name = 'time'
98-
99-
def convert(self, value, param, ctx):
100-
if isinstance(value, arrow.Arrow):
101-
return value
102-
103-
date_pattern = r'\d{4}-\d\d-\d\d'
104-
time_pattern = r'\d\d:\d\d(:\d\d)?'
105-
106-
if re.match('^{time_pat}$'.format(time_pat=time_pattern), value):
107-
cur_date = arrow.now().date().isoformat()
108-
cur_time = '{date}T{time}'.format(date=cur_date, time=value)
109-
elif re.match('^{date_pat}T{time_pat}'.format(
110-
date_pat=date_pattern, time_pat=time_pattern), value):
111-
cur_time = value
112-
else:
113-
errmsg = ('Could not parse time.'
114-
'Please specify in (YYYY-MM-DDT)?HH:MM(:SS)? format.')
115-
raise click.ClickException(style('error', errmsg))
116-
117-
local_tz = tz.tzlocal()
118-
return arrow.get(cur_time).replace(tzinfo=local_tz)
96+
def _parse_multiformat(self, value):
97+
date = None
98+
for fmt in (None, 'HH:mm:ss', 'HH:mm'):
99+
try:
100+
if fmt is None:
101+
date = arrow.get(value)
102+
else:
103+
date = arrow.get(value, fmt)
104+
date = arrow.now().replace(
105+
hour=date.hour,
106+
minute=date.minute,
107+
second=date.second
108+
)
109+
break
110+
except (ValueError, TypeError):
111+
pass
112+
return date
119113

120114

121-
Date = DateParamType()
122-
Time = TimeParamType()
115+
DateTime = DateTimeParamType()
123116

124117

125118
def catch_watson_error(func):
@@ -250,7 +243,7 @@ def start(ctx, watson, confirm_new_project, confirm_new_tag, args, gap_=True):
250243

251244

252245
@cli.command(context_settings={'ignore_unknown_options': True})
253-
@click.option('--at', 'at_', type=Time, default=None,
246+
@click.option('--at', 'at_', type=DateTime, default=None,
254247
help=('Stop frame at this time. Must be in '
255248
'(YYYY-MM-DDT)?HH:MM(:SS)? format.'))
256249
@click.pass_obj
@@ -428,37 +421,37 @@ def status(watson, project, tags, elapsed):
428421
@cli.command()
429422
@click.option('-c/-C', '--current/--no-current', 'current', default=None,
430423
help="(Don't) include currently running frame in report.")
431-
@click.option('-f', '--from', 'from_', cls=MutuallyExclusiveOption, type=Date,
432-
default=arrow.now().shift(days=-7),
424+
@click.option('-f', '--from', 'from_', cls=MutuallyExclusiveOption,
425+
type=DateTime, default=arrow.now().shift(days=-7),
433426
mutually_exclusive=_SHORTCUT_OPTIONS,
434427
help="The date from when the report should start. Defaults "
435428
"to seven days ago.")
436-
@click.option('-t', '--to', cls=MutuallyExclusiveOption, type=Date,
429+
@click.option('-t', '--to', cls=MutuallyExclusiveOption, type=DateTime,
437430
default=arrow.now(),
438431
mutually_exclusive=_SHORTCUT_OPTIONS,
439432
help="The date at which the report should stop (inclusive). "
440433
"Defaults to tomorrow.")
441-
@click.option('-y', '--year', cls=MutuallyExclusiveOption, type=Date,
434+
@click.option('-y', '--year', cls=MutuallyExclusiveOption, type=DateTime,
442435
flag_value=_SHORTCUT_OPTIONS_VALUES['year'],
443436
mutually_exclusive=['day', 'week', 'luna', 'month', 'all'],
444437
help='Reports activity for the current year.')
445-
@click.option('-m', '--month', cls=MutuallyExclusiveOption, type=Date,
438+
@click.option('-m', '--month', cls=MutuallyExclusiveOption, type=DateTime,
446439
flag_value=_SHORTCUT_OPTIONS_VALUES['month'],
447440
mutually_exclusive=['day', 'week', 'luna', 'year', 'all'],
448441
help='Reports activity for the current month.')
449-
@click.option('-l', '--luna', cls=MutuallyExclusiveOption, type=Date,
442+
@click.option('-l', '--luna', cls=MutuallyExclusiveOption, type=DateTime,
450443
flag_value=_SHORTCUT_OPTIONS_VALUES['luna'],
451444
mutually_exclusive=['day', 'week', 'month', 'year', 'all'],
452445
help='Reports activity for the current moon cycle.')
453-
@click.option('-w', '--week', cls=MutuallyExclusiveOption, type=Date,
446+
@click.option('-w', '--week', cls=MutuallyExclusiveOption, type=DateTime,
454447
flag_value=_SHORTCUT_OPTIONS_VALUES['week'],
455448
mutually_exclusive=['day', 'month', 'luna', 'year', 'all'],
456449
help='Reports activity for the current week.')
457-
@click.option('-d', '--day', cls=MutuallyExclusiveOption, type=Date,
450+
@click.option('-d', '--day', cls=MutuallyExclusiveOption, type=DateTime,
458451
flag_value=_SHORTCUT_OPTIONS_VALUES['day'],
459452
mutually_exclusive=['week', 'month', 'luna', 'year', 'all'],
460453
help='Reports activity for the current day.')
461-
@click.option('-a', '--all', cls=MutuallyExclusiveOption, type=Date,
454+
@click.option('-a', '--all', cls=MutuallyExclusiveOption, type=DateTime,
462455
flag_value=_SHORTCUT_OPTIONS_VALUES['all'],
463456
mutually_exclusive=['day', 'week', 'month', 'luna', 'year'],
464457
help='Reports all activities.')
@@ -717,12 +710,12 @@ def _final_print(lines):
717710
@cli.command()
718711
@click.option('-c/-C', '--current/--no-current', 'current', default=None,
719712
help="(Don't) include currently running frame in report.")
720-
@click.option('-f', '--from', 'from_', cls=MutuallyExclusiveOption, type=Date,
721-
default=arrow.now().shift(days=-7),
713+
@click.option('-f', '--from', 'from_', cls=MutuallyExclusiveOption,
714+
type=DateTime, default=arrow.now().shift(days=-7),
722715
mutually_exclusive=_SHORTCUT_OPTIONS,
723716
help="The date from when the report should start. Defaults "
724717
"to seven days ago.")
725-
@click.option('-t', '--to', cls=MutuallyExclusiveOption, type=Date,
718+
@click.option('-t', '--to', cls=MutuallyExclusiveOption, type=DateTime,
726719
default=arrow.now(),
727720
mutually_exclusive=_SHORTCUT_OPTIONS,
728721
help="The date at which the report should stop (inclusive). "
@@ -861,34 +854,34 @@ def aggregate(ctx, watson, current, from_, to, projects, tags, output_format,
861854
@cli.command()
862855
@click.option('-c/-C', '--current/--no-current', 'current', default=None,
863856
help="(Don't) include currently running frame in output.")
864-
@click.option('-f', '--from', 'from_', type=Date,
857+
@click.option('-f', '--from', 'from_', type=DateTime,
865858
default=arrow.now().shift(days=-7),
866859
help="The date from when the log should start. Defaults "
867860
"to seven days ago.")
868-
@click.option('-t', '--to', type=Date, default=arrow.now(),
861+
@click.option('-t', '--to', type=DateTime, default=arrow.now(),
869862
help="The date at which the log should stop (inclusive). "
870863
"Defaults to tomorrow.")
871-
@click.option('-y', '--year', cls=MutuallyExclusiveOption, type=Date,
864+
@click.option('-y', '--year', cls=MutuallyExclusiveOption, type=DateTime,
872865
flag_value=_SHORTCUT_OPTIONS_VALUES['year'],
873866
mutually_exclusive=['day', 'week', 'month', 'all'],
874867
help='Reports activity for the current year.')
875-
@click.option('-m', '--month', cls=MutuallyExclusiveOption, type=Date,
868+
@click.option('-m', '--month', cls=MutuallyExclusiveOption, type=DateTime,
876869
flag_value=_SHORTCUT_OPTIONS_VALUES['month'],
877870
mutually_exclusive=['day', 'week', 'year', 'all'],
878871
help='Reports activity for the current month.')
879-
@click.option('-l', '--luna', cls=MutuallyExclusiveOption, type=Date,
872+
@click.option('-l', '--luna', cls=MutuallyExclusiveOption, type=DateTime,
880873
flag_value=_SHORTCUT_OPTIONS_VALUES['luna'],
881874
mutually_exclusive=['day', 'week', 'month', 'year', 'all'],
882875
help='Reports activity for the current moon cycle.')
883-
@click.option('-w', '--week', cls=MutuallyExclusiveOption, type=Date,
876+
@click.option('-w', '--week', cls=MutuallyExclusiveOption, type=DateTime,
884877
flag_value=_SHORTCUT_OPTIONS_VALUES['week'],
885878
mutually_exclusive=['day', 'month', 'year', 'all'],
886879
help='Reports activity for the current week.')
887-
@click.option('-d', '--day', cls=MutuallyExclusiveOption, type=Date,
880+
@click.option('-d', '--day', cls=MutuallyExclusiveOption, type=DateTime,
888881
flag_value=_SHORTCUT_OPTIONS_VALUES['day'],
889882
mutually_exclusive=['week', 'month', 'year', 'all'],
890883
help='Reports activity for the current day.')
891-
@click.option('-a', '--all', cls=MutuallyExclusiveOption, type=Date,
884+
@click.option('-a', '--all', cls=MutuallyExclusiveOption, type=DateTime,
892885
flag_value=_SHORTCUT_OPTIONS_VALUES['all'],
893886
mutually_exclusive=['day', 'week', 'month', 'year'],
894887
help='Reports all activities.')
@@ -1138,9 +1131,9 @@ def frames(watson):
11381131
@cli.command(context_settings={'ignore_unknown_options': True})
11391132
@click.argument('args', nargs=-1,
11401133
autocompletion=get_project_or_task_completion)
1141-
@click.option('-f', '--from', 'from_', required=True, type=Date,
1134+
@click.option('-f', '--from', 'from_', required=True, type=DateTime,
11421135
help="Date and time of start of tracked activity")
1143-
@click.option('-t', '--to', required=True, type=Date,
1136+
@click.option('-t', '--to', required=True, type=DateTime,
11441137
help="Date and time of end of tracked activity")
11451138
@click.option('-c', '--confirm-new-project', is_flag=True, default=False,
11461139
help="Confirm addition of new project.")

0 commit comments

Comments
 (0)