Skip to content

Commit 4cbd284

Browse files
aero31aeroprat0088joelostblom
committed
Add 'notes' field to frames.
This is a large commit that adds the following: 1. Syntax: `watson stop --notes "some additional information"`. 2. Print only non-empty notes in log. 3. Always pass id to `new_frame` so that the length of array with/without notes doesn't cause ambiguity. 4. Print a warning message and the existing note if overwriting a note. 5. Print notes in report. Primary work here was done by the following people: Co-authored-by: Tristan Pratt <[email protected]> Co-authored-by: Joel Ostblom <[email protected]>
1 parent 2cb871e commit 4cbd284

File tree

7 files changed

+301
-71
lines changed

7 files changed

+301
-71
lines changed

docs/user-guide/commands.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,17 +689,23 @@ If '--at' option is given, the provided stopping time is used. The
689689
specified time must be after the begin of the to be ended frame and must
690690
not be in the future.
691691

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

695+
Example:
694696

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

698703
### Options
699704

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

705711
## `sync`

tests/test_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ def test_frames_to_csv(watson):
311311
result = frames_to_csv(watson.frames)
312312

313313
read_csv = list(csv.reader(StringIO(result)))
314-
header = ['id', 'start', 'stop', 'project', 'tags']
314+
header = ['id', 'start', 'stop', 'project', 'tags', 'note']
315315
assert len(read_csv) == 2
316316
assert read_csv[0] == header
317317
assert read_csv[1][3] == 'foo'
@@ -330,7 +330,7 @@ def test_frames_to_json(watson):
330330

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

333-
keys = {'id', 'start', 'stop', 'project', 'tags'}
333+
keys = {'id', 'start', 'stop', 'project', 'tags', 'note'}
334334
assert len(result) == 1
335335
assert set(result[0].keys()) == keys
336336
assert result[0]['project'] == 'foo'

tests/test_watson.py

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,54 @@ def test_frames_without_tags(mocker, watson):
150150
assert watson.frames[0].tags == []
151151

152152

153+
def test_frames_with_note(mocker, watson):
154+
"""Test loading frames with notes."""
155+
content = json.dumps([
156+
[3601, 3610, 'foo', 'abcdefg', ['A', 'B', 'C'], 3650,
157+
"My hovercraft is full of eels"]
158+
])
159+
160+
mocker.patch('%s.open' % builtins, mocker.mock_open(read_data=content))
161+
assert len(watson.frames) == 1
162+
frame = watson.frames['abcdefg']
163+
assert frame.id == 'abcdefg'
164+
assert frame.project == 'foo'
165+
assert frame.start == arrow.get(3601)
166+
assert frame.stop == arrow.get(3610)
167+
assert frame.tags == ['A', 'B', 'C']
168+
assert frame.note == "My hovercraft is full of eels"
169+
170+
171+
def test_frames_without_note(mocker, watson):
172+
"""Test loading frames without notes."""
173+
content = json.dumps([
174+
[3601, 3610, 'foo', 'abcdefg'],
175+
[3611, 3620, 'foo', 'hijklmn', ['A', 'B', 'C']],
176+
[3621, 3630, 'foo', 'opqrstu', ['A', 'B', 'C'], 3630]
177+
])
178+
179+
mocker.patch('%s.open' % builtins, mocker.mock_open(read_data=content))
180+
assert len(watson.frames) == 3
181+
frame = watson.frames['abcdefg']
182+
assert frame.id == 'abcdefg'
183+
assert frame.project == 'foo'
184+
assert frame.start == arrow.get(3601)
185+
assert frame.stop == arrow.get(3610)
186+
assert frame.tags == []
187+
assert frame.note is None
188+
189+
frame = watson.frames['hijklmn']
190+
assert frame.id == 'hijklmn'
191+
assert frame.tags == ['A', 'B', 'C']
192+
assert frame.note is None
193+
194+
frame = watson.frames['opqrstu']
195+
assert frame.id == 'opqrstu'
196+
assert frame.tags == ['A', 'B', 'C']
197+
assert frame.updated_at == arrow.get(3630)
198+
assert frame.note is None
199+
200+
153201
def test_frames_with_empty_file(mocker, watson):
154202
mocker.patch('%s.open' % builtins, mocker.mock_open(read_data=""))
155203
mocker.patch('os.path.getsize', return_value=0)
@@ -322,6 +370,32 @@ def test_stop_started_project_without_tags(watson):
322370
assert watson.frames[0].tags == []
323371

324372

