Skip to content

Commit

Permalink
Implement Formula property (#5)
Browse files Browse the repository at this point in the history
Co-authored-by: Thomas Rausch <[email protected]>
  • Loading branch information
jvaesteves and thrau committed Oct 29, 2024
1 parent db8d521 commit 8e7e84f
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 26 deletions.
2 changes: 1 addition & 1 deletion notion_objects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
from .properties import *
from .values import *

__version__ = "0.6.2"
__version__ = "0.6.3"
48 changes: 23 additions & 25 deletions notion_objects/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"url",
"email",
"phone_number",
"formula", # TODO
"formula",
"relation",
"rollup", # TODO
"created_time",
Expand Down Expand Up @@ -413,30 +413,7 @@ def set(self, field: str, value: DateValue, obj: dict):

@staticmethod
def get_value(field: str, obj: dict) -> DateValue:
# TODO: timezone

start, end = None, None
include_time = False

if container := obj["properties"][field]["date"]:
if date_str := container["start"]:
start = dateutil.parser.parse(date_str)
if len(date_str) > 10:
include_time = True

if container := obj["properties"][field]["date"]:
if date_str := container["end"]:
end = dateutil.parser.parse(date_str)
if len(date_str) > 10:
include_time = True

if not include_time:
if start:
start = start.date()
if end:
end = end.date()

return DateValue(start, end, include_time, None)
return DateValue.from_dict(obj["properties"][field]["date"])

@staticmethod
def set_value(field: str, value: DateValue, obj: dict):
Expand Down Expand Up @@ -653,6 +630,26 @@ def set(self, field: str, value: List[str], obj: dict):
PeopleProperty.set_value(field, value, obj)


class Formula(Property[Union[str, float, bool, DateValue]]):
type = "formula"

def get(self, field: str, obj: dict) -> Union[str, float, bool, DateValue]:
formula = obj["properties"][field][self.type]
formula_type = formula["type"]

if formula_type == "string":
return formula["string"]
elif formula_type == "number":
return formula["number"]
elif formula_type == "boolean":
return formula["boolean"]
elif formula_type == "date":
return DateValue.from_dict(formula["date"])

def set(self, field: str, value: Optional[str], obj: dict):
raise NotImplementedError("cannot update the formula of individual records")


class Properties(Iterable[_P]):
factories: Dict[PropertyType, Type[Property]] = {
"title": TitleText,
Expand All @@ -668,6 +665,7 @@ class Properties(Iterable[_P]):
"checkbox": Checkbox,
"number": Number,
"relation": Relation,
"formula": Formula,
# TODO: ...
}

Expand Down
24 changes: 24 additions & 0 deletions notion_objects/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from datetime import date, datetime, tzinfo
from typing import Optional, Union

import dateutil


@dataclass
class DateValue:
Expand All @@ -14,6 +16,28 @@ class DateValue:
def is_range(self) -> bool:
return self.end is not None

@classmethod
def from_dict(cls, date_obj: dict) -> "DateValue":
# TODO: timezone

start, end = None, None
include_time = False

if date_obj:
if date_str := date_obj.get("start"):
start = dateutil.parser.parse(date_str)
if len(date_str) > 10:
include_time = True
start = start.date()

if date_str := date_obj.get("end"):
end = dateutil.parser.parse(date_str)
if len(date_str) > 10:
include_time = True
end = end.date()

return cls(start, end, include_time, None)


@dataclass
class UserValue:
Expand Down
6 changes: 6 additions & 0 deletions tests/integration/test_basic_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Checkbox,
Database,
Date,
Formula,
MultiSelect,
Number,
Page,
Expand Down Expand Up @@ -46,6 +47,7 @@
},
},
"+1": {"people": {}},
"Rounded Price": {"formula": {"expression": 'round(prop("Price"))'}},
}


Expand All @@ -59,6 +61,7 @@ class FoodRecord(Page):
store_availability = MultiSelect("Store availability")
upvoted = People("+1")
description_plain = Text("Description")
rounded_price = Formula("Rounded Price")


@pytest.fixture()
Expand Down Expand Up @@ -97,6 +100,7 @@ def test_create_and_list_records(food_db):
assert records[0].food_group is None
assert records[0].store_availability == []
assert records[0].Price is None
assert records[0].rounded_price is None
assert records[0].last_ordered is None
assert records[0].description == []
assert records[0].description_plain == ""
Expand All @@ -106,6 +110,7 @@ def test_create_and_list_records(food_db):
assert records[1].food_group == "Fruit"
assert records[1].store_availability == ["Duc Loi Market", "Rainbow Grocery"]
assert records[1].Price == 123.45
assert records[1].rounded_price == 123
assert records[1].last_ordered == now
assert records[1].description_plain == "hot food record"
assert records[1].description[0].to_dict() == {
Expand Down Expand Up @@ -157,4 +162,5 @@ def test_create_to_dict(food_db):
"name": "My New Food Record",
"store_availability": ["Duc Loi Market", "Rainbow Grocery"],
"upvoted": [],
"rounded_price": 123,
}

0 comments on commit 8e7e84f

Please sign in to comment.