Skip to content

Commit a7bd8ec

Browse files
committed
Hack together the gtasks-uncheck-all-items script
1 parent a2aff68 commit a7bd8ec

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed

gtasks-uncheck-all-items.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
Login to google account, based on either the cached credentials or using interactive oAuth2
5+
authentication via the browser. Then given the task list name, uncheck all the items in that
6+
list.
7+
"""
8+
9+
import logging
10+
import pickle
11+
from argparse import ArgumentParser, Namespace
12+
from pathlib import Path
13+
from typing import Optional, Sequence, cast
14+
15+
import pkg_resources
16+
from google.auth.transport.requests import Request
17+
from google_auth_oauthlib.flow import InstalledAppFlow
18+
19+
20+
def parse_args() -> Namespace:
21+
parser = ArgumentParser(description=__doc__)
22+
parser.add_argument("task_list", help="Name of the list of tasks to uncheck its items")
23+
parser.add_argument(
24+
"-v", "--verbose", action="store_true", help="Increase output verbosity"
25+
)
26+
return parser.parse_args()
27+
28+
29+
class GoogleTasksUnchecker:
30+
def __init__(
31+
self,
32+
scopes: Sequence[str],
33+
oauth_port: int,
34+
credentials_cache: Path,
35+
client_secret: str,
36+
logger: logging.Logger,
37+
**kargs,
38+
):
39+
super().__init__(**kargs)
40+
41+
self._scopes = scopes
42+
self._oauth_port = oauth_port
43+
self._client_secret = client_secret
44+
self._credentials_cache = credentials_cache
45+
self._logger = logger
46+
47+
# If you modify this, delete your previously saved credentials
48+
self._service = None
49+
50+
def fetch_and_cache_credentials(self):
51+
"""Get valid user credentials from storage.
52+
53+
If nothing has been stored, or if the stored credentials are invalid,
54+
the OAuth2 flow is completed to obtain the new credentials.
55+
56+
:return: Credentials, the obtained credentials.
57+
"""
58+
creds = None
59+
credentials_cache = self._credentials_cache
60+
if credentials_cache.is_file():
61+
with credentials_cache.open("rb") as f:
62+
creds = pickle.load(f) # noqa: S301
63+
64+
if not creds or not creds.valid:
65+
self._logger.debug("Invalid credentials. Fetching again...")
66+
if creds and creds.expired and creds.refresh_token:
67+
creds.refresh(Request())
68+
else:
69+
flow = InstalledAppFlow.from_client_secrets_file(
70+
self._client_secret,
71+
self._scopes,
72+
)
73+
try:
74+
creds = flow.run_local_server(port=self._oauth_port)
75+
except OSError as e:
76+
raise RuntimeError(
77+
f"Port {self._oauth_port} is already in use, please specify a"
78+
" different port or stop the process that's already using it.",
79+
) from e
80+
81+
# Save the credentials for the next run
82+
with credentials_cache.open("wb") as f:
83+
pickle.dump(creds, f)
84+
else:
85+
self._logger.info("Using already cached credentials...")
86+
87+
return creds
88+
89+
def fetch_task_list_id(self, title: str, must_exist: bool = True) -> Optional[str]:
90+
"""Return the id of the task list based on the given Title.
91+
92+
:returns: id or None if that was not found
93+
"""
94+
res = self._service.tasklists().list().execute() # type: ignore
95+
task_lists_list: list[GTasksList] = res["items"] # type: ignore
96+
97+
matching_task_lists = [
98+
task_list["id"] for task_list in task_lists_list if task_list["title"] == title
99+
]
100+
101+
if len(matching_task_lists) == 0:
102+
if must_exist:
103+
raise ValueError(f'Task list with title "{title}" not found')
104+
105+
return None
106+
107+
if len(matching_task_lists) == 1:
108+
return cast(str, matching_task_lists[0])
109+
110+
raise RuntimeError(
111+
f'Multiple matching task lists for title -> "{title}"',
112+
)
113+
114+
def uncheck_all_items(self, list_id: str):
115+
"""Uncheck all the items in the given list."""
116+
self._logger.debug('Unchecking all items in the list "%s"', list_id)
117+
pass
118+
119+
120+
def main():
121+
args = parse_args()
122+
123+
logger = logging.getLogger(__name__)
124+
logging.basicConfig(level=logging.DEBUG if args.is_verbose else logging.INFO)
125+
126+
DEFAULT_CLIENT_SECRET = pkg_resources.resource_filename(
127+
"syncall",
128+
"res/gtasks_client_secret.json",
129+
)
130+
131+
# Initialize the google side instance -----------------------------------------------------
132+
unchecker = GoogleTasksUnchecker(
133+
name="Gtasks",
134+
fullname="Google Tasks List Unchecker",
135+
scopes=["https://www.googleapis.com/auth/tasks"],
136+
credentials_cache=Path.home() / ".gtasks_credentials.pickle",
137+
client_secret=DEFAULT_CLIENT_SECRET,
138+
oauth_port=8081,
139+
logger=logger,
140+
)
141+
142+
# connect with your credentials -----------------------------------------------------------
143+
logger.debug("Connecting to Google Tasks...")
144+
unchecker.fetch_and_cache_credentials()
145+
task_list_id = unchecker.fetch_task_list_id(title=args.task_list, must_exist=True)
146+
assert task_list_id is not None
147+
148+
logger.debug("Connected to Google Tasks.")
149+
unchecker.uncheck_all_items(list_id=task_list_id)
150+
151+
152+
if __name__ == "__main__":
153+
main()

0 commit comments

Comments
 (0)