Source code for notion.converter

from datetime import datetime, date
from random import choice
from typing import Any, Callable
from uuid import uuid1

from notion.block.collection.common import NotionDate
from notion.markdown import markdown_to_notion, notion_to_markdown
from notion.utils import (
    remove_signed_prefix_as_needed,
    add_signed_prefix_as_needed,
    to_list,
    extract_id,
)


[docs]class BaseConverter: """ Base Converter. """ _converters = None @classmethod def _ensure_type(cls, name: str, value, types): types = to_list(types) for t in types: if isinstance(value, t): break else: types = [t.__name__ for t in types] msg = f"Value type passed to prop '{name}' must be one of {types}." raise TypeError(msg) @classmethod def _get_converter_for_type(cls, type_: str) -> Callable: if not cls._converters: cls._converters = [m for m in dir(cls) if m.startswith("convert_")] method_name = f"convert_{type_}" if method_name in cls._converters: return getattr(cls, method_name)
[docs] @classmethod def convert(cls, name: str, value: Any, prop: dict, block) -> (str, Any): """ Convert `value` from attribute `name`. Attributes ---------- name : str Property name. value : Any Value to convert. prop : dict More information about the block property. block : Block instance of the block itself. Returns ------- (str, Any) Tuple containing property path and converted value. """ prop_id = prop["id"] prop_type = prop["type"] callback = cls._get_converter_for_type(prop_type) if not callback: raise ValueError( f"Prop '{name}' with type '{prop_type}'" " does not have a converter method" ) value = callback(name=name, value=value, prop=prop, block=block) return f"properties.{prop_id}", value
[docs]class PythonToNotionConverter(BaseConverter):
[docs] @classmethod def convert_title(cls, name, value, **_): cls._ensure_type(name, value, str) return markdown_to_notion(value)
[docs] @classmethod def convert_text(cls, **kwargs): return cls.convert_title(**kwargs)
[docs] @classmethod def convert_number(cls, name, value, **_): if value is None: return None cls._ensure_type(name, value, [int, float]) return [[str(value)]]
[docs] @classmethod def convert_select(cls, value, prop, block, **_): value = to_list(value) if value == [None]: return value options = prop["options"] = prop.get("options", []) valid_options = [[p["value"].lower() for p in options]] colors = [ "default", "gray", "brown", "orange", "yellow", "green", "blue", "purple", "pink", "red", ] schema_needs_update = False for i, v in enumerate(value): value[i] = v = v.replace(",", "") v_key = v.lower() if v_key not in valid_options: schema_needs_update = True valid_options.append(v_key) options.append( {"id": str(uuid1()), "value": v, "color": choice(colors)} ) value = [[",".join(value)]] if schema_needs_update: schema = block.collection.get("schema") schema[prop["id"]] = prop block.collection.set("schema", schema) return value
[docs] @classmethod def convert_multi_select(cls, **kwargs): return cls.convert_select(**kwargs)
[docs] @classmethod def convert_email(cls, value, **_): return [[value, [["a", value]]]]
[docs] @classmethod def convert_phone_number(cls, **kwargs): return cls.convert_email(**kwargs)
[docs] @classmethod def convert_url(cls, **kwargs): return cls.convert_email(**kwargs)
[docs] @classmethod def convert_date(cls, name, value, **_): cls._ensure_type(name, value, [date, datetime, NotionDate]) if isinstance(value, NotionDate): return value.to_notion() return NotionDate(value)
[docs] @classmethod def convert_checkbox(cls, name, value, **_): cls._ensure_type(name, value, bool) return [["Yes" if value else "No"]]
[docs] @classmethod def convert_person(cls, value, **_): users = [] for user in to_list(value): users += [["‣", [["u", extract_id(user)]]], [","]] return users[:-1]
[docs] @classmethod def convert_file(cls, value, **_): files = [] for url in to_list(value): url = remove_signed_prefix_as_needed(url) name = url.split("/")[-1] files += [[name, [["a", url]]], [","]] return files[:-1]
[docs] @classmethod def convert_relation(cls, value, block, **_): pages = [] for page in to_list(value): if isinstance(page, str): page = block._client.get_block(page) pages += [["‣", [["p", page.id]]], [","]] return pages[:-1]
[docs] @classmethod def convert_created_time(cls, value, **_): return int(value.timestamp() * 1000)
[docs] @classmethod def convert_last_edited_time(cls, **kwargs): return cls.convert_created_time(**kwargs)
[docs] @classmethod def convert_created_by(cls, value, **_): return extract_id(value)
[docs] @classmethod def convert_last_edited_by(cls, **kwargs): return cls.convert_created_by(**kwargs)
[docs]class NotionToPythonConverter(BaseConverter):
[docs] @classmethod def convert_title(cls, value, block, **_): for i, part in enumerate(value): if len(part) == 2: for fmt in part[1]: if "p" in fmt: page = block._client.get_block(fmt[1]) title = f"{page.icon} {page.title}" address = page.get_browseable_url() value[i] = f"[{title}]({address})" return notion_to_markdown(value) if value else ""
[docs] @classmethod def convert_text(cls, **kwargs): return cls.convert_title(**kwargs)
[docs] @classmethod def convert_number(cls, value, **_): if value is None: return None value = value[0][0].replace(",", "") if "." in value: return float(value) return int(value)
[docs] @classmethod def convert_select(cls, value, **_): return value[0][0] if value else None
[docs] @classmethod def convert_multi_select(cls, value, **_): if not value: return [] return [v.strip() for v in value[0][0].split(",")]
[docs] @classmethod def convert_email(cls, **kwargs): return cls.convert_select(**kwargs)
[docs] @classmethod def convert_phone_number(cls, **kwargs): return cls.convert_select(**kwargs)
[docs] @classmethod def convert_url(cls, **kwargs): return cls.convert_select(**kwargs)
[docs] @classmethod def convert_date(cls, value, **_): return NotionDate.from_notion(value)
[docs] @classmethod def convert_checkbox(cls, value, **_): return value[0][0] == "Yes" if value else False
[docs] @classmethod def convert_person(cls, value, block, **_): if not value: return [] items = [i[1][0][1] for i in value if i[0] == "‣"] return [block._client.get_user(i) for i in items]
[docs] @classmethod def convert_file(cls, value, block, **_): if not value: return [] client = block._client items = [i[1][0][1] for i in value if i[0] != ","] return [add_signed_prefix_as_needed(i, client=client) for i in items]
[docs] @classmethod def convert_relation(cls, value, block, **_): if not value: return [] items = [i[1][0][1] for i in value if i[0] != "‣"] return [block._client.get_block(i) for i in items]
[docs] @classmethod def convert_created_time(cls, block, prop, **_): value = block.get(prop["type"]) value = datetime.utcfromtimestamp(value / 1000) return int(value.timestamp() * 1000)
[docs] @classmethod def convert_last_edited_time(cls, **kwargs): return cls.convert_created_time(**kwargs)
[docs] @classmethod def convert_created_by(cls, block, prop, **_): value = block.get(prop["type"] + "_id") return block._client.get_user(value)
[docs] @classmethod def convert_last_edited_by(cls, **kwargs): return cls.convert_created_by(**kwargs)