from inspect import signature
from typing import Callable
from notion.markdown import (
markdown_to_notion,
notion_to_markdown,
plaintext_to_notion,
notion_to_plaintext,
)
from notion.utils import (
add_signed_prefix_as_needed,
remove_signed_prefix_as_needed,
)
[docs]class Mapper(property):
"""
Mapper for converting to/from notion and Python.
"""
[docs] def __init__(
self,
path: str,
python_to_api: Callable,
api_to_python: Callable,
*args,
**kwargs,
):
"""
Create mapper object and fill its fields.
Arguments
---------
path : str
Path can either be a top-level field-name or a
dot-delimited string representing the key names to traverse.
python_to_api : Callable
Function that converts values as given in the Python layer
into the internal API representation.
api_to_python : Callable
Function that converts what is received from the API
into an internal representation to be returned to the Python layer.
"""
self.path = path
self.python_to_api = python_to_api
self.api_to_python = api_to_python
super().__init__(*args, **kwargs)
[docs]def field_map(
path: str,
python_to_api: Callable = lambda x: x,
api_to_python: Callable = lambda x: x,
) -> Mapper:
"""
Return a property that maps a Block attribute
onto a field in the API data structures.
Arguments
---------
path : str
Path can either be a top-level field-name or a
dot-delimited string representing the key names to traverse.
python_to_api : Callable, optional
Function that converts values as given in the Python layer into
the internal API representation.
Defaults to proxy lambda x: x.
api_to_python : Callable, optional
Function that converts what is received from the API into
an internal representation to be returned to the Python layer.
Defaults to proxy lambda x: x.
Returns
-------
Mapper
Property map.
See Also
--------
property_map
"""
def fget(self):
kwargs = {}
if "client" in signature(api_to_python).parameters:
kwargs["client"] = self._client
return api_to_python(self.get(path), **kwargs)
def fset(self, value):
kwargs = {}
if "client" in signature(python_to_api).parameters:
kwargs["client"] = self._client
self.set(path, python_to_api(value, **kwargs))
return Mapper(
path=path,
python_to_api=python_to_api,
api_to_python=api_to_python,
fget=fget,
fset=fset,
)
[docs]def prefixed_field_map(name: str) -> Mapper:
"""
Arguments
---------
name : str
Name of the property.
Returns
-------
Mapper
Field map.
See Also
--------
field_map
"""
return field_map(
name,
api_to_python=add_signed_prefix_as_needed,
python_to_api=remove_signed_prefix_as_needed,
)
[docs]def nested_field_map(name: str) -> Mapper:
"""
Arguments
---------
name : str
Name of the property.
Returns
-------
Mapper
Field map.
See Also
--------
field_map
"""
return field_map(
name,
python_to_api=lambda x: [[x]],
api_to_python=lambda x: x[0][0],
)
[docs]def markdown_field_map(name: str) -> Mapper:
"""
Arguments
---------
name : str
Name of the property.
Returns
-------
Mapper
Field map.
See Also
--------
field_map
"""
return field_map(
name, api_to_python=notion_to_markdown, python_to_api=markdown_to_notion
)
[docs]def property_map(
name: str,
python_to_api: Callable = lambda x: x,
api_to_python: Callable = lambda x: x,
markdown: bool = True,
) -> Mapper:
"""
Similar to `field_map`, except it works specifically with
the data under the "properties" field in the API block table,
and just takes a single name to specify which subkey to reference.
Also, these properties all seem to use a special "embedded list"
format that breaks the text up into a sequence of chunks and associated
format metadata.
Arguments
---------
name : str
Name of the property.
python_to_api : Callable, optional
Function that converts values as given in the Python layer into
the internal API representation.
Defaults to proxy lambda x: x.
api_to_python : Callable, optional
Function that converts what is received from the API into
an internal representation to be returned to the Python layer.
Defaults to proxy lambda x: x.
markdown : bool, optional
Whether or not to convert the representation into commonmark-compatible
markdown text upon reading from API and again when saving.
Defaults to True.
Returns
-------
Mapper
Property map.
See Also
--------
field_map
"""
def py2api(x, client=None):
kwargs = {}
if "client" in signature(python_to_api).parameters:
kwargs["client"] = client
x = python_to_api(x, **kwargs)
if markdown:
x = markdown_to_notion(x)
return x
def api2py(x, client=None):
x = x or [[""]]
if markdown:
x = notion_to_markdown(x)
kwargs = {}
if "client" in signature(api_to_python).parameters:
kwargs["client"] = client
return api_to_python(x, **kwargs)
path = f"properties.{name}"
return field_map(path, python_to_api=py2api, api_to_python=api2py)
[docs]def prefixed_property_map(name: str) -> Mapper:
"""
Arguments
---------
name : str
Name of the property.
Returns
-------
Mapper
Property map.
See Also
--------
property_map
"""
return property_map(
name,
api_to_python=add_signed_prefix_as_needed,
python_to_api=remove_signed_prefix_as_needed,
)
[docs]def plaintext_property_map(name: str) -> Mapper:
"""
Arguments
---------
name : str
Name of the property.
Returns
-------
Mapper
Property map.
See Also
--------
property_map
"""
return property_map(
name,
python_to_api=plaintext_to_notion,
api_to_python=notion_to_plaintext,
markdown=False,
)
[docs]def boolean_property_map(name: str) -> Mapper:
"""
Arguments
---------
name : str
Name of the property.
Returns
-------
Mapper
Property map.
See Also
--------
property_map
"""
return property_map(
name,
python_to_api=lambda x: "Yes" if x else "No",
api_to_python=lambda x: x == "Yes",
)