Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reverse-Sync from G to N #10

Merged
merged 6 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import os
from notion_client import Client
from pprint import pprint
from dotenv import load_dotenv
from typing import List, Dict
import os.path
import logging

from sync.notion_to_google_task_sync import (
authenticate_and_print,
get_all_pages,
get_all_blocks,
get_todo,
create_notion_tasklist,
insert_notion_tasks_in_google_tasks,
add_id_mapping_to_redis,
update_google_tasks,
remove_deleted_tasks_ids_from_redis,
)

from sync.google_to_notion_task_sync import (
update_notion_tasks,
remove_deleted_google_tasks,
insert_google_task_into_notion,
)

if __name__ == "__main__":

load_dotenv()
NOTION_ID = os.getenv("NOTION_KEY")
if NOTION_ID is None:
raise KeyError("Missing NOTION ID environment variable")

service = authenticate_and_print()

# Create Client
client = Client(auth=NOTION_ID)

# Get all page ids
page_ids = get_all_pages(client)

# Get every block from each page id
total_blocks = []

for page_id in page_ids:
total_blocks.extend(get_all_blocks(client, page_id))

# Get all Notion todos
total_notion_tasks = get_todo(client, total_blocks)


for page_id in page_ids:
all_blocks = []

all_blocks.extend(get_all_blocks(client, page_id))
notion_tasks = get_todo(client, all_blocks)

title = client.blocks.retrieve(page_id)["child_page"]["title"]
TASK_LIST_ID = create_notion_tasklist(service, title)

# Insert tasks from Notion to Google
insert_notion_tasks_in_google_tasks(service, notion_tasks, TASK_LIST_ID)
add_id_mapping_to_redis(service, notion_tasks, TASK_LIST_ID)
remove_deleted_tasks_ids_from_redis(service, total_notion_tasks, TASK_LIST_ID)
update_google_tasks(service, notion_tasks, TASK_LIST_ID)

96 changes: 91 additions & 5 deletions sync/google_to_notion_task_sync.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,96 @@
"""Script that handles the sync from Google Tasks to Notion"""
from notion_client import Client
from typing import List
from sync.notion_to_google_task_sync import r_reverse, r


# Extra features:
# (???) If a task is added to GC, it should be added to Notion as well (???) Where?
# If a task is ticked on Google Calendar, it should be ticket on Notion as well, etc.
# If a task is changed (text edited) in GC it should be changed in Notion as well
# If a task is deleted on GC, if should be deleted in Notion as well
def get_pages_data(client: Client) -> List[str]:
"""Get all pages that have enabled the integration"""
# import ipdb;ipdb.set_trace()
pages_data = {}
for page in client.search()["results"]:

title = page["properties"]["title"]["title"][0]["plain_text"]
pages_data[title] = page["id"]

return pages_data


def insert_google_task_into_notion(service, client, notion_page_id, task_list_id):
"""Insert google tasks into notion"""

current_google_tasks = [
{"title": task["title"], "id": task["id"], "status": task["status"]}
for task in service.tasks().list(tasklist=task_list_id).execute()["items"]
]

for google_task in current_google_tasks[::-1]:
if r_reverse.get(google_task["id"]) is None:

if google_task["status"] == "needsAction":
checked = False
else:
checked = True

notion_task = client.blocks.children.append(
notion_page_id,
children=[
{
"to_do": {
"rich_text": [{"text": {"content": google_task["title"]}}],
"checked": checked,
}
}
],
)

# this should be done in another function. Use it for demo for now
r.set(notion_task["results"][0]["id"], google_task["id"])
r_reverse.set(google_task["id"], notion_task["results"][0]["id"])


def remove_deleted_google_tasks(service, client, task_list_id):
"""Delete NT that has been removed from GT"""

current_google_task_ids = []

current_google_task_ids = [
task["id"]
for task in service.tasks()
.list(tasklist=task_list_id, showHidden=True)
.execute()["items"]
]

for google_task_id in r_reverse.keys():
if google_task_id not in current_google_task_ids:

notion_id_in_db = r_reverse.get(google_task_id)
client.blocks.delete(notion_id_in_db)
r.delete(notion_id_in_db)
r_reverse.delete(google_task_id)


def update_notion_tasks(service, client, task_list_id):
"""Function that Updates tasks. Closes tasks marked as completed from Notion to Google Takss"""

current_google_tasks = [
{"title": task["title"], "id": task["id"], "status": task["status"]}
for task in service.tasks()
.list(tasklist=task_list_id, showHidden=True)
.execute()["items"]
]

for google_task in current_google_tasks:

