Skip to content
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
8 changes: 7 additions & 1 deletion docs/user-guide/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -705,17 +705,23 @@ If `--at` option is given, the provided stopping time is used. The
specified time must be after the beginning of the to-be-ended frame and must
not be in the future.

Example:
You can optionally pass a log message to be saved with the frame via
the ``-n/--note`` option.

Example:

$ watson stop --at 13:37
Stopping project apollo11, started an hour ago and stopped 30 minutes ago. (id: e9ccd52) # noqa: E501
$ watson stop -n "Done some thinking"
Stopping project apollo11, started a minute ago. (id: e7ccd52)
Log message: Done some thinking

### Options

Flag | Help
-----|-----
`--at DATETIME` | Stop frame at this time. Must be in (YYYY-MM-DDT)?HH:MM(:SS)? format.
`-n, --note TEXT` | Save given log message with the project frame.
`--help` | Show this message and exit.

## `sync`
Expand Down
4 changes: 2 additions & 2 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def test_frames_to_csv(watson):
result = frames_to_csv(watson.frames)

read_csv = list(csv.reader(StringIO(result)))
header = ['id', 'start', 'stop', 'project', 'tags']
header = ['id', 'start', 'stop', 'project', 'tags', 'note']
assert len(read_csv) == 2
assert read_csv[0] == header
assert read_csv[1][3] == 'foo'
Expand All @@ -319,7 +319,7 @@ def test_frames_to_json(watson):

result = json.loads(frames_to_json(watson.frames))

keys = {'id', 'start', 'stop', 'project', 'tags'}
keys = {'id', 'start', 'stop', 'project', 'tags', 'note'}
assert len(result) == 1
assert set(result[0].keys()) == keys
assert result[0]['project'] == 'foo'
Expand Down
111 changes: 108 additions & 3 deletions tests/test_watson.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,54 @@ def test_frames_without_tags(mocker, watson):
assert watson.frames[0].tags == []


def test_frames_with_note(mocker, watson):
"""Test loading frames with notes."""
content = json.dumps([
[3601, 3610, 'foo', 'abcdefg', ['A', 'B', 'C'], 3650,
"My hovercraft is full of eels"]
])

mocker.patch('builtins.open', mocker.mock_open(read_data=content))
assert len(watson.frames) == 1
frame = watson.frames['abcdefg']
assert frame.id == 'abcdefg'
assert frame.project == 'foo'
assert frame.start == arrow.get(3601)
assert frame.stop == arrow.get(3610)
assert frame.tags == ['A', 'B', 'C']
assert frame.note == "My hovercraft is full of eels"


def test_frames_without_note(mocker, watson):
"""Test loading frames without notes."""
content = json.dumps([
[3601, 3610, 'foo', 'abcdefg'],
[3611, 3620, 'foo', 'hijklmn', ['A', 'B', 'C']],
[3621, 3630, 'foo', 'opqrstu', ['A', 'B', 'C'], 3630]
])

mocker.patch('builtins.open', mocker.mock_open(read_data=content))
assert len(watson.frames) == 3
frame = watson.frames['abcdefg']
assert frame.id == 'abcdefg'
assert frame.project == 'foo'
assert frame.start == arrow.get(3601)
assert frame.stop == arrow.get(3610)
assert frame.tags == []
assert frame.note is None

frame = watson.frames['hijklmn']
assert frame.id == 'hijklmn'
assert frame.tags == ['A', 'B', 'C']
assert frame.note is None

frame = watson.frames['opqrstu']
assert frame.id == 'opqrstu'
assert frame.tags == ['A', 'B', 'C']
assert frame.updated_at == arrow.get(3630)
assert frame.note is None


def test_frames_with_empty_file(mocker, watson):
mocker.patch('builtins.open', mocker.mock_open(read_data=""))
mocker.patch('os.path.getsize', return_value=0)
Expand Down Expand Up @@ -315,6 +363,32 @@ def test_stop_started_project_without_tags(watson):
assert watson.frames[0].tags == []


def test_stop_started_project_without_note(watson):
"""Test stopping watson without adding a note."""
watson.start('foo')
watson.stop()