373+
def test_stop_started_project_without_note(watson):
374+
"""Test stopping watson without adding a note."""
375+
watson.start('foo')
376+
watson.stop()
377+
378+
assert watson.current == {}
379+
assert watson.is_started is False
380+
assert len(watson.frames) == 1
381+
frame = watson.frames[0]
382+
assert frame.project == 'foo'
383+
assert frame.note is None
384+
385+
386+
def test_stop_started_project_with_note(watson):
387+
"""Test stopping watson when adding a note."""
388+
watson.start('foo')
389+
watson.stop(None, "My hovercraft is full of eels")
390+
391+
assert watson.current == {}
392+
assert watson.is_started is False
393+
assert len(watson.frames) == 1
394+
frame = watson.frames[0]
395+
assert frame.project == 'foo'
396+
assert frame.note == "My hovercraft is full of eels"
397+
398+
325399
def test_stop_no_project(watson):
326400
with pytest.raises(WatsonError):
327401
watson.stop()
@@ -410,7 +484,8 @@ def test_save_empty_current(config_dir, mocker, json_mock):
410484

411485
assert json_mock.call_count == 1
412486
result = json_mock.call_args[0][0]
413-
assert result == {'project': 'foo', 'start': 4000, 'tags': []}
487+
assert result == {'project': 'foo', 'start': 4000,
488+
'tags': [], 'note': None}
414489

415490
watson.current = {}
416491
watson.save()
@@ -770,9 +845,12 @@ def test_report(watson):
770845
assert 'time' in report['projects'][0]['tags'][0]
771846
assert report['projects'][0]['tags'][1]['name'] == 'B'
772847
assert 'time' in report['projects'][0]['tags'][1]
848+
assert len(report['projects'][0]['notes']) == 0
849+
assert len(report['projects'][0]['tags'][0]['notes']) == 0
850+
assert len(report['projects'][0]['tags'][1]['notes']) == 0
773851

774852
watson.start('bar', tags=['C'])
775-
watson.stop()
853+
watson.stop(note='bar note')
776854

777855
report = watson.report(arrow.now(), arrow.now())
778856
assert len(report['projects']) == 2
@@ -781,6 +859,13 @@ def test_report(watson):
781859
assert len(report['projects'][0]['tags']) == 1
782860
assert report['projects'][0]['tags'][0]['name'] == 'C'
783861

862+
assert len(report['projects'][1]['notes']) == 0
863+
assert len(report['projects'][1]['tags'][0]['notes']) == 0
864+
assert len(report['projects'][1]['tags'][1]['notes']) == 0
865+
assert len(report['projects'][0]['notes']) == 0
866+
assert len(report['projects'][0]['tags'][0]['notes']) == 1
867+
assert report['projects'][0]['tags'][0]['notes'][0] == 'bar note'
868+
784869
report = watson.report(
785870
arrow.now(), arrow.now(), projects=['foo'], tags=['B']
786871
)
@@ -790,16 +875,36 @@ def test_report(watson):
790875
assert report['projects'][0]['tags'][0]['name'] == 'B'
791876

792877
watson.start('baz', tags=['D'])
793-
watson.stop()
878+
watson.stop(note='baz note')
879+
880+
watson.start('foo')
881+
watson.stop(note='foo no tags')
882+
883+
watson.start('foo', tags=['A'])
884+
watson.stop(note='foo one tag A')
794885

795886
report = watson.report(arrow.now(), arrow.now(), projects=["foo"])
887+
796888
assert len(report['projects']) == 1
889+
assert len(report['projects'][0]['notes']) == 1
890+
# A project-level note because this frame has no tags
891+
assert report['projects'][0]['notes'][0] == 'foo no tags'
892+
assert len(report['projects'][0]['tags']) == 2
893+
assert report['projects'][0]['tags'][0]['name'] == 'A'
894+
assert report['projects'][0]['tags'][1]['name'] == 'B'
895+
assert len(report['projects'][0]['tags'][0]['notes']) == 1
896+
assert len(report['projects'][0]['tags'][1]['notes']) == 0
897+
# A tag-level note because this frame has tags
898+
assert report['projects'][0]['tags'][0]['notes'][0] == 'foo one tag A'
797899

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

801903
report = watson.report(arrow.now(), arrow.now(), tags=["A"])
802904
assert len(report['projects']) == 1
905+
assert len(report['projects'][0]['notes']) == 0
906+
assert len(report['projects'][0]['tags'][0]['notes']) == 1
907+
assert report['projects'][0]['tags'][0]['notes'][0] == 'foo one tag A'
803908

804909
report = watson.report(arrow.now(), arrow.now(), ignore_tags=["D"])
805910
assert len(report['projects']) == 2

0 commit comments

Comments
 (0)