diff --git a/src/cloud_util/notion.py b/src/cloud_util/notion.py new file mode 100644 index 0000000..f4767b2 --- /dev/null +++ b/src/cloud_util/notion.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from dataclasses import asdict, dataclass, field + +from notion_client import AsyncClient as Client + + +@dataclass +class Text: + content: str + + +@dataclass +class RichText: + type: str + href: str | None = None + + +@dataclass +class RichTextText(RichText): + type: str = "text" + text: Text = field(default_factory=lambda: Text(content="")) + + +class NotionAsync: + def __init__(self, token: str) -> None: + self._client = Client(auth=token) + + def update_token(self, token: str) -> None: + self._client.aclose() + self._client = Client(auth=token) + + async def block_is_table(self, block_id: str) -> bool: + block = await self._client.blocks.retrieve(block_id=block_id) + return block["type"] == "table" + + async def get_table_width(self, table_id: str) -> int: + table = await self._client.blocks.retrieve(block_id=table_id) + return table["table"]["table_width"] + + async def append_table_row_text(self, table_id: str, text_list: list[str]) -> None: + cells: list[RichText] = [] + for content in text_list: + cells.append([asdict(RichTextText(text=Text(content)))]) # noqa: PERF401 + await self.append_table_row(table_id=table_id, cells=cells) + + async def append_table_row(self, table_id: str, cells: list[RichText]) -> None: + if not await self.block_is_table(table_id): + return + table_width = await self.get_table_width(table_id=table_id) + if table_width != len(cells): + return + await self._client.blocks.children.append( + block_id=table_id, + children=[ + { + "object": "block", + "type": "table_row", + "table_row": { + "cells": cells, + }, + }, + ], + ) diff --git a/src/main.py b/src/main.py index 34d893b..0b6dca2 100644 --- a/src/main.py +++ b/src/main.py @@ -4,6 +4,7 @@ from fastapi import FastAPI from pydantic import BaseModel from src.cloud_util.mqtt import MQTT +from src.cloud_util.notion import NotionAsync from src.cloud_util.ticktick import TickTick from src.config import Config from src.recorder.poo import PooRecorder @@ -11,8 +12,9 @@ from src.recorder.poo import PooRecorder Config.init() ticktick = TickTick() +notion = NotionAsync(token=Config.get_env(key="NOTION_TOKEN")) mqtt = MQTT() -poo_recorder = PooRecorder(mqtt) +poo_recorder = PooRecorder(mqtt=mqtt, notion=notion) @asynccontextmanager @@ -29,12 +31,14 @@ class PooRecordField(BaseModel): app = FastAPI(lifespan=_lifespan) +# Poo recorder @app.post("/poo/record") async def record(record_detail: PooRecordField) -> PooRecordField: await poo_recorder.record(record_detail.status) return record_detail +# ticktick @app.get("/ticktick/auth/code") async def ticktick_auth(code: str, state: str) -> dict: if ticktick.retrieve_access_token(code, state): diff --git a/src/recorder/notion_handle.py b/src/recorder/notion_handle.py deleted file mode 100644 index e5e8de1..0000000 --- a/src/recorder/notion_handle.py +++ /dev/null @@ -1,32 +0,0 @@ -from datetime import datetime - -from notion_client import AsyncClient as Client - -from src.config import Config - - -class NotionClient: - def __init__(self) -> None: - self._notion = Client(auth=Config.get_env("NOTION_TOKEN")) - self._page_id = "3cf594afd0754497ba0a93b94912b897" - self._table_id = "9828b56c53de46c794673fe1d01ad522" - - async def note(self, now: datetime, status: str) -> None: - formatted_date = now.strftime("%Y-%m-%d") - formatted_time = now.strftime("%H:%M") - await self._notion.blocks.children.append( - block_id=self._table_id, - children=[ - { - "object": "block", - "type": "table_row", - "table_row": { - "cells": [ - [{"type": "text", "text": {"content": formatted_date}}], - [{"type": "text", "text": {"content": formatted_time}}], - [{"type": "text", "text": {"content": status}}], - ], - }, - }, - ], - ) diff --git a/src/recorder/poo.py b/src/recorder/poo.py index 586a8ee..b34450e 100644 --- a/src/recorder/poo.py +++ b/src/recorder/poo.py @@ -1,8 +1,8 @@ from datetime import datetime from src.cloud_util.mqtt import MQTT +from src.cloud_util.notion import NotionAsync from src.config import Config -from src.recorder.notion_handle import NotionClient class PooRecorder: @@ -14,18 +14,24 @@ class PooRecorder: ONLINE = "online" OFFLINE = "offline" - def __init__(self, mqtt: MQTT) -> None: + def __init__(self, mqtt: MQTT, notion: NotionAsync) -> None: print("Poo Recorder Initialization...") - self._notion = NotionClient() + self._notion = notion + self._table_id = Config.get_env("POO_RECORD_NOTION_TABLE_ID") self._mqtt = mqtt self._mqtt.publish(PooRecorder.CONFIG_TOPIC, PooRecorder.compose_config(), retain=True) self._mqtt.publish(PooRecorder.AVAILABILITY_TOPIC, PooRecorder.ONLINE, retain=True) + async def _note(self, now: datetime, status: str) -> None: + formatted_date = now.strftime("%Y-%m-%d") + formatted_time = now.strftime("%H:%M") + await self._notion.append_table_row_text(self._table_id, [formatted_date, formatted_time, status]) + async def record(self, status: str) -> None: self._publish_text(status) now = datetime.now(tz=datetime.now().astimezone().tzinfo) self._publish_time(now) - await self._notion.note(now, status) + await self._note(now, status) def _publish_text(self, new_text: str) -> None: self._mqtt.publish(PooRecorder.AVAILABILITY_TOPIC, PooRecorder.ONLINE, retain=True)