better-notion 2.1.1__py3-none-any.whl → 2.1.3__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.
@@ -421,7 +421,11 @@ class TaskSchema:
421
421
 
422
422
  @staticmethod
423
423
  def get_schema() -> Dict[str, Dict[str, Any]]:
424
- """Return Notion database schema for Tasks."""
424
+ """Return Notion database schema for Tasks.
425
+
426
+ Note: "Dependencies" self-referential relation is NOT included here.
427
+ It will be added after the database is created via database.update().
428
+ """
425
429
  return {
426
430
  "Title": PropertyBuilder.title("Title"),
427
431
  "Version": PropertyBuilder.relation("Version"),
@@ -458,8 +462,6 @@ class TaskSchema:
458
462
  SelectOption.option("Low", "blue"),
459
463
  ],
460
464
  ),
461
- # Dependencies: Self-referential relation (will be updated after database creation)
462
- "Dependencies": PropertyBuilder.relation("Dependencies", dual_property=False),
463
465
  # Related Work Issue: Relation to Work Issues (will be updated after database creation)
464
466
  "Related Work Issue": PropertyBuilder.relation("Related Work Issue", dual_property=False),
465
467
  "Estimated Hours": PropertyBuilder.number("Estimated Hours"),
@@ -93,10 +93,17 @@ class WorkspaceInitializer:
93
93
  logger.error(error_msg)
94
94
  raise Exception(error_msg) from e
95
95
 
96
- # Check for existing workspace unless skipped
97
- if not skip_detection:
98
- existing = await WorkspaceMetadata.detect_workspace(parent, self._client)
99
- if existing:
96
+ # Check for existing workspace
97
+ existing = await WorkspaceMetadata.detect_workspace(parent, self._client)
98
+
99
+ if existing:
100
+ if skip_detection:
101
+ # --reset flag: Delete all existing databases first
102
+ logger.warning(f"Reset mode: Deleting {len(existing.get('database_ids', {}))} existing databases...")
103
+ await self._delete_existing_databases(parent, existing.get("database_ids", {}))
104
+ logger.info("All existing databases deleted")
105
+ else:
106
+ # Normal mode: Raise error if workspace exists
100
107
  workspace_info = {
101
108
  "workspace_id": existing.get("workspace_id", "unknown"),
102
109
  "workspace_name": existing.get("workspace_name", workspace_name),
@@ -126,20 +133,38 @@ class WorkspaceInitializer:
126
133
  ("Incidents", "incidents", self._create_incidents_db),
127
134
  ]
128
135
 
129
- for display_name, key, create_func in databases_order:
130
- try:
131
- logger.info(f"Creating {display_name} database...")
132
- await create_func(parent)
133
- logger.info(f"✓ {display_name} database created: {self._database_ids[key]}")
134
- except Exception as e:
135
- error_msg = (
136
- f"Failed to create {display_name} database: {str(e)}\n"
137
- f"Databases created so far: {list(self._database_ids.keys())}\n"
138
- f"Parent page: {parent_page_id}\n"
139
- f"Workspace name: {workspace_name}"
140
- )
141
- logger.error(error_msg)
142
- raise Exception(error_msg) from e
136
+ try:
137
+ for display_name, key, create_func in databases_order:
138
+ try:
139
+ logger.info(f"Creating {display_name} database...")
140
+ await create_func(parent)
141
+ logger.info(f"✓ {display_name} database created: {self._database_ids[key]}")
142
+ except Exception as e:
143
+ # Clean up databases created so far
144
+ import traceback
145
+ logger.error(f"Failed to create {display_name} database: {str(e)}")
146
+ logger.error(f"Full traceback:\n{traceback.format_exc()}")
147
+
148
+ # Try to get more error details if available
149
+ error_details = str(e)
150
+ if hasattr(e, 'response') and hasattr(e.response, 'text'):
151
+ logger.error(f"Notion API response: {e.response.text}")
152
+ error_details += f"\nNotion API response: {e.response.text}"
153
+
154
+ logger.warning(f"Cleaning up {len(self._database_ids)} databases that were created...")
155
+ await self._delete_existing_databases(parent, self._database_ids)
156
+
157
+ error_msg = (
158
+ f"Failed to create {display_name} database: {error_details}\n"
159
+ f"All partially created databases have been deleted.\n"
160
+ f"Parent page: {parent_page_id}\n"
161
+ f"Workspace name: {workspace_name}"
162
+ )
163
+ logger.error(error_msg)
164
+ raise Exception(error_msg) from e
165
+ except Exception as e:
166
+ # Re-raise the exception
167
+ raise e
143
168
 
144
169
  # Update cross-database relations that require both databases to exist
145
170
  await self._update_cross_database_relations()
@@ -337,19 +362,67 @@ class WorkspaceInitializer:
337
362
  # This is a no-op but documents the intent
338
363
  pass
339
364
 
365
+ async def _delete_existing_databases(self, parent: Page, database_ids: dict) -> None:
366
+ """Delete all existing databases in the parent page.
367
+
368
+ Args:
369
+ parent: Parent page
370
+ database_ids: Dict of database IDs to delete
371
+ """
372
+ import asyncio
373
+
374
+ deletion_tasks = []
375
+ for db_name, db_id in database_ids.items():
376
+ if db_id:
377
+ logger.info(f"Deleting {db_name} database ({db_id})...")
378
+ deletion_tasks.append(self._delete_database(db_id))
379
+
380
+ # Delete all databases concurrently
381
+ if deletion_tasks:
382
+ await asyncio.gather(*deletion_tasks, return_exceptions=True)
383
+
384
+ async def _delete_database(self, database_id: str) -> None:
385
+ """Delete a single database.
386
+
387
+ Args:
388
+ database_id: ID of the database to delete
389
+ """
390
+ try:
391
+ await self._client._api._request(
392
+ "DELETE",
393
+ f"/blocks/{database_id}"
394
+ )
395
+ logger.info(f"Deleted database {database_id}")
396
+ except Exception as e:
397
+ logger.warning(f"Failed to delete database {database_id}: {str(e)}")
398
+ # Continue with other deletions even if this one fails
399
+
340
400
  async def _update_self_relations(self, database_id: str) -> None:
