notionary 0.2.13__py3-none-any.whl → 0.2.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. notionary/__init__.py +3 -16
  2. notionary/{notion_client.py → base_notion_client.py} +92 -98
  3. notionary/blocks/__init__.py +61 -0
  4. notionary/{elements → blocks}/audio_element.py +6 -3
  5. notionary/{elements → blocks}/bookmark_element.py +3 -5
  6. notionary/{elements → blocks}/bulleted_list_element.py +5 -6
  7. notionary/{elements → blocks}/callout_element.py +4 -6
  8. notionary/{elements → blocks}/code_block_element.py +4 -5
  9. notionary/{elements → blocks}/column_element.py +3 -5
  10. notionary/{elements → blocks}/divider_element.py +3 -5
  11. notionary/{elements → blocks}/embed_element.py +4 -5
  12. notionary/{elements → blocks}/heading_element.py +4 -7
  13. notionary/{elements → blocks}/image_element.py +4 -5
  14. notionary/{elements → blocks}/mention_element.py +3 -6
  15. notionary/blocks/notion_block_client.py +26 -0
  16. notionary/{elements → blocks}/notion_block_element.py +2 -3
  17. notionary/{elements → blocks}/numbered_list_element.py +4 -6
  18. notionary/{elements → blocks}/paragraph_element.py +4 -6
  19. notionary/{prompting/element_prompt_content.py → blocks/prompts/element_prompt_builder.py} +1 -40
  20. notionary/blocks/prompts/element_prompt_content.py +41 -0
  21. notionary/{elements → blocks}/qoute_element.py +4 -5
  22. notionary/{elements → blocks}/registry/block_registry.py +4 -26
  23. notionary/{elements → blocks}/registry/block_registry_builder.py +26 -25
  24. notionary/{elements → blocks}/table_element.py +5 -6
  25. notionary/{elements → blocks}/text_inline_formatter.py +1 -4
  26. notionary/{elements → blocks}/todo_element.py +5 -6
  27. notionary/{elements → blocks}/toggle_element.py +3 -5
  28. notionary/{elements → blocks}/toggleable_heading_element.py +4 -6
  29. notionary/{elements → blocks}/video_element.py +4 -5
  30. notionary/cli/main.py +157 -128
  31. notionary/cli/onboarding.py +10 -9
  32. notionary/database/__init__.py +0 -0
  33. notionary/database/client.py +132 -0
  34. notionary/database/database_exceptions.py +13 -0
  35. notionary/database/factory.py +0 -0
  36. notionary/database/filter_builder.py +175 -0
  37. notionary/database/notion_database.py +339 -126
  38. notionary/database/notion_database_provider.py +230 -0
  39. notionary/elements/__init__.py +0 -0
  40. notionary/models/notion_database_response.py +294 -13
  41. notionary/models/notion_page_response.py +9 -31
  42. notionary/models/search_response.py +0 -0
  43. notionary/page/__init__.py +0 -0
  44. notionary/page/client.py +110 -0
  45. notionary/page/content/page_content_retriever.py +5 -20
  46. notionary/page/content/page_content_writer.py +3 -4
  47. notionary/page/formatting/markdown_to_notion_converter.py +1 -3
  48. notionary/{prompting → page}/markdown_syntax_prompt_generator.py +1 -2
  49. notionary/page/notion_page.py +354 -317
  50. notionary/page/notion_to_markdown_converter.py +1 -4
  51. notionary/page/properites/property_value_extractor.py +0 -64
  52. notionary/page/{properites/property_formatter.py → property_formatter.py} +7 -4
  53. notionary/page/search_filter_builder.py +131 -0
  54. notionary/page/utils.py +60 -0
  55. notionary/util/__init__.py +12 -3
  56. notionary/util/factory_decorator.py +33 -0
  57. notionary/util/fuzzy_matcher.py +82 -0
  58. notionary/util/page_id_utils.py +0 -21
  59. notionary/util/singleton_metaclass.py +22 -0
  60. notionary/workspace.py +69 -0
  61. {notionary-0.2.13.dist-info → notionary-0.2.14.dist-info}/METADATA +4 -1
  62. notionary-0.2.14.dist-info/RECORD +72 -0
  63. notionary/database/database_discovery.py +0 -142
  64. notionary/database/notion_database_factory.py +0 -190
  65. notionary/exceptions/database_exceptions.py +0 -76
  66. notionary/exceptions/page_creation_exception.py +0 -9
  67. notionary/page/metadata/metadata_editor.py +0 -150
  68. notionary/page/metadata/notion_icon_manager.py +0 -77
  69. notionary/page/metadata/notion_page_cover_manager.py +0 -56
  70. notionary/page/notion_page_factory.py +0 -328
  71. notionary/page/properites/database_property_service.py +0 -302
  72. notionary/page/properites/page_property_manager.py +0 -152
  73. notionary/page/relations/notion_page_relation_manager.py +0 -350
  74. notionary/page/relations/notion_page_title_resolver.py +0 -104
  75. notionary/page/relations/page_database_relation.py +0 -68
  76. notionary/util/warn_direct_constructor_usage.py +0 -54
  77. notionary-0.2.13.dist-info/RECORD +0 -67
  78. /notionary/util/{singleton.py → singleton_decorator.py} +0 -0
  79. {notionary-0.2.13.dist-info → notionary-0.2.14.dist-info}/WHEEL +0 -0
  80. {notionary-0.2.13.dist-info → notionary-0.2.14.dist-info}/entry_points.txt +0 -0
  81. {notionary-0.2.13.dist-info → notionary-0.2.14.dist-info}/licenses/LICENSE +0 -0
  82. {notionary-0.2.13.dist-info → notionary-0.2.14.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,230 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Dict, Optional, TYPE_CHECKING
4
+ from notionary.database.client import NotionDatabaseClient
5
+ from notionary.database.database_exceptions import DatabaseNotFoundException
6
+ from notionary.models.notion_database_response import NotionDatabaseResponse
7
+ from notionary.util import LoggingMixin, FuzzyMatcher, format_uuid
8
+ from notionary.util.singleton_metaclass import SingletonMetaClass
9
+
10
+ if TYPE_CHECKING:
11
+ from notionary import NotionDatabase
12
+
13
+
14
+ class NotionDatabaseProvider(LoggingMixin, metaclass=SingletonMetaClass):
15
+ """
16
+ Provider class for creating and caching Notion database instances.
17
+
18
+ Prevents duplicate database creation when working with multiple pages from the same database.
19
+ Each Notion page references its parent database to determine selectable properties and options.
20
+ By caching database instances, this provider avoids excessive network requests when reading options,
21
+ significantly improving performance for repeated property lookups across many pages.
22
+ """
23
+
24
+ def __init__(self):
25
+ self._database_cache: Dict[str, NotionDatabase] = {}
26
+
27
+ async def get_database_by_id(
28
+ self, database_id: str, token: Optional[str] = None, force_refresh: bool = False
29
+ ) -> NotionDatabase:
30
+ """Get a NotionDatabase by ID with caching."""
31
+ cache_key = self._create_id_cache_key(database_id)
32
+
33
+ if self._should_use_cache(cache_key, force_refresh):
34
+ self.logger.debug(f"Using cached database for ID: {database_id}")
35
+ return self._database_cache[cache_key]
36
+
37
+ database = await self._create_from_database_id(database_id, token)
38
+ self._cache_database(database, token)
39
+ return database
40
+
41
+ async def get_database_by_name(
42
+ self,
43
+ database_name: str,
44
+ token: Optional[str] = None,
45
+ min_similarity: float = 0.6,
46
+ force_refresh: bool = False,
47
+ ) -> NotionDatabase:
48
+ """Get a NotionDatabase by name with caching."""
49
+ name_cache_key = self._create_name_cache_key(database_name, token)
50
+
51
+ if self._should_use_cache(name_cache_key, force_refresh):
52
+ return self._database_cache[name_cache_key]
53
+
54
+ database = await self._create_from_database_name(
55
+ database_name, token, min_similarity
56
+ )
57
+
58
+ id_cache_key = self._create_id_cache_key(database.database_id)
59
+ if not force_refresh and id_cache_key in self._database_cache:
60
+ self.logger.debug(
61
+ f"Found existing cached database by ID: {database.database_id}"
62
+ )
63
+ existing_database = self._database_cache[id_cache_key]
64
+
65
+ self._database_cache[name_cache_key] = existing_database
66
+ return existing_database
67
+
68
+ self._cache_database(database, token, database_name)
69
+ self.logger.debug(
70
+ f"Cached database: {database.title} (ID: {database.database_id})"
71
+ )
72
+
73
+ return database
74
+
75
+ def invalidate_database_cache(self, database_id: str) -> bool:
76
+ """
77
+ Simply invalidate (remove) cache entries for a database without reloading.
78
+
79
+ Args:
80
+ database_id: The database ID to invalidate
81
+
82
+ Returns:
83
+ True if cache entries were found and removed, False otherwise
84
+ """
85
+
86
+ id_cache_key = self._create_id_cache_key(database_id)
87
+ was_cached = id_cache_key in self._database_cache
88
+
89
+ if not was_cached:
90
+ self.logger.debug(f"No cache entry found for database ID: {database_id}")
91
+ return False
92
+
93
+ removed_database = self._database_cache.pop(id_cache_key)
94
+ self.logger.debug(f"Invalidated cached database: {removed_database.title}")
95
+
96
+ name_keys_to_remove = [
97
+ cache_key
98
+ for cache_key, cached_db in self._database_cache.items()
99
+ if (cache_key.startswith("name:") and cached_db.database_id == database_id)
100
+ ]
101
+
102
+ for name_key in name_keys_to_remove:
103
+ self._database_cache.pop(name_key)
104
+ self.logger.debug(f"Invalidated name-based cache: {name_key}")
105
+
106
+ return was_cached
107
+
108
+ async def _create_from_database_id(
109
+ self, database_id: str, token: Optional[str]
110
+ ) -> NotionDatabase:
111
+ """Create a NotionDatabase from database ID via API."""
112
+ formatted_id = format_uuid(database_id) or database_id
113
+
114
+ async with NotionDatabaseClient(token=token) as client:
115
+ db_response = await client.get_database(formatted_id)
116
+ return self._create_from_response(db_response, token)
117
+
118
+ async def _create_from_database_name(
119
+ self,
120
+ database_name: str,
121
+ token: Optional[str] = None,
122
+ min_similarity: float = 0.6,
123
+ ) -> NotionDatabase:
124
+ """Create a NotionDatabase by finding it via name with fuzzy matching."""
125
+ async with NotionDatabaseClient(token=token) as client:
126
+ search_result = await client.search_databases(database_name, limit=10)
127
+
128
+ if not search_result.results:
129
+ self.logger.warning("No databases found for name: %s", database_name)
130
+ raise DatabaseNotFoundException(database_name)
131
+
132
+ best_match = FuzzyMatcher.find_best_match(
133
+ query=database_name,
134
+ items=search_result.results,
135
+ text_extractor=lambda db: self._extract_title(db),
136
+ min_similarity=min_similarity,
137
+ )
138
+
139
+ if not best_match:
140
+ available_titles = [
141
+ self._extract_title(db) for db in search_result.results[:5]
142
+ ]
143
+ self.logger.warning(
144
+ "No sufficiently similar database found for '%s' (min: %.3f). Available: %s",
145
+ database_name,
146
+ min_similarity,
147
+ available_titles,
148
+ )
149
+ raise DatabaseNotFoundException(database_name)
150
+
151
+ database_id = best_match.item.id
152
+ db_response = await client.get_database(database_id=database_id)
153
+ instance = self._create_from_response(db_response, token)
154
+
155
+ self.logger.info(
156
+ "Created database: '%s' (ID: %s, similarity: %.3f)",
157
+ instance.title,
158
+ database_id,
159
+ best_match.similarity,
160
+ )
161
+
162
+ return instance
163
+
164
+ def _should_use_cache(self, cache_key: str, force_refresh: bool) -> bool:
165
+ """Returns True if the cache should be used for the given cache_key."""
166
+ return not force_refresh and cache_key in self._database_cache
167
+
168
+ def _cache_database(
169
+ self,
170
+ database: NotionDatabase,
171
+ token: Optional[str],
172
+ original_name: Optional[str] = None,
173
+ ) -> None:
174
+ """Cache a database by both ID and name (if provided)."""
175
+ # Always cache by ID
176
+ id_cache_key = self._create_id_cache_key(database.database_id)
177
+ self._database_cache[id_cache_key] = database
178
+
179
+ if original_name:
180
+ name_cache_key = self._create_name_cache_key(original_name, token)
181
+ self._database_cache[name_cache_key] = database
182
+
183
+ def _create_id_cache_key(self, database_id: str) -> str:
184
+ """Create cache key for database ID."""
185
+ return f"id:{database_id}"
186
+
187
+ def _create_name_cache_key(self, database_name: str, token: Optional[str]) -> str:
188
+ """Create cache key for database name."""
189
+ token_suffix = f":{hash(token)}" if token else ":default"
190
+ return f"name:{database_name.lower().strip()}{token_suffix}"
191
+
192
+ def _create_from_response(
193
+ self, db_response: NotionDatabaseResponse, token: Optional[str]
194
+ ) -> NotionDatabase:
195
+ """Create NotionDatabase instance from API response."""
196
+ from notionary import NotionDatabase
197
+
198
+ title = self._extract_title(db_response)
199
+ emoji_icon = self._extract_emoji_icon(db_response)
200
+
201
+ instance = NotionDatabase(
202
+ database_id=db_response.id,
203
+ title=title,
204
+ url=db_response.url,
205
+ emoji_icon=emoji_icon,
206
+ properties=db_response.properties,
207
+ token=token,
208
+ )
209
+
210
+ self.logger.info(
211
+ "Created database manager: '%s' (ID: %s)", title, db_response.id
212
+ )
213
+
214
+ return instance
215
+
216
+ def _extract_title(self, db_response: NotionDatabaseResponse) -> str:
217
+ """Extract title from database response."""
218
+ if db_response.title and len(db_response.title) > 0:
219
+ return db_response.title[0].plain_text
220
+ return "Untitled Database"
221
+
222
+ def _extract_emoji_icon(self, db_response: NotionDatabaseResponse) -> Optional[str]:
223
+ """Extract emoji from database response."""
224
+ if not db_response.icon:
225
+ return None
226
+
227
+ if db_response.icon.type == "emoji":
228
+ return db_response.icon.emoji
229
+
230
+ return None
File without changes
@@ -1,10 +1,11 @@
1
1
  from pydantic import BaseModel
2
2
  from dataclasses import dataclass
3
- from typing import Optional, List, Dict, Any, Literal
3
+ from typing import Optional, List, Dict, Any, Literal, Union
4
4
 
5
- from notionary.models.notion_page_response import Icon
5
+ from notionary.models.notion_page_response import Cover, Icon
6
6
 
7
7
 
8
+ # Reusing existing types from your codebase
8
9
  @dataclass
9
10
  class TextContent:
10
11
  content: str
@@ -27,11 +28,214 @@ class User:
27
28
 
28
29
  @dataclass
29
30
  class Parent:
30
- type: Literal["page_id", "workspace", "block_id"]
31
+ type: Literal["page_id", "workspace", "block_id", "database_id"]
31
32
  page_id: Optional[str] = None
32
- block_id: Optional[str] = None # Added block_id field
33
+ block_id: Optional[str] = None
34
+ database_id: Optional[str] = None
33
35
 
34
36
 
37
+ # Rich text types for Pydantic models
38
+ class TextContentPydantic(BaseModel):
39
+ content: str
40
+ link: Optional[Dict[str, str]] = None
41
+
42
+
43
+ class Annotations(BaseModel):
44
+ bold: bool
45
+ italic: bool
46
+ strikethrough: bool
47
+ underline: bool
48
+ code: bool
49
+ color: str
50
+
51
+
52
+ class RichTextItemPydantic(BaseModel):
53
+ type: str # 'text', 'mention', 'equation'
54
+ text: Optional[TextContentPydantic] = None
55
+ annotations: Annotations
56
+ plain_text: str
57
+ href: Optional[str] = None
58
+
59
+
60
+ # Database property schema types (these are schema definitions, not values)
61
+ class StatusOption(BaseModel):
62
+ id: str
63
+ name: str
64
+ color: str
65
+ description: Optional[str] = None
66
+
67
+
68
+ class StatusGroup(BaseModel):
69
+ id: str
70
+ name: str
71
+ color: str
72
+ option_ids: List[str]
73
+
74
+
75
+ class StatusPropertySchema(BaseModel):
76
+ options: List[StatusOption]
77
+ groups: List[StatusGroup]
78
+
79
+
80
+ class DatabaseStatusProperty(BaseModel):
81
+ id: str
82
+ name: str
83
+ type: Literal["status"]
84
+ status: StatusPropertySchema
85
+
86
+
87
+ class RelationPropertySchema(BaseModel):
88
+ database_id: str
89
+ type: str # "single_property"
90
+ single_property: Dict[str, Any]
91
+
92
+
93
+ class DatabaseRelationProperty(BaseModel):
94
+ id: str
95
+ name: str
96
+ type: Literal["relation"]
97
+ relation: RelationPropertySchema
98
+
99
+
100
+ class DatabaseUrlProperty(BaseModel):
101
+ id: str
102
+ name: str
103
+ type: Literal["url"]
104
+ url: Dict[str, Any] # Usually empty dict
105
+
106
+
107
+ class DatabaseRichTextProperty(BaseModel):
108
+ id: str
109
+ name: str
110
+ type: Literal["rich_text"]
111
+ rich_text: Dict[str, Any] # Usually empty dict
112
+
113
+
114
+ class MultiSelectOption(BaseModel):
115
+ id: str
116
+ name: str
117
+ color: str
118
+ description: Optional[str] = None
119
+
120
+
121
+ class MultiSelectPropertySchema(BaseModel):
122
+ options: List[MultiSelectOption]
123
+
124
+
125
+ class DatabaseMultiSelectProperty(BaseModel):
126
+ id: str
127
+ name: str
128
+ type: Literal["multi_select"]
129
+ multi_select: MultiSelectPropertySchema
130
+
131
+
132
+ class DatabaseTitleProperty(BaseModel):
133
+ id: str
134
+ name: str
135
+ type: Literal["title"]
136
+ title: Dict[str, Any] # Usually empty dict
137
+
138
+
139
+ # Generic database property for unknown types
140
+ class GenericDatabaseProperty(BaseModel):
141
+ id: str
142
+ name: str
143
+ type: str
144
+
145
+ class Config:
146
+ extra = "allow"
147
+
148
+
149
+ # Union of all database property types
150
+ DatabaseProperty = Union[
151
+ DatabaseStatusProperty,
152
+ DatabaseRelationProperty,
153
+ DatabaseUrlProperty,
154
+ DatabaseRichTextProperty,
155
+ DatabaseMultiSelectProperty,
156
+ DatabaseTitleProperty,
157
+ GenericDatabaseProperty,
158
+ ]
159
+
160
+
161
+ # Page property value types (these are actual values, not schemas)
162
+ class StatusValue(BaseModel):
163
+ id: str
164
+ name: str
165
+ color: str
166
+
167
+
168
+ class StatusProperty(BaseModel):
169
+ id: str
170
+ type: str # 'status'
171
+ status: Optional[StatusValue] = None
172
+
173
+
174
+ class RelationItem(BaseModel):
175
+ id: str
176
+
177
+
178
+ class RelationProperty(BaseModel):
179
+ id: str
180
+ type: str # 'relation'
181
+ relation: List[RelationItem]
182
+ has_more: bool
183
+
184
+
185
+ class UrlProperty(BaseModel):
186
+ id: str
187
+ type: str # 'url'
188
+ url: Optional[str] = None
189
+
190
+
191
+ class RichTextProperty(BaseModel):
192
+ id: str
193
+ type: str # 'rich_text'
194
+ rich_text: List[RichTextItemPydantic]
195
+
196
+
197
+ class MultiSelectItem(BaseModel):
198
+ id: str
199
+ name: str
200
+ color: str
201
+
202
+
203
+ class MultiSelectProperty(BaseModel):
204
+ id: str
205
+ type: str # 'multi_select'
206
+ multi_select: List[MultiSelectItem]
207
+
208
+
209
+ class TitleProperty(BaseModel):
210
+ id: str
211
+ type: str # 'title'
212
+ title: List[RichTextItemPydantic]
213
+
214
+
215
+ # Cover types
216
+ class ExternalCover(BaseModel):
217
+ url: str
218
+
219
+
220
+ class NotionCover(BaseModel):
221
+ type: str # 'external', 'file'
222
+ external: Optional[ExternalCover] = None
223
+
224
+
225
+ # Parent types for Pydantic
226
+ class NotionParent(BaseModel):
227
+ type: str # 'database_id', 'page_id', 'workspace'
228
+ database_id: Optional[str] = None
229
+ page_id: Optional[str] = None
230
+
231
+
232
+ # User type for Pydantic
233
+ class NotionUser(BaseModel):
234
+ object: str # 'user'
235
+ id: str
236
+
237
+
238
+ # Database object
35
239
  class NotionDatabaseResponse(BaseModel):
36
240
  """
37
241
  Represents the response from the Notion API when retrieving a database.
@@ -39,19 +243,96 @@ class NotionDatabaseResponse(BaseModel):
39
243
 
40
244
  object: Literal["database"]
41
245
  id: str
42
- cover: Optional[Any]
43
- icon: Optional[Icon]
246
+ cover: Optional[Any] = None
247
+ icon: Optional[Icon] = None
248
+ cover: Optional[Cover]
44
249
  created_time: str
45
250
  last_edited_time: str
46
- created_by: User
47
- last_edited_by: User
48
- title: List[RichText]
251
+ created_by: NotionUser
252
+ last_edited_by: NotionUser
253
+ title: List[RichTextItemPydantic]
49
254
  description: List[Any]
50
255
  is_inline: bool
51
- properties: Dict[str, Any]
52
- parent: Parent
256
+ properties: Dict[
257
+ str, Any
258
+ ] # Using Any for flexibility with different property schemas
259
+ parent: NotionParent
53
260
  url: str
54
- public_url: Optional[str]
261
+ public_url: Optional[str] = None
55
262
  archived: bool
56
263
  in_trash: bool
57
- request_id: Optional[str] = None
264
+
265
+
266
+ class NotionPageResponse(BaseModel):
267
+ object: Literal["page"]
268
+ id: str
269
+ created_time: str
270
+ last_edited_time: str
271
+ created_by: NotionUser
272
+ last_edited_by: NotionUser
273
+ cover: Optional[NotionCover] = None
274
+ icon: Optional[Icon] = None
275
+ parent: NotionParent
276
+ archived: bool
277
+ in_trash: bool
278
+ properties: Dict[str, Any]
279
+ url: str
280
+ public_url: Optional[str] = None
281
+
282
+
283
+ class NotionQueryResponse(BaseModel):
284
+ """
285
+ Complete Notion search/query response model that can contain both pages and databases.
286
+ """
287
+
288
+ object: Literal["list"]
289
+ results: List[Union[NotionPageResponse, NotionDatabaseResponse]]
290
+ next_cursor: Optional[str] = None
291
+ has_more: bool
292
+ type: Literal["page_or_database"]
293
+ page_or_database: Dict[str, Any]
294
+ request_id: str
295
+
296
+
297
+ # Specific response type for database queries (pages only)
298
+ class NotionQueryDatabaseResponse(BaseModel):
299
+ """
300
+ Notion database query response model for querying pages within a database.
301
+ """
302
+
303
+ object: Literal["list"]
304
+ results: List[NotionPageResponse]
305
+ next_cursor: Optional[str] = None
306
+ has_more: bool
307
+ type: Literal["page_or_database"]
308
+ page_or_database: Dict[str, Any]
309
+ request_id: str
310
+
311
+
312
+ # Specific response type for search results (can be mixed)
313
+ class NotionSearchResponse(BaseModel):
314
+ """
315
+ Notion search response model that can return both pages and databases.
316
+ """
317
+
318
+ object: Literal["list"]
319
+ results: List[Union[NotionPageResponse, NotionDatabaseResponse]]
320
+ next_cursor: Optional[str] = None
321
+ has_more: bool
322
+ type: Literal["page_or_database"]
323
+ page_or_database: Dict[str, Any]
324
+ request_id: str
325
+
326
+
327
+ class NotionDatabaseSearchResponse(BaseModel):
328
+ """
329
+ Notion search response model for database-only searches.
330
+ """
331
+
332
+ object: Literal["list"]
333
+ results: List[NotionDatabaseResponse]
334
+ next_cursor: Optional[str] = None
335
+ has_more: bool
336
+ type: Literal["page_or_database"]
337
+ page_or_database: Dict[str, Any]
338
+ request_id: str
@@ -1,73 +1,52 @@
1
- from dataclasses import dataclass
2
1
  from typing import Literal, Optional, Dict, Any, Union
3
2
 
4
3
  from pydantic import BaseModel
5
4
 
6
5
 
7
- @dataclass
8
- class User:
6
+ class User(BaseModel):
9
7
  """Represents a Notion user object."""
10
8
 
11
9
  object: str
12
10
  id: str
13
11
 
14
12
 
15
- @dataclass
16
- class ExternalFile:
13
+ class ExternalFile(BaseModel):
17
14
  """Represents an external file, e.g., for cover images."""
18
15
 
19
16
  url: str
20
17
 
21
18
 
22
- @dataclass
23
- class Cover:
19
+ class Cover(BaseModel):
24
20
  """Cover image for a Notion page."""
25
21
 
26
22
  type: str
27
23
  external: ExternalFile
28
24
 
29
25
 
30
- @dataclass
31
- class EmojiIcon:
26
+ class EmojiIcon(BaseModel):
32
27
  type: Literal["emoji"]
33
28
  emoji: str
34
29
 
35
30
 
36
- @dataclass
37
- class ExternalIcon:
31
+ class ExternalIcon(BaseModel):
38
32
  type: Literal["external"]
39
33
  external: ExternalFile
40
34
 
41
35
 
42
- @dataclass
43
- class FileObject:
44
- url: str
45
- expiry_time: str
46
-
47
-
48
- @dataclass
49
- class FileIcon:
50
- type: Literal["file"]
51
- file: FileObject
52
-
53
-
54
- Icon = Union[EmojiIcon, ExternalIcon, FileIcon]
36
+ Icon = Union[EmojiIcon, ExternalIcon]
55
37
 
56
38
 
57
- @dataclass
58
- class DatabaseParent:
39
+ class DatabaseParent(BaseModel):
59
40
  type: Literal["database_id"]
60
41
  database_id: str
61
42
 
62
43
 
63
- @dataclass
64
- class PageParent:
44
+ class PageParent(BaseModel):
65
45
  type: Literal["page_id"]
66
46
  page_id: str
67
47
 
68
48
 
69
- @dataclass
70
- class WorkspaceParent:
49
+ class WorkspaceParent(BaseModel):
71
50
  type: Literal["workspace"]
72
51
  workspace: bool = True
73
52
 
@@ -75,7 +54,6 @@ class WorkspaceParent:
75
54
  Parent = Union[DatabaseParent, PageParent, WorkspaceParent]
76
55
 
77
56
 
78
- @dataclass
79
57
  class NotionPageResponse(BaseModel):
80
58
  """
81
59
  Represents a full Notion page object as returned by the Notion API.
File without changes
File without changes