better-notion 1.4.0__py3-none-any.whl → 1.5.1__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.
@@ -5,6 +5,7 @@ required databases for the workflow management system.
5
5
  """
6
6
 
7
7
  import json
8
+ import logging
8
9
  from pathlib import Path
9
10
  from typing import Any, Dict, Optional
10
11
 
@@ -22,6 +23,8 @@ from better_notion.utils.agents.schemas import (
22
23
  WorkIssueSchema,
23
24
  )
24
25
 
26
+ logger = logging.getLogger(__name__)
27
+
25
28
 
26
29
  class WorkspaceInitializer:
27
30
  """Initialize a new workspace with workflow databases.
@@ -64,38 +67,48 @@ class WorkspaceInitializer:
64
67
  Dict mapping database names to their IDs
65
68
 
66
69
  Raises:
67
- Exception: If database creation fails
70
+ Exception: If database creation fails with detailed error message
68
71
  """
72
+ logger.info(f"Initializing workspace '{workspace_name}' in page {parent_page_id}")
73
+
69
74
  # Get parent page
70
- parent = await Page.get(parent_page_id, client=self._client)
75
+ try:
76
+ parent = await Page.get(parent_page_id, client=self._client)
77
+ logger.info(f"Parent page found: {parent.id}")
78
+ except Exception as e:
79
+ error_msg = f"Failed to get parent page '{parent_page_id}': {str(e)}"
80
+ logger.error(error_msg)
81
+ raise Exception(error_msg) from e
71
82
 
72
83
  # Create databases in order (independent first, then dependent)
73
84
  self._database_ids = {}
74
-
75
- # 1. Organizations (independent)
76
- await self._create_organizations_db(parent)
77
-
78
- # 2. Tags (independent)
79
- await self._create_tags_db(parent)
80
-
81
- # 3. Projects (depends on Organizations)
82
- await self._create_projects_db(parent)
83
-
84
- # 4. Versions (depends on Projects)
85
- await self._create_versions_db(parent)
86
-
87
- # 5. Tasks (depends on Versions)
88
- await self._create_tasks_db(parent)
89
-
90
- # 6. Ideas (depends on Projects)
91
- await self._create_ideas_db(parent)
92
-
93
- # 7. Work Issues (depends on Projects, Tasks, Ideas)
94
- await self._create_work_issues_db(parent)
95
-
96
- # 8. Incidents (depends on Projects, Versions, Tasks)
97
- await self._create_incidents_db(parent)
98
-
85
+ databases_order = [
86
+ ("Organizations", "organizations", self._create_organizations_db),
87
+ ("Tags", "tags", self._create_tags_db),
88
+ ("Projects", "projects", self._create_projects_db),
89
+ ("Versions", "versions", self._create_versions_db),
90
+ ("Tasks", "tasks", self._create_tasks_db),
91
+ ("Ideas", "ideas", self._create_ideas_db),
92
+ ("Work Issues", "work_issues", self._create_work_issues_db),
93
+ ("Incidents", "incidents", self._create_incidents_db),
94
+ ]
95
+
96
+ for display_name, key, create_func in databases_order:
97
+ try:
98
+ logger.info(f"Creating {display_name} database...")
99
+ await create_func(parent)
100
+ logger.info(f"✓ {display_name} database created: {self._database_ids[key]}")
101
+ except Exception as e:
102
+ error_msg = (
103
+ f"Failed to create {display_name} database: {str(e)}\n"
104
+ f"Databases created so far: {list(self._database_ids.keys())}\n"
105
+ f"Parent page: {parent_page_id}\n"
106
+ f"Workspace name: {workspace_name}"
107
+ )
108
+ logger.error(error_msg)
109
+ raise Exception(error_msg) from e
110
+
111
+ logger.info(f"Workspace initialization complete. Created {len(self._database_ids)} databases")
99
112
  return self._database_ids
100
113
 
101
114
  async def _create_organizations_db(self, parent: Page) -> None:
@@ -208,8 +221,8 @@ class WorkspaceInitializer:
208
221
  # Update relations
209
222
  if "projects" in self._database_ids:
210
223
  schema["Project"]["relation"]["database_id"] = self._database_ids["projects"]
211
-
212
- # Related Task will be updated after Tasks DB creation
224
+ if "tasks" in self._database_ids:
225
+ schema["Related Task"]["relation"]["database_id"] = self._database_ids["tasks"]
213
226
  db = await self._client.databases.create(
214
227
  parent=parent,
215
228
  title="Ideas",
@@ -234,6 +247,7 @@ class WorkspaceInitializer:
234
247
  schema["Project"]["relation"]["database_id"] = self._database_ids["projects"]
235
248
  if "tasks" in self._database_ids:
236
249
  schema["Task"]["relation"]["database_id"] = self._database_ids["tasks"]
250
+ schema["Fix Tasks"]["relation"]["database_id"] = self._database_ids["tasks"]
237
251
  if "ideas" in self._database_ids:
238
252
  schema["Related Idea"]["relation"]["database_id"] = self._database_ids["ideas"]
239
253
 
@@ -0,0 +1,132 @@
1
+ """Validation helpers for CLI commands.
2
+
3
+ This module provides reusable validation functions for CLI parameters.
4
+ """
5
+
6
+
7
+ class ValidationError(Exception):
8
+ """Custom exception for validation errors."""
9
+
10
+ def __init__(self, message: str):
11
+ self.message = message
12
+ super().__init__(self.message)
13
+
14
+
15
+ class Validators:
16
+ """Reusable validation functions for CLI parameters."""
17
+
18
+ @staticmethod
19
+ def positive_int(value: int, name: str = "value") -> None:
20
+ """
21
+ Validate that a value is a positive integer.
22
+
23
+ Args:
24
+ value: The value to validate
25
+ name: Parameter name for error messages
26
+
27
+ Raises:
28
+ ValidationError: If value is not positive
29
+ """
30
+ if not isinstance(value, int):
31
+ raise ValidationError(f"{name} must be an integer (got: {type(value).__name__})")
32
+ if value <= 0:
33
+ raise ValidationError(f"{name} must be positive (greater than 0, got: {value})")
34
+
35
+ @staticmethod
36
+ def non_negative_int(value: int, name: str = "value") -> None:
37
+ """
38
+ Validate that a value is a non-negative integer.
39
+
40
+ Args:
41
+ value: The value to validate
42
+ name: Parameter name for error messages
43
+
44
+ Raises:
45
+ ValidationError: If value is negative
46
+ """
47
+ if not isinstance(value, int):
48
+ raise ValidationError(f"{name} must be an integer (got: {type(value).__name__})")
49
+ if value < 0:
50
+ raise ValidationError(f"{name} must be non-negative (0 or greater, got: {value})")
51
+
52
+ @staticmethod
53
+ def non_negative_float(value: float, name: str = "value") -> None:
54
+ """
55
+ Validate that a value is a non-negative float.
56
+
57
+ Args:
58
+ value: The value to validate
59
+ name: Parameter name for error messages
60
+
61
+ Raises:
62
+ ValidationError: If value is negative
63
+ """
64
+ if not isinstance(value, (int, float)):
65
+ raise ValidationError(f"{name} must be a number (got: {type(value).__name__})")
66
+ if value < 0:
67
+ raise ValidationError(f"{name} must be non-negative (0 or greater, got: {value})")
68
+
69
+ @staticmethod
70
+ def enum(value: str, name: str, allowed: list, case_sensitive: bool = False) -> None:
71
+ """
72
+ Validate that a value is in the allowed list.
73
+
74
+ Args:
75
+ value: The value to validate
76
+ name: Parameter name for error messages
77
+ allowed: List of allowed values
78
+ case_sensitive: Whether comparison should be case-sensitive
79
+
80
+ Raises:
81
+ ValidationError: If value is not in allowed list
82
+ """
83
+ if not case_sensitive:
84
+ value_lower = value.lower()
85
+ allowed_lower = [a.lower() for a in allowed]
86
+ if value_lower not in allowed_lower:
87
+ raise ValidationError(
88
+ f"{name} must be one of {allowed} (got: {value})"
89
+ )
90
+ else:
91
+ if value not in allowed:
92
+ raise ValidationError(
93
+ f"{name} must be one of {allowed} (got: {value})"
94
+ )
95
+
96
+ @staticmethod
97
+ def range_int(value: int, name: str, min_val: int, max_val: int) -> None:
98
+ """
99
+ Validate that a value is within a specified range.
100
+
101
+ Args:
102
+ value: The value to validate
103
+ name: Parameter name for error messages
104
+ min_val: Minimum allowed value (inclusive)
105
+ max_val: Maximum allowed value (inclusive)
106
+
107
+ Raises:
108
+ ValidationError: If value is outside the range
109
+ """
110
+ if not isinstance(value, int):
111
+ raise ValidationError(f"{name} must be an integer (got: {type(value).__name__})")
112
+ if value < min_val or value > max_val:
113
+ raise ValidationError(
114
+ f"{name} must be between {min_val} and {max_val} (got: {value})"
115
+ )
116
+
117
+ @staticmethod
118
+ def required(value: Any, name: str) -> None:
119
+ """
120
+ Validate that a value is not None or empty.
121
+
122
+ Args:
123
+ value: The value to validate
124
+ name: Parameter name for error messages
125
+
126
+ Raises:
127
+ ValidationError: If value is None or empty
128
+ """
129
+ if value is None:
130
+ raise ValidationError(f"{name} is required")
131
+ if isinstance(value, str) and not value.strip():
132
+ raise ValidationError(f"{name} cannot be empty")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: better-notion
3
- Version: 1.4.0
3
+ Version: 1.5.1
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
@@ -6,7 +6,7 @@ better_notion/_api/oauth.py,sha256=pDQFtwJzb90zroFSLQleNEwMwVsNwlsO7MIieaegPz4,4
6
6
  better_notion/_api/collections/__init__.py,sha256=snBRDpUOWxKR-w6FjNtfRddclO1LF2LNy65T7dbv57A,539
7
7
  better_notion/_api/collections/blocks.py,sha256=Ka8HbVk9NCDh1zMJeKvP1XCjxJI_1yuGzYyzSxFgYGY,3703
8
8
  better_notion/_api/collections/comments.py,sha256=Qkl2pOxqiti1ALJpBi1TE1arQoSc8Y6cuOkqpsXrzpE,3222
9
- better_notion/_api/collections/databases.py,sha256=vsgTudDOEO_8PwU_o0x_cHptHswenBY5v15avgQM0vY,2123
9
+ better_notion/_api/collections/databases.py,sha256=eUpNSwSWn8J5AkCq1uZEBPb7rNmrrzvNMqFTsfFCcDc,3084
10
10
  better_notion/_api/collections/pages.py,sha256=qX_QwNJsq8jPvWp91A44zfais69gIXNSRjeH7NL8ha0,3174
11
11
  better_notion/_api/collections/users.py,sha256=h-J5fQbXQSj7ZgMfHRDoXLKUv1lP3On6uWO-3YO4dlc,1309
12
12
  better_notion/_api/entities/__init__.py,sha256=QYSgFMr5kC94M6nzpkr_KSjUZcCpdZBObBod7XOrhU4,521
@@ -41,8 +41,8 @@ better_notion/_cli/commands/auth.py,sha256=d1-g8SKCrC8Di4yM36hafVlF9YZQ9pypRYU9I
41
41
  better_notion/_cli/commands/blocks.py,sha256=ErcR-SHhPoz6B9uy0xOG6CGZB-bhvny5lxBYufmYHbw,15711
42
42
  better_notion/_cli/commands/comments.py,sha256=dBdxvpkjk_yA_1WUivXl1Nt8Ig-uYD5-k4XtenHU_cI,4981
43
43
  better_notion/_cli/commands/config.py,sha256=3bOCmcRnpTb5SHB2TJPXdxrRshy84nZRAsULxfbSqk0,5360
44
- better_notion/_cli/commands/databases.py,sha256=05NR4BQBaxXedl1MBemwlXPA1Q7loWc36t56AGAofdQ,8320
45
- better_notion/_cli/commands/pages.py,sha256=Tt99IhQthAUjFVc6KkLmvubfYgW84RwCvcjt4gNXqdA,14280
44
+ better_notion/_cli/commands/databases.py,sha256=SRfUi74Cm7YVvdwPjbcwP1g__DiZsMyY0YLQTY-skx4,12953
45
+ better_notion/_cli/commands/pages.py,sha256=NZwrWDOim43FzIdtqubbXarsflnjQo2T4BGB1TJsco0,13068
46
46
  better_notion/_cli/commands/plugins.py,sha256=mBYSVciwwgnCRXYOuJfSYDV2UMAr_9P8KwPuiC-pY2A,27067
47
47
  better_notion/_cli/commands/search.py,sha256=JM20W5TiohsOKLyEUABEaWr0yB82W2_qAGQ1w64_pGk,4671
48
48
  better_notion/_cli/commands/update.py,sha256=NfmijzTpCbVTEY2h0QdcJZXEiGmWkk-iyEtZCqrgmAk,4997
@@ -65,7 +65,7 @@ better_notion/_sdk/managers/user_manager.py,sha256=AfFWhxKbaWhMCqJmRoU55JwfZNJHh
65
65
  better_notion/_sdk/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
66
  better_notion/_sdk/models/block.py,sha256=4Wpx46zVUy0LrwE2eEvUof37iyflmY-nL-NttC7o8tI,15163
67
67
  better_notion/_sdk/models/comment.py,sha256=R82jfAhQODNCdvVywv8dIrmA9lfKgkI8ZSKThQFvG98,8485
68
- better_notion/_sdk/models/database.py,sha256=BZZkoS7jtAWmJQMucmmL7w9Hm34UaJwHqek030kAoW4,16454
68
+ better_notion/_sdk/models/database.py,sha256=NaplivKjbbxnpYtB19f80yY6075tHZWuvimCPA9j0h4,16746
69
69
  better_notion/_sdk/models/page.py,sha256=KVGTdt-xuhEYlb1snmZ0VdjMZAQDSWcUwZ18oK5me4g,20541
70
70
  better_notion/_sdk/models/user.py,sha256=1yo4F7horPDf7m9Z1Xl1VGxcmgG7vCn_pEFj_oiPyVo,10261
71
71
  better_notion/_sdk/models/blocks/__init__.py,sha256=8kykYs4cvuBlgn6R1tq7b5RMJu7ng7IcWA-0y7kww6A,1928
@@ -107,26 +107,27 @@ better_notion/plugins/base.py,sha256=3h9jOZzS--UqmVW3RREtcQ2h1GTWWPUryTencsJKhTM
107
107
  better_notion/plugins/loader.py,sha256=zCWsMdJyvZs1IHFm0zjEiqm_l_5jB1Uw4x30Kq8rLS4,9527
108
108
  better_notion/plugins/state.py,sha256=jH_tZWvC35hqLO4qwl2Kwq9ziWVavwCEUcCqy3s5wMY,3780
109
109
  better_notion/plugins/official/__init__.py,sha256=rPg5vdk1cEANVstMPzxcWmImtsOpdSR40JSml7h1uUk,426
110
- better_notion/plugins/official/agents.py,sha256=UF9zn7DNBX-3_bmpjrHhbNd4dZXQIGa5ljhCC71SRcA,25939
111
- better_notion/plugins/official/agents_cli.py,sha256=v2yem60ER6EVRt9u1f5xF3MOExQeQ4lGqGCQ6z7pV18,51706
110
+ better_notion/plugins/official/agents.py,sha256=HDCAQOq7za00_YZXLftU5cBaeK3VSgutFWkXwwvtRxs,26636
111
+ better_notion/plugins/official/agents_cli.py,sha256=8l6e1zJCAT4DdAO-QfdjK_vrrrik3pmrojwakE32ZNY,53048
112
112
  better_notion/plugins/official/productivity.py,sha256=_-whP4pYA4HufE1aUFbIdhrjU-O9njI7xUO_Id2M1J8,8726
113
113
  better_notion/plugins/official/agents_sdk/__init__.py,sha256=luQBzZLsJ7fC5U0jFu8dfzMviiXj2SBZXcTohM53wkQ,725
114
- better_notion/plugins/official/agents_sdk/managers.py,sha256=dkpGozJhKXvz8rFqc2tAWyL6MeVzFVcjGMC0Ed2Z1dQ,29813
114
+ better_notion/plugins/official/agents_sdk/managers.py,sha256=0zMZPu63zhdyqiudO2gKsmM3YOJh0nFAR9FrMlwkV2A,31186
115
115
  better_notion/plugins/official/agents_sdk/models.py,sha256=gwGw9t-Z17NxddzmRKWiD9Noje86sxgPNlLJGARI0KA,75448
116
116
  better_notion/plugins/official/agents_sdk/plugin.py,sha256=bs9O8Unv6SARGj4lBU5Gj9HGbLTUNqTacJ3RLUhdbI4,4479
117
117
  better_notion/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
118
118
  better_notion/utils/helpers.py,sha256=HgFuUQlG_HzBOB0z2GA9RxPLoXgwRc0DIxa9Fg6C-Jk,2337
119
119
  better_notion/utils/retry.py,sha256=9WJDNitiIfVTL18hIlipvOKn41ukePrOwtAwx-LevpQ,7479
120
+ better_notion/utils/validators.py,sha256=RyAGcYJexKOq1YepF6insoBu4QEOVPoAMWqi8Mt_5Xk,4583
120
121
  better_notion/utils/agents/__init__.py,sha256=Zu32q0abbimrJY5dczjWNEastE-IrtGPQjpxD4JS4IU,1715
121
122
  better_notion/utils/agents/auth.py,sha256=_SBcqBjXmX8CJMCPpRWM-UuaDg7-OOtMWbhnYEiIBTs,6568
122
123
  better_notion/utils/agents/dependency_resolver.py,sha256=PfHHDIQztGih4LwylMb0_MyhDFbOYPjvUxcxY52mSEs,12033
123
124
  better_notion/utils/agents/project_context.py,sha256=aJlzy5H2rL4sAfW2jHL_3K2VkBJ4ihUhCRVolkpuO78,7477
124
125
  better_notion/utils/agents/rbac.py,sha256=8ZA8Y7wbOiVZDbpjpH7iC35SZrZ0jl4fcJ3xWCm3SsE,11820
125
- better_notion/utils/agents/schemas.py,sha256=e_lpGGO12FXtfqFyI91edj9xc5RUtuA6pU7Sk6ip7xg,21784
126
+ better_notion/utils/agents/schemas.py,sha256=eHfGhY90FAPXA3E8qE6gP75dgNzn-9z5Ju1FMwBKnQQ,22120
126
127
  better_notion/utils/agents/state_machine.py,sha256=xUBEeDTbU1Xq-rsRo2sbr6AUYpYrV9DTHOtZT2cWES8,6699
127
- better_notion/utils/agents/workspace.py,sha256=T8mP_DNIWhI1-k6UydJINsEJ6kQxQ6_Pa44ul58k088,12370
128
- better_notion-1.4.0.dist-info/METADATA,sha256=WXldrn_zOf9rVjRSQJmWLLr_ixIdAOtZxGW2BhWNmZc,11096
129
- better_notion-1.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
130
- better_notion-1.4.0.dist-info/entry_points.txt,sha256=D0bUcP7Z00Zyjxw7r2p29T95UrwioDO0aGDoHe9I6fo,55
131
- better_notion-1.4.0.dist-info/licenses/LICENSE,sha256=BAdN3JpgMY_y_fWqZSCFSvSbC2mTHP-BKDAzF5FXQAI,1069
132
- better_notion-1.4.0.dist-info/RECORD,,
128
+ better_notion/utils/agents/workspace.py,sha256=FYarHj8eD2OeUG0KMPelqpBavm4RnYBoW2PVuwYkKI4,13614
129
+ better_notion-1.5.1.dist-info/METADATA,sha256=lLDjood4wOkoyNlgmc7IUY9doLnTz6u_6tluC4rgQG4,11096
130
+ better_notion-1.5.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
131
+ better_notion-1.5.1.dist-info/entry_points.txt,sha256=D0bUcP7Z00Zyjxw7r2p29T95UrwioDO0aGDoHe9I6fo,55
132
+ better_notion-1.5.1.dist-info/licenses/LICENSE,sha256=BAdN3JpgMY_y_fWqZSCFSvSbC2mTHP-BKDAzF5FXQAI,1069
133
+ better_notion-1.5.1.dist-info/RECORD,,