341
- """Update self-referential relations in Tasks database.
401
+ """Add self-referential Dependencies relation to Tasks database.
342
402
 
343
- Note: The database was created with placeholder relations.
344
- This method updates them to point to themselves.
403
+ The Dependencies property cannot be included in the initial schema
404
+ because it requires the database's own ID. This method adds it
405
+ after the database is created.
406
+
407
+ Args:
408
+ database_id: ID of the Tasks database
345
409
  """
346
410
  # Get the current database schema
347
411
  db = await self._client.databases.get(database_id)
348
-
349
- # Update the Dependencies relation to point to itself
350
412
  schema = db.schema
351
- if "Dependencies" in schema:
352
- schema["Dependencies"]["relation"]["database_id"] = database_id
413
+
414
+ # Add the Dependencies self-referential relation
415
+ # This creates a relation from Tasks to Tasks itself
416
+ schema["Dependencies"] = {
417
+ "relation": {
418
+ "database_id": database_id, # Self-reference
419
+ "type": "dual_property",
420
+ "dual_property": {
421
+ "synced_property_name": "Dependent Tasks",
422
+ "synced_property_type": "relation"
423
+ }
424
+ }
425
+ }
353
426
 
354
427
  # Update the database schema via API
355
428
  await self._client._api._request(
@@ -358,7 +431,7 @@ class WorkspaceInitializer:
358
431
  json={"properties": schema}
359
432
  )
360
433
 
361
- logger.info(f"Updated self-referential relations for Tasks database {database_id}")
434
+ logger.info(f"Added Dependencies self-referential relation to Tasks database {database_id}")
362
435
 
363
436
  async def _update_cross_database_relations(self) -> None:
364
437
  """Update cross-database relations after all databases are created.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: better-notion
3
- Version: 2.1.1
3
+ Version: 2.1.3
4
4
  Summary: A high-level Python SDK for the Notion API with developer experience in mind.
5
5
  Project-URL: Homepage, https://github.com/nesalia-inc/better-notion
6
6
  Project-URL: Documentation, https://github.com/nesalia-inc/better-notion#readme
@@ -130,11 +130,11 @@ better_notion/utils/agents/dependency_resolver.py,sha256=PfHHDIQztGih4LwylMb0_My
130
130
  better_notion/utils/agents/metadata.py,sha256=thQSXfYx9mWgmBud8HtlNTLr5SwH6E_O1AjSNSnMFoo,6614
131
131
  better_notion/utils/agents/project_context.py,sha256=aJlzy5H2rL4sAfW2jHL_3K2VkBJ4ihUhCRVolkpuO78,7477
132
132
  better_notion/utils/agents/rbac.py,sha256=8ZA8Y7wbOiVZDbpjpH7iC35SZrZ0jl4fcJ3xWCm3SsE,11820
133
- better_notion/utils/agents/schemas.py,sha256=EpAEevoth-DGWepGMubRFjdvHfe7zrQxi9i4CHDvem8,22946
133
+ better_notion/utils/agents/schemas.py,sha256=pO4MiSaWzSOCZu6J3uZPH7mtMVpE2H3iDHzd6WH0wXc,22926
134
134
  better_notion/utils/agents/state_machine.py,sha256=xUBEeDTbU1Xq-rsRo2sbr6AUYpYrV9DTHOtZT2cWES8,6699
135
- better_notion/utils/agents/workspace.py,sha256=-EzPLFPjaD9PCeLkSdu1ddRa9-RlHgFvLJ2qxFou7X8,19713
136
- better_notion-2.1.1.dist-info/METADATA,sha256=5hImQwg_VfumZ1dMkCtz1QEWUtcYr8ywTvlPlREvnKE,11096
137
- better_notion-2.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
138
- better_notion-2.1.1.dist-info/entry_points.txt,sha256=D0bUcP7Z00Zyjxw7r2p29T95UrwioDO0aGDoHe9I6fo,55
139
- better_notion-2.1.1.dist-info/licenses/LICENSE,sha256=BAdN3JpgMY_y_fWqZSCFSvSbC2mTHP-BKDAzF5FXQAI,1069
140
- better_notion-2.1.1.dist-info/RECORD,,
135
+ better_notion/utils/agents/workspace.py,sha256=W9MKf4b2fvoGCKHp8EuZROnr1tfmR3lS5GrmCuKrZQQ,22841
136
+ better_notion-2.1.3.dist-info/METADATA,sha256=jQ5fHLFQ9DN3gfYKaiJuUMPIWjDhrC8VeIAQl_KxDMQ,11096
137
+ better_notion-2.1.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
138
+ better_notion-2.1.3.dist-info/entry_points.txt,sha256=D0bUcP7Z00Zyjxw7r2p29T95UrwioDO0aGDoHe9I6fo,55
139
+ better_notion-2.1.3.dist-info/licenses/LICENSE,sha256=BAdN3JpgMY_y_fWqZSCFSvSbC2mTHP-BKDAzF5FXQAI,1069
140
+ better_notion-2.1.3.dist-info/RECORD,,