Skip to content

Commit 06f4764

Browse files
committed
Support notes with checkboxes
1 parent 4d7ced4 commit 06f4764

File tree

8 files changed

+252
-4
lines changed

8 files changed

+252
-4
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
__pycache__
44
/dist
55
/*.egg-info
6+
build

.vscode/launch.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Python: Debug",
9+
"type": "python",
10+
"request": "launch",
11+
"program": "./gkeep",
12+
"args": [
13+
"notes",
14+
15+
// "get",
16+
// "1622667139340.93561477"
17+
18+
"edit",
19+
"--filter-id",
20+
"1622667139340.93561477",
21+
"--text"
22+
23+
// "edit",
24+
// "--filter-title",
25+
// "TODOloulou",
26+
// "--text",
27+
// "coucou"
28+
],
29+
"console": "integratedTerminal"
30+
}
31+
]
32+
}

chardetect

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/python
2+
# EASY-INSTALL-ENTRY-SCRIPT: 'chardet','console_scripts','chardetect'
3+
import re
4+
import sys
5+
6+
# for compatibility with easy_install; see #2198
7+
__requires__ = 'chardet'
8+
9+
try:
10+
from importlib.metadata import distribution
11+
except ImportError:
12+
try:
13+
from importlib_metadata import distribution
14+
except ImportError:
15+
from pkg_resources import load_entry_point
16+
17+
18+
def importlib_load_entry_point(spec, group, name):
19+
dist_name, _, _ = spec.partition('==')
20+
matches = (
21+
entry_point
22+
for entry_point in distribution(dist_name).entry_points
23+
if entry_point.group == group and entry_point.name == name
24+
)
25+
return next(matches).load()
26+
27+
28+
globals().setdefault('load_entry_point', importlib_load_entry_point)
29+
30+
31+
if __name__ == '__main__':
32+
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
33+
sys.exit(load_entry_point('chardet', 'console_scripts', 'chardetect')())

futurize

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/python
2+
# EASY-INSTALL-ENTRY-SCRIPT: 'future','console_scripts','futurize'
3+
import re
4+
import sys
5+
6+
# for compatibility with easy_install; see #2198
7+
__requires__ = 'future'
8+
9+
try:
10+
from importlib.metadata import distribution
11+
except ImportError:
12+
try:
13+
from importlib_metadata import distribution
14+
except ImportError:
15+
from pkg_resources import load_entry_point
16+
17+
18+
def importlib_load_entry_point(spec, group, name):
19+
dist_name, _, _ = spec.partition('==')
20+
matches = (
21+
entry_point
22+
for entry_point in distribution(dist_name).entry_points
23+
if entry_point.group == group and entry_point.name == name
24+
)
25+
return next(matches).load()
26+
27+
28+
globals().setdefault('load_entry_point', importlib_load_entry_point)
29+
30+
31+
if __name__ == '__main__':
32+
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
33+
sys.exit(load_entry_point('future', 'console_scripts', 'futurize')())

gkeep

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/python
2+
# EASY-INSTALL-ENTRY-SCRIPT: 'gkeep','console_scripts','gkeep'
3+
import re
4+
import sys
5+
6+
# for compatibility with easy_install; see #2198
7+
__requires__ = 'gkeep'
8+
9+
try:
10+
from importlib.metadata import distribution
11+
except ImportError:
12+
try:
13+
from importlib_metadata import distribution
14+
except ImportError:
15+
from pkg_resources import load_entry_point
16+
17+
18+
def importlib_load_entry_point(spec, group, name):
19+
dist_name, _, _ = spec.partition('==')
20+
matches = (
21+
entry_point
22+
for entry_point in distribution(dist_name).entry_points
23+
if entry_point.group == group and entry_point.name == name
24+
)
25+
return next(matches).load()
26+
27+
28+
globals().setdefault('load_entry_point', importlib_load_entry_point)
29+
30+
31+
if __name__ == '__main__':
32+
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
33+
sys.exit(load_entry_point('gkeep', 'console_scripts', 'gkeep')())

gkeep.egg-link

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/home/corentin/playground/gkeep
2+
.

google_keep_tasks/notes.py

+85-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import click
33
import gkeepapi
44
import sys
5+
import re
6+
7+
from gkeepapi.node import List
58

69
from google_keep_tasks.cli import GkeepGroup
710
from google_keep_tasks.exceptions import InvalidColor
@@ -73,6 +76,21 @@ def query_params(keep, **kwargs):
7376
set(x.labels.all()) == set(labels) if labels is not None else None]))
7477
return kwargs
7578

79+
def format_note_item(item):
80+
return u'%s%s %s' % (
81+
' ' if item.indented else '',
82+
u'- [x]' if item.checked else u'- [ ]',
83+
item.text
84+
)
85+
86+
def format_note(note):
87+
if not isinstance(note, List):
88+
return note.text
89+
text = ""
90+
for item in note.items:
91+
text += "%s | %s\n" % (item.id.ljust(30), format_note_item(item))
92+
return text
93+
7694

7795
def print_note(note):
7896
params = COLORS.get(note.color, {})
@@ -82,15 +100,74 @@ def print_note(note):
82100
click.echo(click.style('"' * len(note_id), **params))
83101
if note.title:
84102
click.echo(click.style(note.title, bold=True))
85-
click.echo(note.text)
103+
click.echo('-' * len(note_id))
104+
click.echo(format_note(note))
105+
click.echo('-' * len(note_id))
86106
if note.labels:
87-
click.echo()
88107
click.echo(' '.join(click.style('[{}]'.format(label.name), underline=True, bold=True)
89108
for label in note.labels.all()))
90109
click.echo(click.style('"' * len(note_id), **params))
91110
click.echo('\n')
92111

93112

113+
def edit_note_checkboxes(note):
114+
text = click.edit(format_note(note)).strip()
115+
lines = text.split("\n")
116+
regex = re.compile(r"([\w.]*) *\| ( *)- \[(x| )\] (.*)")
117+
118+
last_old_note = 'top'
119+
current_items = []
120+
for line in lines:
121+
id, indent, check_mark, content = regex.match(line).groups()
122+
found = list(filter(lambda item: item.id == id, note.items))
123+
old_note = found[0] if len(found) > 0 else None
124+
indented = len(indent) > 1
125+
checked = check_mark == 'x'
126+
current_items.append({
127+
'id': id,
128+
'previous': last_old_note,
129+
'old': old_note,
130+
'indented': indented,
131+
'checked': checked,
132+
'content': content
133+
})
134+
last_old_note = old_note
135+
136+
# Deletion
137+
for item in note.items:
138+
if item.id not in [parts['id'] for parts in current_items]:
139+
item.delete()
140+
141+
last_added = None
142+
for parts in current_items:
143+
previous = parts['previous'] if parts['previous'] != None else last_added
144+
145+
# Addition
146+
if parts['old'] == None:
147+
sort = int(previous.sort) - 1 if previous != None and previous != 'top' else gkeepapi.node.NewListItemPlacementValue.Top
148+
added = note.add(parts['content'], parts['checked'], sort)
149+
if parts['indented']:
150+
previous.indent(added)
151+
last_added = added
152+
153+
# Modification
154+
else:
155+
if parts['old'].indented and not parts['indented']:
156+
if previous != None:
157+
previous.dedent(parts['old'])
158+
if not parts['old'].indented and parts['indented']:
159+
if previous != None:
160+
previous.indent(parts['old'])
161+
162+
if parts['old'].checked and not parts['checked']:
163+
parts['old'].checked = False
164+
if not parts['old'].checked and parts['checked']:
165+
parts['old'].checked = True
166+
167+
if parts['old'].text != parts['content']:
168+
parts['old'].text = parts['content']
169+
170+
94171
def get_note_instance(keep, id=None, **kwargs):
95172
if id:
96173
note = keep.get(id)
@@ -234,7 +311,11 @@ def edit_note(ctx, title, text, color, labels, archived, pinned, filter_id, filt
234311
click.echo('The note was not found', err=True)
235312
sys.exit(2)
236313
if text == placeholder:
237-
text = click.edit(note.text).strip()
314+
if isinstance(note, List):
315+
edit_note_checkboxes(note)
316+
text = None
317+
else:
318+
text = click.edit(note.text).strip()
238319
updated = {}
239320
boolean_nullables = ['archived', 'pinned'] # 3 state params
240321
for param in ['title', 'text', 'color', 'labels'] + boolean_nullables:
@@ -275,4 +356,4 @@ def delete_note(ctx, **kwargs):
275356
click.echo('Note with title "{}" deleted.'.format(note.title))
276357
else:
277358
click.echo('The note was not found', err=True)
278-
sys.exit(2)
359+
sys.exit(2)

pasteurize

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/python
2+
# EASY-INSTALL-ENTRY-SCRIPT: 'future','console_scripts','pasteurize'
3+
import re
4+
import sys
5+
6+
# for compatibility with easy_install; see #2198
7+
__requires__ = 'future'
8+
9+
try:
10+
from importlib.metadata import distribution
11+
except ImportError:
12+
try:
13+
from importlib_metadata import distribution
14+
except ImportError:
15+
from pkg_resources import load_entry_point
16+
17+
18+
def importlib_load_entry_point(spec, group, name):
19+
dist_name, _, _ = spec.partition('==')
20+
matches = (
21+
entry_point
22+
for entry_point in distribution(dist_name).entry_points
23+
if entry_point.group == group and entry_point.name == name
24+
)
25+
return next(matches).load()
26+
27+
28+
globals().setdefault('load_entry_point', importlib_load_entry_point)
29+
30+
31+
if __name__ == '__main__':
32+
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
33+
sys.exit(load_entry_point('future', 'console_scripts', 'pasteurize')())

0 commit comments

Comments
 (0)