Skip to content

Commit df9e78b

Browse files
authored
Upload df with children (#32)
* better organization of blocks.py * Allow upload with children
1 parent 58ad1ce commit df9e78b

File tree

2 files changed

+74
-41
lines changed

2 files changed

+74
-41
lines changed

src/notion_df/agent.py

+30-10
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212
from notion_df.values import PageProperties, PageProperty
1313
from notion_df.configs import DatabaseSchema, NON_EDITABLE_TYPES
14-
from notion_df.utils import is_uuid
15-
from notion_df.blocks import parse_blocks
14+
from notion_df.utils import is_uuid, flatten_dict
15+
from notion_df.blocks import parse_blocks, BaseNotionBlock
1616

1717
API_KEY = None
1818
NOT_REVERSE_DATAFRAME = -1
@@ -235,20 +235,36 @@ def create_database(
235235
return response
236236

237237

238-
def upload_row_to_database(row, database_id, schema, client) -> Dict:
238+
def upload_row_to_database(row, database_id, schema, children, client) -> Dict:
239239

240240
properties = PageProperty.from_series(row, schema).query_dict()
241-
response = client.pages.create(
242-
parent={"database_id": database_id}, properties=properties
243-
)
241+
if children:
242+
if not isinstance(children, list):
243+
children = [children]
244+
for cid in range(len(children)):
245+
if isinstance(children[cid], BaseNotionBlock):
246+
children[cid] = flatten_dict(children[cid].dict())
247+
248+
response = client.pages.create(
249+
parent={"database_id": database_id}, properties=properties, children=children
250+
)
251+
else:
252+
response = client.pages.create(
253+
parent={"database_id": database_id}, properties=properties,
254+
)
244255
return response
245256

246257

247-
def upload_to_database(df, databse_id, schema, client, errors) -> List[Dict]:
258+
def upload_to_database(df, databse_id, schema, client, errors, children) -> List[Dict]:
248259
all_response = []
249-
for _, row in df[::NOT_REVERSE_DATAFRAME].iterrows():
260+
if children is not None:
261+
assert len(children) == len(df)
262+
children = children[::NOT_REVERSE_DATAFRAME]
263+
264+
for idx, (_, row) in enumerate(df[::NOT_REVERSE_DATAFRAME].iterrows(), ):
250265
try:
251-
response = upload_row_to_database(row, databse_id, schema, client)
266+
child = children[idx] if children is not None else None
267+
response = upload_row_to_database(row, databse_id, schema, child, client)
252268
all_response.append(response)
253269
except Exception as e:
254270
if errors == "strict":
@@ -277,6 +293,7 @@ def upload(
277293
errors: str = "strict",
278294
resolve_relation_values: bool = False,
279295
create_new_rows_in_relation_target: bool = False,
296+
children: List[Union[Dict, BaseNotionBlock]] = None,
280297
return_response: bool = False,
281298
*,
282299
api_key: str = None,
@@ -317,6 +334,9 @@ def upload(
317334
subsequent rows.
318335
3. "warn": print the error message and continue uploading
319336
Defaults to "strict".
337+
children (List[Union[Dict, BaseNotionBlock]], optional):
338+
The corresponding children of the uploaded Notion page. It should be
339+
a list of the same length as the dataframe.
320340
resolve_relation_values (bool, optional):
321341
If `True`, notion-df assumes the items in any relation columns
322342
are not notion object ids, but the value of the corresponding
@@ -432,7 +452,7 @@ def upload(
432452
lambda row: [obj_string_to_id[ele] for ele in row if ele in obj_string_to_id]
433453
)
434454

435-
response = upload_to_database(df, databse_id, schema, client, errors)
455+
response = upload_to_database(df, databse_id, schema, client, errors, children)
436456

437457
print(f"Your dataframe has been uploaded to the Notion page: {notion_url} .")
438458
if return_response:

src/notion_df/blocks.py

+44-31
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,9 @@ class ParentObject(BaseModel):
2626
block_id: Optional[str]
2727

2828

29-
class BaseNotionBlock(BaseModel):
30-
object: str = "block"
31-
parent: ParentObject
32-
id: Optional[str]
33-
type: Optional[str]
34-
created_time: str
35-
# created_by
36-
last_edited_time: str
37-
# created_by
38-
has_children: bool
39-
archived: bool
40-
type: str
41-
42-
@property
43-
def children(self):
44-
return self.__getattribute__(self.type).children
45-
46-
def set_children(self, value: Any):
47-
self.__getattribute__(self.type).children = value
29+
# BaseClasses
30+
class BaseAttributes(BaseModel):
31+
pass
4832

4933

5034
class BaseAttributeWithChildren(BaseModel):
@@ -75,60 +59,89 @@ class ToDoBlockAttributes(BaseAttributeWithChildren):
7559
checked: Optional[bool]
7660

7761

78-
class CodeBlockAttributes(BaseModel):
62+
class CodeBlockAttributes(BaseAttributes):
7963
rich_text: List[RichTextObject]
8064
caption: Optional[List[RichTextObject]]
8165
language: Optional[str] # TODO: it's actually an enum
8266

8367

84-
class ChildPageAttributes(BaseModel):
68+
class ChildPageAttributes(BaseAttributes):
8569
title: List[RichTextObject]
8670

8771

88-
class EmbedBlockAttributes(BaseModel):
72+
class EmbedBlockAttributes(BaseAttributes):
8973
url: str
9074

9175

92-
class ImageBlockAttributes(FileObject):
76+
class ImageBlockAttributes(BaseAttributes, FileObject):
9377
caption: Optional[List[RichTextObject]]
9478
# This is not listed in the docs, but it is in the API response (Nov 2022)
9579

96-
class VideoBlockAttributes(BaseModel):
80+
81+
class VideoBlockAttributes(BaseAttributes):
9782
video: FileObject
9883

9984

100-
class FileBlockAttributes(BaseModel):
85+
class FileBlockAttributes(BaseAttributes):
10186
file: FileObject
10287
caption: Optional[List[RichTextObject]]
10388

10489

105-
class PdfBlockAttributes(BaseModel):
90+
class PdfBlockAttributes(BaseAttributes):
10691
pdf: FileObject
10792

10893

109-
class BookmarkBlockAttributes(BaseModel):
94+
class BookmarkBlockAttributes(BaseAttributes):
11095
url: str
11196
caption: Optional[List[RichTextObject]]
11297

11398

114-
class EquationBlockAttributes(BaseModel):
99+
class EquationBlockAttributes(BaseAttributes):
115100
expression: str
116101

117102

118-
class TableOfContentsAttributes(BaseModel):
103+
class TableOfContentsAttributes(BaseAttributes):
119104
color: Optional[NotionExtendedColorEnum]
120105

121106

122-
class LinkPreviewAttributes(BaseModel):
107+
class LinkPreviewAttributes(BaseAttributes):
123108
url: str
124109

125110

126-
class LinkToPageAttributes(BaseModel):
111+
class LinkToPageAttributes(BaseAttributes):
127112
type: str
128113
page_id: Optional[str]
129114
database_id: Optional[str]
130115

131116

117+
ATTRIBUTES_MAPPING = {
118+
_cls.__name__: _cls
119+
for _cls in BaseAttributes.__subclasses__()
120+
+ BaseAttributeWithChildren.__subclasses__()
121+
}
122+
123+
124+
class BaseNotionBlock(BaseModel):
125+
object: str = "block"
126+
parent: Optional[ParentObject]
127+
id: Optional[str]
128+
type: Optional[str]
129+
created_time: Optional[str]
130+
# created_by
131+
last_edited_time: Optional[str]
132+
# created_by
133+
has_children: Optional[bool]
134+
archived: Optional[bool]
135+
type: str
136+
137+
@property
138+
def children(self):
139+
return self.__getattribute__(self.type).children
140+
141+
def set_children(self, value: Any):
142+
self.__getattribute__(self.type).children = value
143+
144+
132145
class ParagraphBlock(BaseNotionBlock):
133146
type: str = "paragraph"
134147
paragraph: TextBlockAttributes

0 commit comments

Comments
 (0)