notionhelper 0.2.3__tar.gz → 0.3.1__tar.gz

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 (27) hide show
  1. notionhelper-0.3.1/GETTING_STARTED.md +912 -0
  2. {notionhelper-0.2.3 → notionhelper-0.3.1}/PKG-INFO +172 -2
  3. {notionhelper-0.2.3 → notionhelper-0.3.1}/README.md +171 -1
  4. notionhelper-0.3.1/images/notionh3.png +0 -0
  5. {notionhelper-0.2.3 → notionhelper-0.3.1}/pyproject.toml +1 -1
  6. {notionhelper-0.2.3 → notionhelper-0.3.1}/src/notionhelper/helper.py +212 -0
  7. {notionhelper-0.2.3 → notionhelper-0.3.1}/uv.lock +1 -1
  8. notionhelper-0.2.3/.claude/settings.local.json +0 -12
  9. notionhelper-0.2.3/.python-version +0 -1
  10. notionhelper-0.2.3/notion_api_examples.md +0 -234
  11. notionhelper-0.2.3/src/notionhelper/helper1.8.py +0 -546
  12. {notionhelper-0.2.3 → notionhelper-0.3.1}/.coverage +0 -0
  13. {notionhelper-0.2.3 → notionhelper-0.3.1}/.github/workflows/claude-code-review.yml +0 -0
  14. {notionhelper-0.2.3 → notionhelper-0.3.1}/.github/workflows/claude.yml +0 -0
  15. {notionhelper-0.2.3 → notionhelper-0.3.1}/.gitignore +0 -0
  16. {notionhelper-0.2.3 → notionhelper-0.3.1}/images/helper_logo.png +0 -0
  17. {notionhelper-0.2.3 → notionhelper-0.3.1}/images/json_builder.png.png +0 -0
  18. {notionhelper-0.2.3 → notionhelper-0.3.1}/images/logo.png +0 -0
  19. {notionhelper-0.2.3 → notionhelper-0.3.1}/images/pillio.png +0 -0
  20. {notionhelper-0.2.3 → notionhelper-0.3.1}/images/pillio2.png +0 -0
  21. {notionhelper-0.2.3 → notionhelper-0.3.1}/notionapi_md_info.md +0 -0
  22. {notionhelper-0.2.3 → notionhelper-0.3.1}/pytest.ini +0 -0
  23. {notionhelper-0.2.3 → notionhelper-0.3.1}/src/notionhelper/__init__.py +0 -0
  24. {notionhelper-0.2.3 → notionhelper-0.3.1}/tests/README.md +0 -0
  25. {notionhelper-0.2.3 → notionhelper-0.3.1}/tests/__init__.py +0 -0
  26. {notionhelper-0.2.3 → notionhelper-0.3.1}/tests/conftest.py +0 -0
  27. {notionhelper-0.2.3 → notionhelper-0.3.1}/tests/test_helper.py +0 -0