if r_reverse.get(google_task["id"]) is not None:
block = client.blocks.retrieve(r_reverse.get(google_task["id"]))

if google_task["status"] == "needsAction":
block["to_do"]["checked"] = False
else:
block["to_do"]["checked"] = True

block["to_do"]["rich_text"][0]["text"]["content"] = google_task["title"]
block["to_do"]["rich_text"][0]["plain_text"] = google_task["title"]

client.blocks.update(r_reverse.get(google_task["id"]), **block)
70 changes: 13 additions & 57 deletions sync/notion_to_google_task_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
from googleapiclient.errors import HttpError
import redis

logging.basicConfig(level=logging.DEBUG)
# logging.basicConfig(level=logging.DEBUG)

# -----------------
# SETUP

SCOPES = ["https://www.googleapis.com/auth/tasks"]

r = redis.Redis(host="localhost", port=6379, decode_responses=True)
r = redis.Redis(host="localhost", port=6379, decode_responses=True, db=0)

r_reverse = redis.Redis(host="localhost", port=6379, decode_responses=True, db=1)

# -----------------
# NOTION FUNCTIONS
Expand Down Expand Up @@ -118,11 +120,6 @@ def insert_notion_tasks_in_google_tasks(service, notion_tasks, task_list_id):
def update_google_tasks(service, notion_tasks, task_list_id):
"""Function that Updates tasks. Closes tasks marked as completed from Notion to Google Takss"""

current_google_tasks = [
{"title": task["title"], "id": task["id"], "status": task["status"]}
for task in service.tasks().list(tasklist=task_list_id).execute()["items"]
]

for notion_task in notion_tasks:

if r.get(notion_task["id"]) is not None:
Expand All @@ -133,16 +130,14 @@ def update_google_tasks(service, notion_tasks, task_list_id):
).execute()


def create_notion_tasklist(service) -> str:
def create_notion_tasklist(service, title) -> str:
"""Create a dedicated TaskList in Google Tasks if it does not exist"""

for task_list in service.tasklists().list().execute()["items"]:
if task_list["title"] == "Tasks from Notion":
if task_list["title"] == title:
return task_list["id"]

new_task_list = (
service.tasklists().insert(body={"title": "Tasks from Notion"}).execute()
)
new_task_list = service.tasklists().insert(body={"title": title}).execute()
return new_task_list["id"]


Expand Down Expand Up @@ -170,6 +165,10 @@ def add_id_mapping_to_redis(service, notion_tasks, task_list_id):
and notion_task["status"] == google_task["status"]
):
r.set(notion_task["id"], google_task["id"])
r_reverse.set(
google_task["id"], notion_task["id"]
) # store reverse mapping in db1

logging.info(
f"Successfully added k: {notion_task['id']} v: {google_task['id']}"
)
Expand All @@ -187,49 +186,6 @@ def remove_deleted_tasks_ids_from_redis(service, notion_tasks, task_list_id):
service.tasks().delete(
tasklist=task_list_id, task=r.get(notion_id_in_db)
).execute()
google_task_id = r.get(notion_id_in_db)
r.delete(notion_id_in_db)


if __name__ == "__main__":

load_dotenv()
NOTION_ID = os.getenv("NOTION_KEY") #NOTION_KEY
if NOTION_ID is None:
raise KeyError("Missing NOTION ID environment variable")

service = authenticate_and_print()

# Create Client
client = Client(auth=NOTION_ID)

# Get all page ids
page_ids = get_all_pages(client)

# Get every block from each page id
all_blocks = []
for page_id in page_ids:
all_blocks.extend(get_all_blocks(client, page_id))

# Get all Notion todos
notion_tasks = get_todo(client, all_blocks)

TASK_LIST_ID = create_notion_tasklist(service)

# Insert tasks from Notion to Google
insert_notion_tasks_in_google_tasks(service, notion_tasks, TASK_LIST_ID)

# TODO replace .keys() with something more efficient later
# If redis is empty, or new todo has been added, update the database
if not r.keys() or len(r.keys()) < len(notion_tasks): # add a
logging.info("Adding new data to Redis")
add_id_mapping_to_redis(service, notion_tasks, TASK_LIST_ID)

# If redis has more keys than current notion_tasks, delete the Google task and that key
if len(r.keys()) > len(notion_tasks):
logging.info("Deleting tasks")
remove_deleted_tasks_ids_from_redis(service, notion_tasks, TASK_LIST_ID)

# Update the state of tasks, whenever needed (checked, changed name, etc.)
update_google_tasks(service, notion_tasks, TASK_LIST_ID)


r_reverse.delete(google_task_id)