Skip to content

Commit 57f522c

Browse files
committed
Enable virtual tags and refactor scripts
1 parent 64f1075 commit 57f522c

File tree

8 files changed

+430
-371
lines changed

8 files changed

+430
-371
lines changed

syncall/cli.py

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@
44
extra dependency.
55
"""
66

7+
import os
78
import sys
89

910
import click
11+
from bubop import logger
1012

1113
from syncall import __version__
12-
from syncall.app_utils import name_to_resolution_strategy_type
14+
from syncall.app_utils import (
15+
error_and_exit,
16+
fetch_from_pass_manager,
17+
name_to_resolution_strategy_type,
18+
)
1319
from syncall.constants import COMBINATION_FLAGS
1420
from syncall.pdb_cli_utils import run_pdb_on_error as _run_pdb_on_error
1521

@@ -35,6 +41,27 @@ def opt_pdb_on_error():
3541

3642

3743
# asana related options -----------------------------------------------------------------------
44+
def opts_asana(hidden_gid: bool):
45+
def decorator(f):
46+
for d in reversed(
47+
[
48+
opt_asana_token_pass_path,
49+
opt_asana_workspace_gid,
50+
opt_asana_workspace_name,
51+
opt_list_asana_workspaces,
52+
]
53+
):
54+
f = d()(f)
55+
56+
# --asana-task-gid is used to ease development and debugging. It is not currently
57+
# suitable for regular use.
58+
f = opt_asana_task_gid(hidden=hidden_gid)(f)
59+
60+
return f
61+
62+
return decorator
63+
64+
3865
def opt_asana_task_gid(**kwargs):
3966
return click.option(
4067
"-a",
@@ -47,11 +74,34 @@ def opt_asana_task_gid(**kwargs):
4774

4875

4976
def opt_asana_token_pass_path():
77+
def callback(ctx, param, value):
78+
api_token_pass_path = value
79+
80+
# fetch API token to connect to asana -------------------------------------------------
81+
asana_token = os.environ.get("ASANA_PERSONAL_ACCESS_TOKEN")
82+
83+
if asana_token is None and api_token_pass_path is None:
84+
error_and_exit(
85+
"You must provide an Asana Personal Access asana_token, using the"
86+
f" {'/'.join(param.opts)} option"
87+
)
88+
if asana_token is not None:
89+
logger.debug(
90+
"Reading the Asana Personal Access asana_token (PAT) from environment"
91+
" variable..."
92+
)
93+
else:
94+
asana_token = fetch_from_pass_manager(api_token_pass_path)
95+
96+
return asana_token
97+
5098
return click.option(
5199
"--token",
52100
"--token-pass-path",
53-
"token_pass_path",
101+
"asana_token",
54102
help="Path in the UNIX password manager to fetch",
103+
expose_value=True,
104+
callback=callback,
55105
)
56106

57107

@@ -305,6 +355,24 @@ def opt_google_oauth_port():
305355

306356

307357
# caldav options ------------------------------------------------------------------------------
358+
def opts_caldav():
359+
def decorator(f):
360+
for d in reversed(
361+
[
362+
opt_caldav_calendar,
363+
opt_caldav_url,
364+
opt_caldav_user,
365+
opt_caldav_passwd_pass_path,
366+
opt_caldav_passwd_cmd,
367+
]
368+
):
369+
f = d()(f)
370+
371+
return f
372+
373+
return decorator
374+
375+
308376
def opt_caldav_calendar():
309377
return click.option(
310378
"--caldav-calendar",
@@ -365,7 +433,7 @@ def opt_filename_extension():
365433
"--filename-extension",
366434
"filename_extension",
367435
type=str,
368-
help=f"Use this extension for locally created files",
436+
help="Use this extension for locally created files",
369437
default=".md",
370438
)
371439

syncall/scripts/fs_gkeep_sync.py

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from bubop import check_optional_mutually_exclusive, format_dict, logger, loguru_tqdm_sink
77

88
from syncall import inform_about_app_extras
9-
from syncall.cli import opt_filename_extension, opt_gkeep_ignore_labels
9+
from syncall.cli import opt_filename_extension, opt_gkeep_ignore_labels, opts_miscellaneous
1010

1111
try:
1212
from syncall import GKeepNoteSide
@@ -16,31 +16,25 @@
1616
from syncall import (
1717
Aggregator,
1818
FilesystemSide,
19-
__version__,
2019
cache_or_reuse_cached_combination,
2120
convert_filesystem_file_to_gkeep_note,
2221
convert_gkeep_note_to_filesystem_file,
2322
fetch_app_configuration,
2423
get_resolution_strategy,
25-
inform_about_combination_name_usage,
2624
list_named_combinations,
27-
report_toplevel_exception,
2825
)
2926
from syncall.app_utils import (
3027
app_log_to_syslog,
3128
gkeep_read_username_password_token,
29+
register_teardown_handler,
3230
write_to_pass_manager,
3331
)
3432
from syncall.cli import (
35-
opt_combination,
36-
opt_custom_combination_savename,
3733
opt_filesystem_root,
3834
opt_gkeep_labels,
3935
opt_gkeep_passwd_pass_path,
4036
opt_gkeep_token_pass_path,
4137
opt_gkeep_user_pass_path,
42-
opt_list_combinations,
43-
opt_resolution_strategy,
4438
)
4539

4640

@@ -55,14 +49,10 @@
5549
@opt_filename_extension()
5650
@opt_filesystem_root()
5751
# misc options --------------------------------------------------------------------------------
58-
@opt_list_combinations("Filesystem", "Google Keep")
59-
@opt_resolution_strategy()
60-
@opt_combination("Filesystem", "Google Keep")
61-
@opt_custom_combination_savename("Filesystem", "Google Keep")
62-
@click.option("-v", "--verbose", count=True)
63-
@click.version_option(__version__)
52+
@opts_miscellaneous("Filesystem", "Google Keep")
6453
def main(
6554
filesystem_root: Optional[str],
55+
filename_extension: str,
6656
gkeep_labels: Sequence[str],
6757
gkeep_ignore_labels: Sequence[str],
6858
gkeep_user_pass_path: str,
@@ -73,7 +63,8 @@ def main(
7363
combination_name: str,
7464
custom_combination_savename: str,
7565
do_list_combinations: bool,
76-
filename_extension: str,
66+
list_resolution_strategies: bool,
67+
pdb_on_error: bool,
7768
):
7869
"""
7970
Synchronize Notes from your Google Keep with text files in a directory on your filesystem.
@@ -136,6 +127,7 @@ def main(
136127
gkeep_labels = app_config["gkeep_labels"]
137128
gkeep_ignore_labels = app_config["gkeep_ignore_labels"]
138129
filename_extension = app_config["filename_extension"]
130+
139131
# combination manually specified ----------------------------------------------------------
140132
else:
141133
inform_about_config = True
@@ -188,7 +180,6 @@ def main(
188180
gkeep_token_pass_path,
189181
)
190182

191-
# initialize google keep -----------------------------------------------------------------
192183
gkeep_side = GKeepNoteSide(
193184
gkeep_labels=gkeep_labels,
194185
gkeep_ignore_labels=gkeep_ignore_labels,
@@ -197,12 +188,19 @@ def main(
197188
gkeep_token=gkeep_token,
198189
)
199190

200-
# initialize Filesystem Side --------------------------------------------------------------
201191
filesystem_side = FilesystemSide(
202192
filesystem_root=filesystem_root_path, filename_extension=filename_extension
203193
)
204194

205-
# sync ------------------------------------------------------------------------------------
195+
# teardown function and exception handling ------------------------------------------------
196+
register_teardown_handler(
197+
pdb_on_error=pdb_on_error,
198+
inform_about_config=inform_about_config,
199+
combination_name=combination_name,
200+
verbose=verbose,
201+
)
202+
203+
# take extra arguments into account -------------------------------------------------------
206204
def converter_A_to_B(gkeep_note):
207205
return convert_gkeep_note_to_filesystem_file(
208206
gkeep_note=gkeep_note,
@@ -212,40 +210,31 @@ def converter_A_to_B(gkeep_note):
212210

213211
converter_A_to_B.__doc__ = convert_gkeep_note_to_filesystem_file.__doc__
214212

215-
try:
216-
with Aggregator(
217-
side_A=gkeep_side,
218-
side_B=filesystem_side,
219-
converter_B_to_A=convert_filesystem_file_to_gkeep_note,
220-
converter_A_to_B=converter_A_to_B,
221-
resolution_strategy=get_resolution_strategy(
222-
resolution_strategy,
223-
side_A_type=type(gkeep_side),
224-
side_B_type=type(filesystem_side),
225-
),
226-
config_fname=combination_name,
227-
ignore_keys=(
228-
(),
229-
(),
230-
),
231-
) as aggregator:
232-
aggregator.sync()
233-
except KeyboardInterrupt:
234-
logger.error("Exiting...")
235-
return 1
236-
except:
237-
report_toplevel_exception(is_verbose=verbose >= 1)
238-
return 1
239-
240-
# cache the token
213+
# sync ------------------------------------------------------------------------------------
214+
with Aggregator(
215+
side_A=gkeep_side,
216+
side_B=filesystem_side,
217+
converter_B_to_A=convert_filesystem_file_to_gkeep_note,
218+
converter_A_to_B=converter_A_to_B,
219+
resolution_strategy=get_resolution_strategy(
220+
resolution_strategy,
221+
side_A_type=type(gkeep_side),
222+
side_B_type=type(filesystem_side),
223+
),
224+
config_fname=combination_name,
225+
ignore_keys=(
226+
(),
227+
(),
228+
),
229+
) as aggregator:
230+
aggregator.sync()
231+
232+
# cache the token -------------------------------------------------------------------------
241233
token = gkeep_side.get_master_token()
242234
if token is not None:
243235
logger.debug(f"Caching the gkeep token in pass -> {gkeep_token_pass_path}...")
244236
write_to_pass_manager(password_path=gkeep_token_pass_path, passwd=token)
245237

246-
if inform_about_config:
247-
inform_about_combination_name_usage(combination_name)
248-
249238
return 0
250239

251240

0 commit comments

Comments
 (0)