Skip to content

Commit

Permalink
FEA: Tag list type (#159)
Browse files Browse the repository at this point in the history
Resolves: #12
  • Loading branch information
GiantsLoveDeathMetal authored Feb 18, 2018
1 parent b1bc738 commit 40eeb56
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 11 deletions.
6 changes: 3 additions & 3 deletions foolscap/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@
}


def action(do_action, arg):
def action(do_action, arg, list_type='normal'):
func = FUNCTION_MAP[do_action]

new_action = None
if do_action == 'list':
# Quitting from list calls exit() method.
# arg is filter in this case
if arg:
new_action = func(arg)
new_action = func(arg, list_type)
else:
new_action = func(None)
new_action = func(None, list_type)

if new_action:
new_func, note = new_action
Expand Down
14 changes: 13 additions & 1 deletion foolscap/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
'migrate',
]

LIST_TYPES = [
'tags',
]


parser = argparse.ArgumentParser()
parser.add_argument(
'command',
Expand All @@ -25,12 +30,19 @@
action='store',
nargs='?',
)
parser.add_argument(
'-t',
'--list_type',
default='normal',
choices=LIST_TYPES,
)


def main():
args = parser.parse_args()
command = args.command
note_args = args.positional
list_type = args.list_type

action(command, note_args)
action(command, note_args, list_type)

43 changes: 41 additions & 2 deletions foolscap/note_display.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from foolscap.display.console import display_list
from collections import Counter, OrderedDict

from foolscap.meta_data import load_meta
from foolscap.display.console import display_list

TOP_X_VIEWED = 3
# This rule is so perfectly subtle,
Expand All @@ -9,7 +11,11 @@
SORT_BY_VIEWS_RULE = 5


def list_notes(tags):
class OrderedCounter(Counter, OrderedDict):
pass


def list_notes(tags, list_type='normal'):
""" Presents notes in the terminal.
all_notes = {
Expand All @@ -31,6 +37,9 @@ def list_notes(tags):
for key, values in all_notes.items()
if 'tags' in values and tags in values['tags']
}
if list_type == 'tags':
display_notes = create_tag_display(all_notes)
return display_list(display_notes)

if len(all_notes) == 0:
# Fuzzy here
Expand All @@ -42,6 +51,36 @@ def list_notes(tags):
return display_list(display_notes)


def count_tags(all_notes):
list_tags = []
for key, values in all_notes.items():
list_tags.extend(values['tags'])
return OrderedCounter(list_tags)


def get_by_tag(all_notes, tag):
notes = {key: values
for key, values in all_notes.items()
if 'tags' in values and tag in values['tags']}
notes = [(note, values['description']) for note, values in notes.items()]
return sorted(notes, key=lambda x: x[0].lower())


def create_tag_display(all_notes):
display = []
while all_notes:
tag_count = count_tags(all_notes)
max_tag, count = tag_count.most_common(1)[0]
display_tag = {}
display_tag['title'] = max_tag
display_tag['description'] = str(count)
display_tag['sub_headings'] = get_by_tag(all_notes, max_tag)
display.append(display_tag)
for key, _ in display_tag['sub_headings']:
all_notes.pop(key, 0)
return display


def display_information(sorted_notes, note_dict):
""" Get description from list of note titles"""
display_notes = []
Expand Down
16 changes: 13 additions & 3 deletions tests/test_actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def test_list_note_command_no_tags():
# This is tricky, as list calls exit() if it quits in menu object
# but cause we don't call the menu object, we return None
# and have to expect the func to be called again...
expected = [call(None), call()]
expected = [call(None, 'normal'), call()]

# Pass to actor:
actor.action('list', None)
Expand All @@ -72,7 +72,7 @@ def test_list_note_command_tags():
# List with tags expects:
# Note: same as last test.
expected = [
call('tag'),
call('tag', 'normal'),
call('tag')
]

Expand All @@ -92,7 +92,7 @@ def test_list_note_command_returning_func():
# If list function returns a new-action:
mock_list.return_value = ('view', 'mock_note')

expected_list = call('tag')
expected_list = call('tag', 'normal')
expected_view = call('mock_note')

actor.action('list', 'tag')
Expand Down Expand Up @@ -164,3 +164,13 @@ def test_all_actions():
assert set(ACTIONS) == set(TESTED_ACTIONS)


def test_change_list_type():
mock_action = MagicMock()
with patch.dict('foolscap.actor.FUNCTION_MAP', {'list': mock_action}):
mock_action.return_value = None
expected = [
call(None, 'tags'),
call()
]
actor.action('list', None, 'tags')
assert mock_action.call_args_list == expected
6 changes: 4 additions & 2 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_valid_command():
with patch('sys.argv', test_args),\
patch('foolscap.cli.action') as mock_action:

expected = call('save', 'mock_note.txt')
expected = call('save', 'mock_note.txt', 'normal')
main()
assert mock_action.call_args == expected

Expand All @@ -34,7 +34,7 @@ def test_valid_command_optional():
with patch('sys.argv', test_args),\
patch('foolscap.cli.action') as mock_action:

expected = call('new', None)
expected = call('new', None, 'normal')
main()
assert mock_action.call_args == expected

Expand All @@ -52,3 +52,5 @@ def test_invalid_command():
pytest.raises(SystemExit):
main()


# Add changing list type test
53 changes: 53 additions & 0 deletions tests/test_note_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,56 @@ def test_sort_notes(test_dict, expected):
result = note_display.sort_notes(test_dict)
assert result == expected


def test_listing_tags():
def fake_return(input_param):
return input_param

with patch('foolscap.note_display.display_list') as mock_display,\
patch('foolscap.note_display.load_meta') as mock_meta:
mock_meta.return_value = FAKE_MANY_NOTES.copy()
mock_display.side_effect = fake_return
result = note_display.list_notes(None, 'tags')
mock_display.assert_called_once()
assert result == [{'title': 'fake_tag', 'description': str(7),
'sub_headings': [('A', 'This is a fake note'),
('fake_note_1', 'This is a fake note'),
('most_viewed', 'This is a fake note'),
('recently_opened', 'This is a fake note'),
('second_most', 'This is a fake note'),
('third_most', 'This is a fake note'),
('Z', 'This is a fake note')]}]


def test_count_tags():
mock_notes = FAKE_MANY_NOTES.copy()
result = note_display.count_tags(mock_notes)
assert result.most_common(1)[0] == ('fake_tag', 7)


def test_get_by_tag():
# Get all notes with tag: 'fake_tag'
mock_notes = FAKE_MANY_NOTES.copy()
result = note_display.get_by_tag(mock_notes, 'fake_tag')
assert result == [('A', 'This is a fake note'),
('fake_note_1', 'This is a fake note'),
('most_viewed', 'This is a fake note'),
('recently_opened', 'This is a fake note'),
('second_most', 'This is a fake note'),
('third_most', 'This is a fake note'),
('Z', 'This is a fake note')]


def test_create_tag_display():
# Get all notes with tag: 'fake_tag'
mock_notes = FAKE_MANY_NOTES.copy()
result = note_display.create_tag_display(mock_notes)
assert result == [{'title': 'fake_tag', 'description': str(7),
'sub_headings': [('A', 'This is a fake note'),
('fake_note_1', 'This is a fake note'),
('most_viewed', 'This is a fake note'),
('recently_opened', 'This is a fake note'),
('second_most', 'This is a fake note'),
('third_most', 'This is a fake note'),
('Z', 'This is a fake note')]}]

0 comments on commit 40eeb56

Please sign in to comment.