@@ -0,0 +1,912 @@
1
+ # Getting Started with NotionHelper
2
+
3
+ A comprehensive guide to using NotionHelper for basic Notion API operations.
4
+
5
+ ## Table of Contents
6
+ 1. [Installation & Setup](#installation--setup)
7
+ 2. [Understanding Notion's Structure](#understanding-notions-structure)
8
+ 3. [Basic Operations](#basic-operations)
9
+ 4. [Working with Databases](#working-with-databases)
10
+ 5. [Working with Data Sources](#working-with-data-sources)
11
+ 6. [Working with Pages](#working-with-pages)
12
+ 7. [File Operations](#file-operations)
13
+ 8. [Data Retrieval & Analysis](#data-retrieval--analysis)
14
+
15
+ ---
16
+
17
+ ## Installation & Setup
18
+
19
+ ### 1. Install NotionHelper
20
+
21
+ ```bash
22
+ pip install notionhelper
23
+ ```
24
+
25
+ ### 2. Create a Notion Integration
26
+
27
+ 1. Go to [https://www.notion.so/my-integrations](https://www.notion.so/my-integrations)
28
+ 2. Click **"+ New integration"**
29
+ 3. Give it a name (e.g., "My Python App")
30
+ 4. Select the workspace where you want to use it
31
+ 5. Copy the **"Internal Integration Secret"** (your API token)
32
+
33
+ ### 3. Share Your Notion Pages/Databases
34
+
35
+ **IMPORTANT**: Your integration can only access pages and databases that have been explicitly shared with it.
36
+
37
+ To share a page or database:
38
+ 1. Open the page/database in Notion
39
+ 2. Click the **"Share"** button in the top-right
40
+ 3. Click **"Invite"**
41
+ 4. Search for your integration name
42
+ 5. Click **"Invite"**
43
+
44
+ ### 4. Store Your Token Securely
45
+
46
+ It's best practice to store your token as an environment variable:
47
+
48
+ **On macOS/Linux:**
49
+ ```bash
50
+ export NOTION_TOKEN="secret_xxxxxxxxxxxxxxxxxxxx"
51
+ ```
52
+
53
+ **On Windows (PowerShell):**
54
+ ```powershell
55
+ $env:NOTION_TOKEN="secret_xxxxxxxxxxxxxxxxxxxx"
56
+ ```
57
+
58
+ **Or use a `.env` file:**
59
+ ```
60
+ NOTION_TOKEN=secret_xxxxxxxxxxxxxxxxxxxx
61
+ ```
62
+
63
+ ### 5. Initialize NotionHelper
64
+
65
+ ```python
66
+ import os
67
+ from notionhelper import NotionHelper
68
+
69
+ # Get token from environment variable
70
+ notion_token = os.getenv("NOTION_TOKEN")
71
+
72
+ # Initialize the helper
73
+ helper = NotionHelper(notion_token)
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Understanding Notion's Structure
79
+
80
+ ### API Version 2025-09-03 Changes
81
+
82
+ With the latest Notion API, the structure has changed:
83
+
84
+ ```
85
+ Page (Parent)
86
+ └── Database (Container)
87
+ ├── Data Source 1 (Table with schema/properties)
88
+ │ ├── Page 1
89
+ │ ├── Page 2
90
+ │ └── Page 3
91
+ └── Data Source 2 (Another table)
92
+ ├── Page 1
93
+ └── Page 2
94
+ ```
95
+
96
+ **Key Concepts:**
97
+ - **Database**: A container that holds one or more data sources
98
+ - **Data Source**: A table with a specific schema (columns/properties)
99
+ - **Page**: Individual rows in a data source, or standalone pages
100
+
101
+ **Important IDs:**
102
+ - `database_id`: Points to the container
103
+ - `data_source_id`: Points to a specific table (where you add pages)
104
+ - `page_id`: Points to individual pages
105
+
106
+ ---
107
+
108
+ ## Basic Operations
109
+
110
+ ### Finding IDs in Notion
111
+
112
+ **To get a page, database, or data source ID:**
113
+
114
+ 1. Open the item in Notion
115
+ 2. Look at the URL in your browser:
116
+ ```
117
+ https://www.notion.so/My-Page-1234567890abcdef1234567890abcdef
118
+ └── This is the ID ──────────┘
119
+ ```
120
+ 3. The ID is the 32-character string at the end (without hyphens)
121
+ 4. Format with hyphens: `12345678-90ab-cdef-1234-567890abcdef`
122
+
123
+ ### Searching for Pages and Databases
124
+
125
+ ```python
126
+ # Search for all pages
127
+ pages = helper.notion_search_db(query="", filter_object_type="page")
128
+ for page in pages:
129
+ print(f"Page: {page['id']}")
130
+
131
+ # Search for data sources
132
+ data_sources = helper.notion_search_db(query="", filter_object_type="data_source")
133
+ for ds in data_sources:
134
+ print(f"Data Source: {ds['id']}")
135
+
136
+ # Search with a specific query
137
+ results = helper.notion_search_db(query="Project Tasks", filter_object_type="page")
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Working with Databases
143
+
144
+ ### Retrieve a Database
145
+
146
+ ```python
147
+ # Get the database container
148
+ database_id = "12345678-90ab-cdef-1234-567890abcdef"
149
+ database = helper.get_database(database_id)
150
+
151
+ print(f"Database Title: {database['title'][0]['plain_text']}")
152
+ print(f"Number of Data Sources: {len(database['data_sources'])}")
153
+
154
+ # List all data sources in the database
155
+ for ds in database['data_sources']:
156
+ print(f"Data Source ID: {ds['id']}")
157
+ print(f"Data Source Type: {ds['type']}")
158
+ ```
159
+
160
+ ### Create a New Database
161
+
162
+ ```python
163
+ # Define the parent page where the database will be created
164
+ parent_page_id = "your-parent-page-id"
165
+
166
+ # Define the database properties (schema)
167
+ properties = {
168
+ "Task Name": {"title": {}}, # This will be the title column
169
+ "Status": {
170
+ "select": {
171
+ "options": [
172
+ {"name": "Not Started", "color": "red"},
173
+ {"name": "In Progress", "color": "yellow"},
174
+ {"name": "Done", "color": "green"}
175
+ ]
176
+ }
177
+ },
178
+ "Due Date": {"date": {}},
179
+ "Priority": {
180
+ "select": {
181
+ "options": [
182
+ {"name": "Low", "color": "gray"},
183
+ {"name": "Medium", "color": "blue"},
184
+ {"name": "High", "color": "red"}
185
+ ]
186
+ }
187
+ },
188
+ "Notes": {"rich_text": {}}
189
+ }
190
+
191
+ # Create the database
192
+ new_db = helper.create_database(
193
+ parent_page_id=parent_page_id,
194
+ database_title="My Task Database",
195
+ initial_data_source_properties=properties,
196
+ initial_data_source_title="Main Tasks"
197
+ )
198
+
199
+ print(f"Created Database ID: {new_db['id']}")
200
+ print(f"Initial Data Source ID: {new_db['initial_data_source']['id']}")
201
+
202
+ # Save the data source ID for adding pages later
203
+ data_source_id = new_db['initial_data_source']['id']
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Working with Data Sources
209
+
210
+ ### Retrieve a Data Source Schema
211
+
212
+ ```python
213
+ data_source_id = "your-data-source-id"
214
+ data_source = helper.get_data_source(data_source_id)
215
+
216
+ print(f"Data Source Title: {data_source['title'][0]['plain_text']}")
217
+ print("\nProperties (Columns):")
218
+ for prop_name, prop_data in data_source['properties'].items():
219
+ prop_type = prop_data['type']
220
+ print(f" - {prop_name}: {prop_type}")
221
+ ```
222
+
223
+ ### Update a Data Source
224
+
225
+ #### Rename a Column
226
+
227
+ ```python
228
+ data_source_id = "your-data-source-id"
229
+
230
+ updated = helper.update_data_source(
231
+ data_source_id,
232
+ properties={
233
+ "Old Column Name": {
234
+ "name": "New Column Name"
235
+ }
236
+ }
237
+ )
238
+ print("Column renamed successfully!")
239
+ ```
240
+
241
+ #### Add a New Column
242
+
243
+ ```python
244
+ updated = helper.update_data_source(
245
+ data_source_id,
246
+ properties={
247
+ "Email": {"email": {}}, # Add an email column
248
+ "Phone": {"phone_number": {}}, # Add a phone column
249
+ "Completed": {"checkbox": {}} # Add a checkbox column
250
+ }
251
+ )
252
+ print("New columns added!")
253
+ ```
254
+
255
+ #### Remove a Column
256
+
257
+ ```python
258
+ updated = helper.update_data_source(
259
+ data_source_id,
260
+ properties={
261
+ "Column To Remove": None # Set to None to remove
262
+ }
263
+ )
264
+ print("Column removed!")
265
+ ```
266
+
267
+ #### Update Select Options
268
+
269
+ ```python
270
+ updated = helper.update_data_source(
271
+ data_source_id,
272
+ properties={
273
+ "Status": {
274
+ "select": {
275
+ "options": [
276
+ {"name": "Backlog", "color": "gray"},
277
+ {"name": "To Do", "color": "red"},
278
+ {"name": "In Progress", "color": "yellow"},
279
+ {"name": "Review", "color": "blue"},
280
+ {"name": "Done", "color": "green"}
281
+ ]
282
+ }
283
+ }
284
+ }
285
+ )
286
+ print("Select options updated!")
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Working with Pages
292
+
293
+ ### Create a New Page (Row)
294
+
295
+ **Basic Example:**
296
+
297
+ ```python
298
+ data_source_id = "your-data-source-id"
299
+
300
+ # Define the page properties
301
+ page_properties = {
302
+ "Task Name": { # Title property
303
+ "title": [
304
+ {
305
+ "text": {
306
+ "content": "Complete project documentation"
307
+ }
308
+ }
309
+ ]
310
+ },
311
+ "Status": { # Select property
312
+ "select": {
313
+ "name": "In Progress"
314
+ }
315
+ },
316
+ "Due Date": { # Date property
317
+ "date": {
318
+ "start": "2025-12-31"
319
+ }
320
+ },
321
+ "Priority": { # Select property
322
+ "select": {
323
+ "name": "High"
324
+ }
325
+ },
326
+ "Notes": { # Rich text property
327
+ "rich_text": [
328
+ {
329
+ "text": {
330
+ "content": "Need to update README and add examples"
331
+ }
332
+ }
333
+ ]
334
+ }
335
+ }
336
+
337
+ # Create the page
338
+ new_page = helper.new_page_to_data_source(data_source_id, page_properties)
339
+ page_id = new_page['id']
340
+ print(f"Created page with ID: {page_id}")
341
+ ```
342
+
343
+ **Property Type Examples:**
344
+
345
+ ```python
346
+ # Number
347
+ "Age": {
348
+ "number": 25
349
+ }
350
+
351
+ # Checkbox
352
+ "Completed": {
353
+ "checkbox": True
354
+ }
355
+
356
+ # URL
357
+ "Website": {
358
+ "url": "https://example.com"
359
+ }
360
+
361
+ # Email
362
+ "Contact": {
363
+ "email": "user@example.com"
364
+ }
365
+
366
+ # Phone
367
+ "Phone": {
368
+ "phone_number": "+1234567890"
369
+ }
370
+
371
+ # Multi-select
372
+ "Tags": {
373
+ "multi_select": [
374
+ {"name": "Urgent"},
375
+ {"name": "Bug"},
376
+ {"name": "Frontend"}
377
+ ]
378
+ }
379
+
380
+ # Date with end date (range)
381
+ "Event Period": {
382
+ "date": {
383
+ "start": "2025-01-01",
384
+ "end": "2025-01-07"
385
+ }
386
+ }
387
+ ```
388
+
389
+ ### Retrieve a Page
390
+
391
+ ```python
392
+ page_id = "your-page-id"
393
+ page_data = helper.get_page(page_id)
394
+
395
+ # Access properties
396
+ properties = page_data['properties']
397
+ print(f"Properties: {properties}")
398
+
399
+ # Access page content blocks
400
+ content = page_data['content']
401
+ print(f"Number of blocks: {len(content)}")
402
+ ```
403
+
404
+ ### Add Content to a Page
405
+
406
+ ```python
407
+ page_id = "your-page-id"
408
+
409
+ # Define blocks to add
410
+ blocks = [
411
+ {
412
+ "object": "block",
413
+ "type": "heading_1",
414
+ "heading_1": {
415
+ "rich_text": [
416
+ {
417
+ "type": "text",
418
+ "text": {
419
+ "content": "Project Overview"
420
+ }
421
+ }
422
+ ]
423
+ }
424
+ },
425
+ {
426
+ "object": "block",
427
+ "type": "paragraph",
428
+ "paragraph": {
429
+ "rich_text": [
430
+ {
431
+ "type": "text",
432
+ "text": {
433
+ "content": "This project aims to improve user experience by implementing new features."
434
+ }
435
+ }
436
+ ]
437
+ }
438
+ },
439
+ {
440
+ "object": "block",
441
+ "type": "heading_2",
442
+ "heading_2": {
443
+ "rich_text": [
444
+ {
445
+ "type": "text",
446
+ "text": {
447
+ "content": "Key Features"
448
+ }
449
+ }
450
+ ]
451
+ }
452
+ },
453
+ {
454
+ "object": "block",
455
+ "type": "bulleted_list_item",
456
+ "bulleted_list_item": {
457
+ "rich_text": [
458
+ {
459
+ "type": "text",
460
+ "text": {
461
+ "content": "Enhanced search functionality"
462
+ }
463
+ }
464
+ ]
465
+ }
466
+ },
467
+ {
468
+ "object": "block",
469
+ "type": "bulleted_list_item",
470
+ "bulleted_list_item": {
471
+ "rich_text": [
472
+ {
473
+ "type": "text",
474
+ "text": {
475
+ "content": "Improved mobile responsiveness"
476
+ }
477
+ }
478
+ ]
479
+ }
480
+ },
481
+ {
482
+ "object": "block",
483
+ "type": "numbered_list_item",
484
+ "numbered_list_item": {
485
+ "rich_text": [
486
+ {
487
+ "type": "text",
488
+ "text": {
489
+ "content": "Step 1: Research user needs"
490
+ }
491
+ }
492
+ ]
493
+ }
494
+ },
495
+ {
496
+ "object": "block",
497
+ "type": "numbered_list_item",
498
+ "numbered_list_item": {
499
+ "rich_text": [
500
+ {
501
+ "type": "text",
502
+ "text": {
503
+ "content": "Step 2: Design mockups"
504
+ }
505
+ }
506
+ ]
507
+ }
508
+ },
509
+ {
510
+ "object": "block",
511
+ "type": "to_do",
512
+ "to_do": {
513
+ "rich_text": [
514
+ {
515
+ "type": "text",
516
+ "text": {
517
+ "content": "Complete initial testing"
518
+ }
519
+ }
520
+ ],
521
+ "checked": False
522
+ }
523
+ }
524
+ ]
525
+
526
+ # Append blocks to the page
527
+ helper.append_page_body(page_id, blocks)
528
+ print("Content added to page successfully!")
529
+ ```
530
+
531
+ ---
532
+
533
+ ## File Operations
534
+
535
+ ### Upload and Attach a File
536
+
537
+ **Method 1: Two-step process**
538
+
539
+ ```python
540
+ # Step 1: Upload the file
541
+ file_path = "/path/to/document.pdf"
542
+ upload_response = helper.upload_file(file_path)
543
+ file_upload_id = upload_response['id']
544
+
545
+ # Step 2: Attach to a page
546
+ page_id = "your-page-id"
547
+ attach_response = helper.attach_file_to_page(page_id, file_upload_id)
548
+ print("File attached successfully!")
549
+ ```
550
+
551
+ **Method 2: One-step process (recommended)**
552
+
553
+ ```python
554
+ page_id = "your-page-id"
555
+ file_path = "/path/to/document.pdf"
556
+
557
+ response = helper.one_step_file_to_page(page_id, file_path)
558
+ print("File uploaded and attached in one step!")
559
+ ```
560
+
561
+ ### Upload and Embed an Image
562
+
563
+ ```python
564
+ page_id = "your-page-id"
565
+ image_path = "/path/to/image.png"
566
+
567
+ response = helper.one_step_image_embed(page_id, image_path)
568
+ print("Image embedded in page!")
569
+ ```
570
+
571
+ ### Attach File to a Property
572
+
573
+ Some databases have a "Files & Media" property where you can attach files.
574
+
575
+ ```python
576
+ page_id = "your-page-id"
577
+ property_name = "Attachments" # Name of your Files & Media property
578
+ file_path = "/path/to/document.pdf"
579
+ file_name = "Project Document.pdf" # Display name
580
+
581
+ response = helper.one_step_file_to_page_property(
582
+ page_id,
583
+ property_name,
584
+ file_path,
585
+ file_name
586
+ )
587
+ print("File attached to property!")
588
+ ```
589
+
590
+ ### Upload Multiple Files to a Property
591
+
592
+ ```python
593
+ page_id = "your-page-id"
594
+ property_name = "Documents"
595
+ file_paths = [
596
+ "/path/to/document1.pdf",
597
+ "/path/to/document2.docx",
598
+ "/path/to/image.png"
599
+ ]
600
+
601
+ response = helper.upload_multiple_files_to_property(page_id, property_name, file_paths)
602
+ print(f"Uploaded {len(file_paths)} files!")
603
+ ```
604
+
605
+ ---
606
+
607
+ ## Data Retrieval & Analysis
608
+
609
+ ### Get All Page IDs from a Data Source
610
+
611
+ ```python
612
+ data_source_id = "your-data-source-id"
613
+ page_ids = helper.get_data_source_page_ids(data_source_id)
614
+
615
+ print(f"Found {len(page_ids)} pages:")
616
+ for page_id in page_ids:
617
+ print(f" - {page_id}")
618
+ ```
619
+
620
+ ### Get All Pages as JSON
621
+
622
+ ```python
623
+ data_source_id = "your-data-source-id"
624
+
625
+ # Get all pages
626
+ all_pages = helper.get_data_source_pages_as_json(data_source_id)
627
+
628
+ # Get only first 10 pages
629
+ limited_pages = helper.get_data_source_pages_as_json(data_source_id, limit=10)
630
+
631
+ print(f"Retrieved {len(all_pages)} pages")
632
+ print(f"First page properties: {all_pages[0]}")
633
+ ```
634
+
635
+ ### Get All Pages as Pandas DataFrame
636
+
637
+ **This is perfect for data analysis!**
638
+
639
+ ```python
640
+ import pandas as pd
641
+
642
+ data_source_id = "your-data-source-id"
643
+
644
+ # Get all pages as a DataFrame
645
+ df = helper.get_data_source_pages_as_dataframe(data_source_id)
646
+
647
+ print(df.head())
648
+ print(f"\nDataFrame shape: {df.shape}")
649
+ print(f"Columns: {df.columns.tolist()}")
650
+ ```
651
+
652
+ **With pagination:**
653
+
654
+ ```python
655
+ # Get only first 50 pages
656
+ df = helper.get_data_source_pages_as_dataframe(data_source_id, limit=50)
657
+
658
+ # Get pages without IDs
659
+ df = helper.get_data_source_pages_as_dataframe(
660
+ data_source_id,
661
+ include_page_ids=False
662
+ )
663
+ ```
664
+
665
+ **Data Analysis Example:**
666
+
667
+ ```python
668
+ # Get the data
669
+ df = helper.get_data_source_pages_as_dataframe(data_source_id)
670
+
671
+ # Filter completed tasks
672
+ completed = df[df['Status'] == 'Done']
673
+ print(f"Completed tasks: {len(completed)}")
674
+
675
+ # Group by status
676
+ status_counts = df['Status'].value_counts()
677
+ print("\nTasks by status:")
678
+ print(status_counts)
679
+
680
+ # Find high priority tasks
681
+ high_priority = df[df['Priority'] == 'High']
682
+ print(f"\nHigh priority tasks: {len(high_priority)}")
683
+
684
+ # Export to CSV
685
+ df.to_csv('notion_tasks.csv', index=False)
686
+ print("Exported to CSV!")
687
+
688
+ # Calculate statistics
689
+ if 'Completion Rate' in df.columns:
690
+ avg_completion = df['Completion Rate'].mean()
691
+ print(f"Average completion rate: {avg_completion:.2f}%")
692
+ ```
693
+
694
+ ---
695
+
696
+ ## Complete Example: Task Manager
697
+
698
+ Here's a complete example that creates a task management system:
699
+
700
+ ```python
701
+ import os
702
+ from notionhelper import NotionHelper
703
+ from datetime import datetime, timedelta
704
+
705
+ # Initialize
706
+ notion_token = os.getenv("NOTION_TOKEN")
707
+ helper = NotionHelper(notion_token)
708
+
709
+ # 1. Create a task database
710
+ parent_page_id = "your-parent-page-id"
711
+
712
+ properties = {
713
+ "Task": {"title": {}},
714
+ "Status": {
715
+ "select": {
716
+ "options": [
717
+ {"name": "Backlog", "color": "gray"},
718
+ {"name": "To Do", "color": "red"},
719
+ {"name": "In Progress", "color": "yellow"},
720
+ {"name": "Done", "color": "green"}
721
+ ]
722
+ }
723
+ },
724
+ "Priority": {
725
+ "select": {
726
+ "options": [
727
+ {"name": "Low", "color": "gray"},
728
+ {"name": "Medium", "color": "blue"},
729
+ {"name": "High", "color": "red"}
730
+ ]
731
+ }
732
+ },
733
+ "Due Date": {"date": {}},
734
+ "Assignee": {"rich_text": {}},
735
+ "Completed": {"checkbox": {}}
736
+ }
737
+
738
+ db = helper.create_database(
739
+ parent_page_id=parent_page_id,
740
+ database_title="Team Tasks",
741
+ initial_data_source_properties=properties
742
+ )
743
+
744
+ data_source_id = db['initial_data_source']['id']
745
+ print(f"Created database with data source: {data_source_id}")
746
+
747
+ # 2. Add some tasks
748
+ tasks = [
749
+ {
750
+ "Task": "Design homepage mockup",
751
+ "Status": "In Progress",
752
+ "Priority": "High",
753
+ "Due Date": (datetime.now() + timedelta(days=3)).strftime("%Y-%m-%d"),
754
+ "Assignee": "Alice",
755
+ "Completed": False
756
+ },
757
+ {
758
+ "Task": "Write API documentation",
759
+ "Status": "To Do",
760
+ "Priority": "Medium",
761
+ "Due Date": (datetime.now() + timedelta(days=7)).strftime("%Y-%m-%d"),
762
+ "Assignee": "Bob",
763
+ "Completed": False
764
+ },
765
+ {
766
+ "Task": "Set up CI/CD pipeline",
767
+ "Status": "Done",
768
+ "Priority": "High",
769
+ "Due Date": (datetime.now() - timedelta(days=2)).strftime("%Y-%m-%d"),
770
+ "Assignee": "Charlie",
771
+ "Completed": True
772
+ }
773
+ ]
774
+
775
+ for task in tasks:
776
+ page_props = {
777
+ "Task": {
778
+ "title": [{"text": {"content": task["Task"]}}]
779
+ },
780
+ "Status": {
781
+ "select": {"name": task["Status"]}
782
+ },
783
+ "Priority": {
784
+ "select": {"name": task["Priority"]}
785
+ },
786
+ "Due Date": {
787
+ "date": {"start": task["Due Date"]}
788
+ },
789
+ "Assignee": {
790
+ "rich_text": [{"text": {"content": task["Assignee"]}}]
791
+ },
792
+ "Completed": {
793
+ "checkbox": task["Completed"]
794
+ }
795
+ }
796
+
797
+ new_page = helper.new_page_to_data_source(data_source_id, page_props)
798
+ print(f"Created task: {task['Task']}")
799
+
800
+ # 3. Retrieve and analyze
801
+ df = helper.get_data_source_pages_as_dataframe(data_source_id)
802
+ print("\n=== Task Summary ===")
803
+ print(f"Total tasks: {len(df)}")
804
+ print(f"\nBy Status:")
805
+ print(df['Status'].value_counts())
806
+ print(f"\nBy Priority:")
807
+ print(df['Priority'].value_counts())
808
+ print(f"\nCompleted: {df['Completed'].sum()}")
809
+ print(f"In Progress: {len(df[df['Status'] == 'In Progress'])}")
810
+ ```
811
+
812
+ ---
813
+
814
+ ## Common Patterns
815
+
816
+ ### Bulk Add Pages
817
+
818
+ ```python
819
+ import pandas as pd
820
+
821
+ # Read from CSV
822
+ df = pd.read_csv('tasks.csv')
823
+
824
+ for _, row in df.iterrows():
825
+ page_props = {
826
+ "Task": {
827
+ "title": [{"text": {"content": str(row['task_name'])}}]
828
+ },
829
+ "Status": {
830
+ "select": {"name": str(row['status'])}
831
+ },
832
+ "Priority": {
833
+ "select": {"name": str(row['priority'])}
834
+ }
835
+ }
836
+
837
+ helper.new_page_to_data_source(data_source_id, page_props)
838
+ print(f"Added: {row['task_name']}")
839
+ ```
840
+
841
+ ### Sync Data Between Notion and CSV
842
+
843
+ ```python
844
+ # Export from Notion
845
+ df = helper.get_data_source_pages_as_dataframe(data_source_id)
846
+ df.to_csv('notion_export.csv', index=False)
847
+ print("Exported to CSV")
848
+
849
+ # Import to Notion
850
+ df = pd.read_csv('import_data.csv')
851
+ for _, row in df.iterrows():
852
+ # Create page from row data
853
+ pass
854
+ ```
855
+
856
+ ### Update Multiple Pages
857
+
858
+ ```python
859
+ # Get all pages
860
+ page_ids = helper.get_data_source_page_ids(data_source_id)
861
+
862
+ # Note: There's no built-in batch update in the current version
863
+ # You would need to update pages individually using the Notion API
864
+ ```
865
+
866
+ ---
867
+
868
+ ## Troubleshooting
869
+
870
+ ### Common Errors
871
+
872
+ **1. "object not found"**
873
+ - Make sure you've shared the page/database with your integration
874
+ - Check that the ID is correct
875
+
876
+ **2. "body failed validation"**
877
+ - Check property names match exactly (case-sensitive)
878
+ - Verify property types match the schema
879
+ - Ensure select options exist in the database
880
+
881
+ **3. "Unauthorized"**
882
+ - Check your API token is correct
883
+ - Make sure the token is properly set in environment variables
884
+
885
+ **4. "Could not find database"**
886
+ - You might be using a `database_id` instead of a `data_source_id`
887
+ - Get the data source ID from the database object
888
+
889
+ ### Best Practices
890
+
891
+ 1. **Always use environment variables for tokens**
892
+ 2. **Share resources with your integration before accessing**
893
+ 3. **Use data_source_id when creating pages, not database_id**
894
+ 4. **Handle errors with try-except blocks**
895
+ 5. **Test with a single page before bulk operations**
896
+ 6. **Use limit parameter when testing to avoid retrieving too much data**
897
+
898
+ ---
899
+
900
+ ## Next Steps
901
+
902
+ Now that you understand the basics:
903
+
904
+ 1. Explore the [Complete ML Experiment Tracking Guide](README.md#machine-learning-experiment-tracking)
905
+ 2. Check out advanced features in the [README](README.md)
906
+ 3. Review the [Notion API Documentation](https://developers.notion.com)
907
+
908
+ ---
909
+
910
+ **Need Help?**
911
+ - Check the [README.md](README.md) for complete function reference
912
+ - Visit [Notion API JSON Builder](https://notioinapiassistant.streamlit.app) for help with property JSON