better-notion 0.9.9__py3-none-any.whl → 1.0.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.
@@ -0,0 +1,614 @@
1
+ """Database schema builders for the agents workflow system.
2
+
3
+ This module provides schema builders for creating Notion databases with the
4
+ correct structure for the workflow management system.
5
+ """
6
+
7
+ from typing import Any, Dict, List, Optional
8
+
9
+
10
+ class SelectOption:
11
+ """Helper class for building select option properties."""
12
+
13
+ @staticmethod
14
+ def option(name: str, color: str = "default") -> Dict[str, str]:
15
+ """Create a select option.
16
+
17
+ Args:
18
+ name: Option name
19
+ color: Option color (default, gray, brown, orange, yellow, green,
20
+ blue, purple, pink, red)
21
+
22
+ Returns:
23
+ Select option dict
24
+
25
+ Example:
26
+ >>> SelectOption.option("Done", "green")
27
+ {"name": "Done", "color": "green"}
28
+ """
29
+ return {"name": name, "color": color}
30
+
31
+ @staticmethod
32
+ def options(*names: str) -> List[Dict[str, str]]:
33
+ """Create multiple select options with default colors.
34
+
35
+ Args:
36
+ *names: Option names
37
+
38
+ Returns:
39
+ List of select option dicts
40
+
41
+ Example:
42
+ >>> SelectOption.options("To Do", "In Progress", "Done")
43
+ [
44
+ {"name": "To Do", "color": "default"},
45
+ {"name": "In Progress", "color": "default"},
46
+ {"name": "Done", "color": "default"}
47
+ ]
48
+ """
49
+ return [{"name": name, "color": "default"} for name in names]
50
+
51
+
52
+ class PropertyBuilder:
53
+ """Helper class for building Notion database properties."""
54
+
55
+ @staticmethod
56
+ def title(name: str = "Name") -> Dict[str, str]:
57
+ """Create a title property.
58
+
59
+ Args:
60
+ name: Property name (default: "Name")
61
+
62
+ Returns:
63
+ Title property schema
64
+ """
65
+ return {"type": "title", "name": name}
66
+
67
+ @staticmethod
68
+ def text(name: str) -> Dict[str, str]:
69
+ """Create a text property.
70
+
71
+ Args:
72
+ name: Property name
73
+
74
+ Returns:
75
+ Text property schema
76
+ """
77
+ return {"type": "rich_text", "name": name}
78
+
79
+ @staticmethod
80
+ def number(name: str, format: Optional[str] = None) -> Dict[str, Any]:
81
+ """Create a number property.
82
+
83
+ Args:
84
+ name: Property name
85
+ format: Number format (number, percent, dollar, euro, pound, yen, ruble)
86
+
87
+ Returns:
88
+ Number property schema
89
+ """
90
+ prop: Dict[str, Any] = {"type": "number", "name": name}
91
+
92
+ if format:
93
+ prop["number"] = {"format": format}
94
+
95
+ return prop
96
+
97
+ @staticmethod
98
+ def select(name: str, options: List[Dict[str, str]]) -> Dict[str, Any]:
99
+ """Create a select property.
100
+
101
+ Args:
102
+ name: Property name
103
+ options: List of option dicts from SelectOption.option()
104
+
105
+ Returns:
106
+ Select property schema
107
+ """
108
+ return {"type": "select", "name": name, "select": {"options": options}}
109
+
110
+ @staticmethod
111
+ def multi_select(name: str, options: List[Dict[str, str]]) -> Dict[str, Any]:
112
+ """Create a multi-select property.
113
+
114
+ Args:
115
+ name: Property name
116
+ options: List of option dicts
117
+
118
+ Returns:
119
+ Multi-select property schema
120
+ """
121
+ return {"type": "multi_select", "name": name, "multi_select": {"options": options}}
122
+
123
+ @staticmethod
124
+ def date(name: str) -> Dict[str, str]:
125
+ """Create a date property.
126
+
127
+ Args:
128
+ name: Property name
129
+
130
+ Returns:
131
+ Date property schema
132
+ """
133
+ return {"type": "date", "name": name}
134
+
135
+ @staticmethod
136
+ def checkbox(name: str) -> Dict[str, str]:
137
+ """Create a checkbox property.
138
+
139
+ Args:
140
+ name: Property name
141
+
142
+ Returns:
143
+ Checkbox property schema
144
+ """
145
+ return {"type": "checkbox", "name": name}
146
+
147
+ @staticmethod
148
+ def url(name: str) -> Dict[str, str]:
149
+ """Create a URL property.
150
+
151
+ Args:
152
+ name: Property name
153
+
154
+ Returns:
155
+ URL property schema
156
+ """
157
+ return {"type": "url", "name": name}
158
+
159
+ @staticmethod
160
+ def email(name: str) -> Dict[str, str]:
161
+ """Create an email property.
162
+
163
+ Args:
164
+ name: Property name
165
+
166
+ Returns:
167
+ Email property schema
168
+ """
169
+ return {"type": "email", "name": name}
170
+
171
+ @staticmethod
172
+ def phone(name: str) -> Dict[str, str]:
173
+ """Create a phone property.
174
+
175
+ Args:
176
+ name: Property name
177
+
178
+ Returns:
179
+ Phone property schema
180
+ """
181
+ return {"type": "phone", "name": name}
182
+
183
+ @staticmethod
184
+ def people(name: str) -> Dict[str, str]:
185
+ """Create a people property.
186
+
187
+ Args:
188
+ name: Property name
189
+
190
+ Returns:
191
+ People property schema
192
+ """
193
+ return {"type": "people", "name": name}
194
+
195
+ @staticmethod
196
+ def files(name: str) -> Dict[str, str]:
197
+ """Create a files property.
198
+
199
+ Args:
200
+ name: Property name
201
+
202
+ Returns:
203
+ Files property schema
204
+ """
205
+ return {"type": "files", "name": name}
206
+
207
+ @staticmethod
208
+ def relation(
209
+ name: str,
210
+ database_id: Optional[str] = None,
211
+ dual_property: bool = True,
212
+ ) -> Dict[str, Any]:
213
+ """Create a relation property.
214
+
215
+ Args:
216
+ name: Property name
217
+ database_id: Related database ID (can be set later)
218
+ dual_property: Whether to create dual_property relation (bidirectional)
219
+
220
+ Returns:
221
+ Relation property schema
222
+
223
+ Note:
224
+ If database_id is None, it must be set later when the related
225
+ database is created.
226
+ """
227
+ prop: Dict[str, Any] = {"type": "relation", "name": name, "relation": {}}
228
+
229
+ if database_id:
230
+ prop["relation"]["database_id"] = database_id
231
+
232
+ if dual_property:
233
+ prop["relation"]["type"] = "dual_property"
234
+
235
+ return prop
236
+
237
+ @staticmethod
238
+ def formula(name: str, expression: str) -> Dict[str, Any]:
239
+ """Create a formula property.
240
+
241
+ Args:
242
+ name: Property name
243
+ expression: Formula expression
244
+
245
+ Returns:
246
+ Formula property schema
247
+ """
248
+ return {"type": "formula", "name": name, "formula": {"expression": expression}}
249
+
250
+ @staticmethod
251
+ def created_time(name: str = "Created time") -> Dict[str, str]:
252
+ """Create a created_time property.
253
+
254
+ Args:
255
+ name: Property name (default: "Created time")
256
+
257
+ Returns:
258
+ Created time property schema
259
+ """
260
+ return {"type": "created_time", "name": name}
261
+
262
+ @staticmethod
263
+ def created_by(name: str = "Created by") -> Dict[str, str]:
264
+ """Create a created_by property.
265
+
266
+ Args:
267
+ name: Property name (default: "Created by")
268
+
269
+ Returns:
270
+ Created by property schema
271
+ """
272
+ return {"type": "created_by", "name": name}
273
+
274
+
275
+ class OrganizationSchema:
276
+ """Schema builder for Organizations database."""
277
+
278
+ @staticmethod
279
+ def get_schema() -> Dict[str, Dict[str, Any]]:
280
+ """Return Notion database schema for Organizations.
281
+
282
+ Returns:
283
+ Dict mapping property names to property schemas
284
+
285
+ Example:
286
+ >>> schema = OrganizationSchema.get_schema()
287
+ >>> # Use with Notion API to create database
288
+ """
289
+ return {
290
+ "Name": PropertyBuilder.title("Name"),
291
+ "Slug": PropertyBuilder.text("Slug"),
292
+ "Description": PropertyBuilder.text("Description"),
293
+ "Repository URL": PropertyBuilder.url("Repository URL"),
294
+ "Status": PropertyBuilder.select(
295
+ "Status",
296
+ [
297
+ SelectOption.option("Active", "green"),
298
+ SelectOption.option("Archived", "gray"),
299
+ SelectOption.option("On Hold", "yellow"),
300
+ ],
301
+ ),
302
+ }
303
+
304
+
305
+ class ProjectSchema:
306
+ """Schema builder for Projects database."""
307
+
308
+ @staticmethod
309
+ def get_schema() -> Dict[str, Dict[str, Any]]:
310
+ """Return Notion database schema for Projects."""
311
+ return {
312
+ "Name": PropertyBuilder.title("Name"),
313
+ "Organization": PropertyBuilder.relation("Organization"),
314
+ "Slug": PropertyBuilder.text("Slug"),
315
+ "Description": PropertyBuilder.text("Description"),
316
+ "Repository": PropertyBuilder.url("Repository"),
317
+ "Status": PropertyBuilder.select(
318
+ "Status",
319
+ [
320
+ SelectOption.option("Active", "green"),
321
+ SelectOption.option("Archived", "gray"),
322
+ SelectOption.option("Planning", "blue"),
323
+ SelectOption.option("Completed", "purple"),
324
+ ],
325
+ ),
326
+ "Tech Stack": PropertyBuilder.multi_select(
327
+ "Tech Stack",
328
+ [
329
+ SelectOption.option("Python", "blue"),
330
+ SelectOption.option("JavaScript", "yellow"),
331
+ SelectOption.option("TypeScript", "blue"),
332
+ SelectOption.option("React", "blue"),
333
+ SelectOption.option("Vue", "green"),
334
+ SelectOption.option("Node.js", "green"),
335
+ SelectOption.option("Go", "cyan"),
336
+ SelectOption.option("Rust", "orange"),
337
+ SelectOption.option("Java", "red"),
338
+ SelectOption.option("C++", "blue"),
339
+ SelectOption.option("SQL", "purple"),
340
+ SelectOption.option("NoSQL", "pink"),
341
+ ],
342
+ ),
343
+ "Role": PropertyBuilder.select(
344
+ "Role",
345
+ [
346
+ SelectOption.option("Developer", "blue"),
347
+ SelectOption.option("PM", "purple"),
348
+ SelectOption.option("Product Analyst", "orange"),
349
+ SelectOption.option("QA", "green"),
350
+ SelectOption.option("Designer", "pink"),
351
+ SelectOption.option("DevOps", "gray"),
352
+ SelectOption.option("Admin", "red"),
353
+ ],
354
+ ),
355
+ }
356
+
357
+
358
+ class VersionSchema:
359
+ """Schema builder for Versions database."""
360
+
361
+ @staticmethod
362
+ def get_schema() -> Dict[str, Dict[str, Any]]:
363
+ """Return Notion database schema for Versions."""
364
+ return {
365
+ "Version": PropertyBuilder.title("Version"),
366
+ "Project": PropertyBuilder.relation("Project"),
367
+ "Status": PropertyBuilder.select(
368
+ "Status",
369
+ [
370
+ SelectOption.option("Planning", "gray"),
371
+ SelectOption.option("Alpha", "blue"),
372
+ SelectOption.option("Beta", "purple"),
373
+ SelectOption.option("RC", "orange"),
374
+ SelectOption.option("In Progress", "yellow"),
375
+ SelectOption.option("Released", "green"),
376
+ SelectOption.option("On Hold", "default"),
377
+ SelectOption.option("Cancelled", "red"),
378
+ ],
379
+ ),
380
+ "Type": PropertyBuilder.select(
381
+ "Type",
382
+ [
383
+ SelectOption.option("Major", "red"),
384
+ SelectOption.option("Minor", "orange"),
385
+ SelectOption.option("Patch", "yellow"),
386
+ SelectOption.option("Hotfix", "red"),
387
+ ],
388
+ ),
389
+ "Branch Name": PropertyBuilder.text("Branch Name"),
390
+ "Progress": PropertyBuilder.number("Progress", format="percent"),
391
+ "Release Date": PropertyBuilder.date("Release Date"),
392
+ "Superseded By": PropertyBuilder.relation("Superseded By", dual_property=False),
393
+ }
394
+
395
+
396
+ class TaskSchema:
397
+ """Schema builder for Tasks database."""
398
+
399
+ @staticmethod
400
+ def get_schema() -> Dict[str, Dict[str, Any]]:
401
+ """Return Notion database schema for Tasks."""
402
+ return {
403
+ "Title": PropertyBuilder.title("Title"),
404
+ "Version": PropertyBuilder.relation("Version"),
405
+ "Target Version": PropertyBuilder.relation("Target Version", dual_property=False),
406
+ "Type": PropertyBuilder.select(
407
+ "Type",
408
+ [
409
+ SelectOption.option("New Feature", "green"),
410
+ SelectOption.option("Refactor", "blue"),
411
+ SelectOption.option("Documentation", "gray"),
412
+ SelectOption.option("Test", "purple"),
413
+ SelectOption.option("Bug Fix", "red"),
414
+ SelectOption.option("Performance", "orange"),
415
+ SelectOption.option("Security", "red"),
416
+ ],
417
+ ),
418
+ "Status": PropertyBuilder.select(
419
+ "Status",
420
+ [
421
+ SelectOption.option("Backlog", "gray"),
422
+ SelectOption.option("Claimed", "blue"),
423
+ SelectOption.option("In Progress", "yellow"),
424
+ SelectOption.option("In Review", "purple"),
425
+ SelectOption.option("Completed", "green"),
426
+ SelectOption.option("Cancelled", "red"),
427
+ ],
428
+ ),
429
+ "Priority": PropertyBuilder.select(
430
+ "Priority",
431
+ [
432
+ SelectOption.option("Critical", "red"),
433
+ SelectOption.option("High", "orange"),
434
+ SelectOption.option("Medium", "yellow"),
435
+ SelectOption.option("Low", "blue"),
436
+ ],
437
+ ),
438
+ "Dependencies": PropertyBuilder.relation("Dependencies", dual_property=False),
439
+ "Dependent Tasks": PropertyBuilder.relation("Dependent Tasks", dual_property=False),
440
+ "Estimated Hours": PropertyBuilder.number("Estimated Hours"),
441
+ "Actual Hours": PropertyBuilder.number("Actual Hours"),
442
+ "Assignee": PropertyBuilder.people("Assignee"),
443
+ "Created Date": PropertyBuilder.date("Created Date"),
444
+ "Completed Date": PropertyBuilder.date("Completed Date"),
445
+ }
446
+
447
+
448
+ class IdeaSchema:
449
+ """Schema builder for Ideas database."""
450
+
451
+ @staticmethod
452
+ def get_schema() -> Dict[str, Dict[str, Any]]:
453
+ """Return Notion database schema for Ideas."""
454
+ return {
455
+ "Title": PropertyBuilder.title("Title"),
456
+ "Project": PropertyBuilder.relation("Project"),
457
+ "Category": PropertyBuilder.select(
458
+ "Category",
459
+ [
460
+ SelectOption.option("Feature", "green"),
461
+ SelectOption.option("Improvement", "blue"),
462
+ SelectOption.option("Refactor", "purple"),
463
+ SelectOption.option("Process", "orange"),
464
+ SelectOption.option("Tool", "pink"),
465
+ ],
466
+ ),
467
+ "Status": PropertyBuilder.select(
468
+ "Status",
469
+ [
470
+ SelectOption.option("New", "gray"),
471
+ SelectOption.option("Evaluated", "blue"),
472
+ SelectOption.option("Accepted", "green"),
473
+ SelectOption.option("Rejected", "red"),
474
+ SelectOption.option("Deferred", "yellow"),
475
+ ],
476
+ ),
477
+ "Description": PropertyBuilder.text("Description"),
478
+ "Proposed Solution": PropertyBuilder.text("Proposed Solution"),
479
+ "Benefits": PropertyBuilder.text("Benefits"),
480
+ "Effort Estimate": PropertyBuilder.select(
481
+ "Effort Estimate",
482
+ [
483
+ SelectOption.option("Small", "green"),
484
+ SelectOption.option("Medium", "yellow"),
485
+ SelectOption.option("Large", "red"),
486
+ ],
487
+ ),
488
+ "Context": PropertyBuilder.text("Context"),
489
+ "Related Task": PropertyBuilder.relation("Related Task", dual_property=False),
490
+ }
491
+
492
+
493
+ class WorkIssueSchema:
494
+ """Schema builder for Work Issues database."""
495
+
496
+ @staticmethod
497
+ def get_schema() -> Dict[str, Dict[str, Any]]:
498
+ """Return Notion database schema for Work Issues."""
499
+ return {
500
+ "Title": PropertyBuilder.title("Title"),
501
+ "Project": PropertyBuilder.relation("Project"),
502
+ "Task": PropertyBuilder.relation("Task"),
503
+ "Type": PropertyBuilder.select(
504
+ "Type",
505
+ [
506
+ SelectOption.option("Blocker", "red"),
507
+ SelectOption.option("Confusion", "yellow"),
508
+ SelectOption.option("Documentation", "gray"),
509
+ SelectOption.option("Tooling", "orange"),
510
+ SelectOption.option("Process", "purple"),
511
+ ],
512
+ ),
513
+ "Severity": PropertyBuilder.select(
514
+ "Severity",
515
+ [
516
+ SelectOption.option("Critical", "red"),
517
+ SelectOption.option("High", "orange"),
518
+ SelectOption.option("Medium", "yellow"),
519
+ SelectOption.option("Low", "blue"),
520
+ ],
521
+ ),
522
+ "Status": PropertyBuilder.select(
523
+ "Status",
524
+ [
525
+ SelectOption.option("Open", "red"),
526
+ SelectOption.option("Investigating", "yellow"),
527
+ SelectOption.option("Resolved", "green"),
528
+ SelectOption.option("Won't Fix", "gray"),
529
+ SelectOption.option("Deferred", "blue"),
530
+ ],
531
+ ),
532
+ "Description": PropertyBuilder.text("Description"),
533
+ "Context": PropertyBuilder.text("Context"),
534
+ "Proposed Solution": PropertyBuilder.text("Proposed Solution"),
535
+ "Related Idea": PropertyBuilder.relation("Related Idea", dual_property=False),
536
+ "Fix Tasks": PropertyBuilder.relation("Fix Tasks", dual_property=False),
537
+ }
538
+
539
+
540
+ class IncidentSchema:
541
+ """Schema builder for Incidents database."""
542
+
543
+ @staticmethod
544
+ def get_schema() -> Dict[str, Dict[str, Any]]:
545
+ """Return Notion database schema for Incidents."""
546
+ return {
547
+ "Title": PropertyBuilder.title("Title"),
548
+ "Project": PropertyBuilder.relation("Project"),
549
+ "Affected Version": PropertyBuilder.relation("Affected Version"),
550
+ "Severity": PropertyBuilder.select(
551
+ "Severity",
552
+ [
553
+ SelectOption.option("Critical", "red"),
554
+ SelectOption.option("High", "orange"),
555
+ SelectOption.option("Medium", "yellow"),
556
+ SelectOption.option("Low", "blue"),
557
+ ],
558
+ ),
559
+ "Type": PropertyBuilder.select(
560
+ "Type",
561
+ [
562
+ SelectOption.option("Bug", "red"),
563
+ SelectOption.option("Crash", "purple"),
564
+ SelectOption.option("Performance", "orange"),
565
+ SelectOption.option("Security", "red"),
566
+ SelectOption.option("Data Loss", "red"),
567
+ ],
568
+ ),
569
+ "Status": PropertyBuilder.select(
570
+ "Status",
571
+ [
572
+ SelectOption.option("Open", "red"),
573
+ SelectOption.option("Investigating", "yellow"),
574
+ SelectOption.option("Fix In Progress", "blue"),
575
+ SelectOption.option("Resolved", "green"),
576
+ ],
577
+ ),
578
+ "Fix Task": PropertyBuilder.relation("Fix Task", dual_property=False),
579
+ "Root Cause": PropertyBuilder.text("Root Cause"),
580
+ "Detected Date": PropertyBuilder.date("Detected Date"),
581
+ "Resolved Date": PropertyBuilder.date("Resolved Date"),
582
+ }
583
+
584
+
585
+ class TagSchema:
586
+ """Schema builder for Tags database."""
587
+
588
+ @staticmethod
589
+ def get_schema() -> Dict[str, Dict[str, Any]]:
590
+ """Return Notion database schema for Tags."""
591
+ return {
592
+ "Name": PropertyBuilder.title("Name"),
593
+ "Category": PropertyBuilder.select(
594
+ "Category",
595
+ [
596
+ SelectOption.option("Type", "blue"),
597
+ SelectOption.option("Domain", "green"),
598
+ SelectOption.option("Component", "purple"),
599
+ SelectOption.option("Priority", "orange"),
600
+ ],
601
+ ),
602
+ "Color": PropertyBuilder.select(
603
+ "Color",
604
+ [
605
+ SelectOption.option("Red", "red"),
606
+ SelectOption.option("Orange", "orange"),
607
+ SelectOption.option("Yellow", "yellow"),
608
+ SelectOption.option("Green", "green"),
609
+ SelectOption.option("Blue", "blue"),
610
+ SelectOption.option("Purple", "purple"),
611
+ ],
612
+ ),
613
+ "Description": PropertyBuilder.text("Description"),
614
+ }