assert watson.current == {}
assert watson.is_started is False
assert len(watson.frames) == 1
frame = watson.frames[0]
assert frame.project == 'foo'
assert frame.note is None


def test_stop_started_project_with_note(watson):
"""Test stopping watson when adding a note."""
watson.start('foo')
watson.stop(None, "My hovercraft is full of eels")

assert watson.current == {}
assert watson.is_started is False
assert len(watson.frames) == 1
frame = watson.frames[0]
assert frame.project == 'foo'
assert frame.note == "My hovercraft is full of eels"


def test_stop_no_project(watson):
with pytest.raises(WatsonError):
watson.stop()
Expand Down Expand Up @@ -403,7 +477,8 @@ def test_save_empty_current(config_dir, mocker, json_mock):

assert json_mock.call_count == 1
result = json_mock.call_args[0][0]
assert result == {'project': 'foo', 'start': 4000, 'tags': []}
assert result == {'project': 'foo', 'start': 4000,
'tags': [], 'note': None}

watson.current = {}
watson.save()
Expand Down Expand Up @@ -763,9 +838,12 @@ def test_report(watson):
assert 'time' in report['projects'][0]['tags'][0]
assert report['projects'][0]['tags'][1]['name'] == 'B'
assert 'time' in report['projects'][0]['tags'][1]
assert len(report['projects'][0]['notes']) == 0
assert len(report['projects'][0]['tags'][0]['notes']) == 0
assert len(report['projects'][0]['tags'][1]['notes']) == 0

watson.start('bar', tags=['C'])
watson.stop()
watson.stop(note='bar note')

report = watson.report(arrow.now(), arrow.now())
assert len(report['projects']) == 2
Expand All @@ -774,6 +852,13 @@ def test_report(watson):
assert len(report['projects'][0]['tags']) == 1
assert report['projects'][0]['tags'][0]['name'] == 'C'

assert len(report['projects'][1]['notes']) == 0
assert len(report['projects'][1]['tags'][0]['notes']) == 0
assert len(report['projects'][1]['tags'][1]['notes']) == 0
assert len(report['projects'][0]['notes']) == 0
assert len(report['projects'][0]['tags'][0]['notes']) == 1
assert report['projects'][0]['tags'][0]['notes'][0] == 'bar note'

report = watson.report(
arrow.now(), arrow.now(), projects=['foo'], tags=['B']
)
Expand All @@ -783,16 +868,36 @@ def test_report(watson):
assert report['projects'][0]['tags'][0]['name'] == 'B'

watson.start('baz', tags=['D'])
watson.stop()
watson.stop(note='baz note')

watson.start('foo')
watson.stop(note='foo no tags')

watson.start('foo', tags=['A'])
watson.stop(note='foo one tag A')

report = watson.report(arrow.now(), arrow.now(), projects=["foo"])

assert len(report['projects']) == 1
assert len(report['projects'][0]['notes']) == 1
# A project-level note because this frame has no tags
assert report['projects'][0]['notes'][0] == 'foo no tags'
assert len(report['projects'][0]['tags']) == 2
assert report['projects'][0]['tags'][0]['name'] == 'A'
assert report['projects'][0]['tags'][1]['name'] == 'B'
assert len(report['projects'][0]['tags'][0]['notes']) == 1
assert len(report['projects'][0]['tags'][1]['notes']) == 0
# A tag-level note because this frame has tags
assert report['projects'][0]['tags'][0]['notes'][0] == 'foo one tag A'

report = watson.report(arrow.now(), arrow.now(), ignore_projects=["bar"])
assert len(report['projects']) == 2

report = watson.report(arrow.now(), arrow.now(), tags=["A"])
assert len(report['projects']) == 1
assert len(report['projects'][0]['notes']) == 0
assert len(report['projects'][0]['tags'][0]['notes']) == 1
assert report['projects'][0]['tags'][0]['notes'][0] == 'foo one tag A'

report = watson.report(arrow.now(), arrow.now(), ignore_tags=["D"])
assert len(report['projects']) == 2
Expand Down
Loading