Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,27 @@ new_goal = client.goal.create(
)
```

### Bulk Operations

Create multiple items at once with detailed error handling:

```python
# Create multiple todos in one operation
todos = [
{"title": "Review sales report", "meeting_id": meeting_id},
{"title": "Update CRM data", "meeting_id": meeting_id},
{"title": "Send follow-up emails", "meeting_id": meeting_id}
]

result = client.todo.create_many(todos)
print(f"Created {len(result.successful)} todos successfully")

if result.failed:
print(f"Failed to create {len(result.failed)} todos:")
for failure in result.failed:
print(f" - {failure.input_data['title']}: {failure.error}")
```

### Working with Meetings

```python
Expand Down Expand Up @@ -215,4 +236,5 @@ except BloomyError as e:

- Read the [Configuration Guide](configuration.md) for more authentication options
- Explore the [API Reference](../api/client.md) for all available operations
- Check out the [User Guide](../guide/usage.md) for advanced usage patterns
- Check out the [User Guide](../guide/usage.md) for advanced usage patterns
- Learn about [Bulk Operations](../guide/bulk-operations.md) for efficient batch processing
330 changes: 330 additions & 0 deletions docs/guide/bulk-operations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
# Bulk Operations

The Bloomy SDK provides bulk creation methods for efficiently creating multiple resources at once. These methods use a best-effort approach, processing items sequentially to avoid rate limiting while capturing both successful and failed operations.

## Overview

Bulk operations are available for:

- Issues
- Todos
- Meetings
- Goals (Rocks)

Each bulk operation returns a `BulkCreateResult` containing:

- `successful`: List of successfully created items
- `failed`: List of `BulkCreateError` objects with failure details

!!! note "Best-Effort Processing"
Bulk operations are not transactional. If one item fails, the operation continues with the remaining items. Always check both successful and failed results.

## BulkCreateResult Structure

```python
from bloomy.models import BulkCreateResult, BulkCreateError

# Result structure
result = BulkCreateResult(
successful=[...], # List of successfully created items
failed=[ # List of failed operations
BulkCreateError(
index=2, # Original index in input list
input_data={"title": "..."}, # Data that failed
error="Validation error: ..." # Error message
)
]
)
```

## Creating Multiple Issues

Create multiple issues for a meeting:

```python
# Prepare issue data
issues = [
{
"meeting_id": 123,
"title": "Review Q4 metrics",
"notes": "Focus on conversion rates"
},
{
"meeting_id": 123,
"title": "Discuss budget overruns",
"user_id": 456 # Assign to specific user
},
{
"meeting_id": 123,
"title": "Customer feedback analysis"
}
]

# Create issues
result = client.issue.create_many(issues)

# Handle results
print(f"Created {len(result.successful)} issues")
for issue in result.successful:
print(f" - {issue.title} (ID: {issue.id})")

if result.failed:
print(f"\nFailed to create {len(result.failed)} issues:")
for failure in result.failed:
print(f" - Index {failure.index}: {failure.error}")
```

## Creating Multiple Todos

Batch create todos with different assignees and due dates:

```python
from datetime import datetime, timedelta

# Generate due dates
today = datetime.now().date()
tomorrow = (today + timedelta(days=1)).isoformat()
next_week = (today + timedelta(days=7)).isoformat()

# Prepare todos
todos = [
{
"meeting_id": 123,
"title": "Send meeting notes",
"due_date": tomorrow
},
{
"meeting_id": 123,
"title": "Update project dashboard",
"user_id": 456,
"due_date": next_week,
"notes": "Include latest metrics"
},
{
"meeting_id": 123,
"title": "Schedule follow-up meeting"
}
]

# Create todos
result = client.todo.create_many(todos)

# Process results
if result.successful:
print(f"Successfully created {len(result.successful)} todos")

if result.failed:
# Handle failures - maybe retry or log
for failure in result.failed:
print(f"Failed todo '{failure.input_data['title']}': {failure.error}")
```

## Creating Multiple Goals

Create quarterly goals for team members:

```python
# Quarterly goals for different team members
goals = [
{
"meeting_id": 123,
"title": "Increase NPS score to 70+",
"user_id": 456
},
{
"meeting_id": 123,
"title": "Launch mobile app v2.0",
"user_id": 789
},
{
"meeting_id": 123,
"title": "Reduce customer churn by 15%"
# user_id defaults to current user
}
]

# Create goals
result = client.goal.create_many(goals)

# Report results
print(f"Goal creation summary:")
print(f" Successful: {len(result.successful)}")
print(f" Failed: {len(result.failed)}")

# Archive failed goals for review
if result.failed:
failed_titles = [f.input_data['title'] for f in result.failed]
print(f"Failed goals: {', '.join(failed_titles)}")
```

## Creating Multiple Meetings

Create a series of meetings:

```python
# Weekly team meetings
meetings = [
{
"title": "Weekly Team Sync - Week 1",
"attendees": [456, 789] # User IDs
},
{
"title": "Weekly Team Sync - Week 2",
"attendees": [456, 789, 321]
},
{
"title": "Monthly All-Hands",
"add_self": True # Explicitly add current user
}
]

# Create meetings
result = client.meeting.create_many(meetings)

# Get meeting IDs for further operations
meeting_ids = [m['id'] for m in result.successful]
print(f"Created meetings with IDs: {meeting_ids}")
```

## Error Handling Strategies

### Retry Failed Operations

```python
def retry_failed_items(client, failed_items, resource_type):
"""Retry failed bulk operations."""
if not failed_items:
return

# Extract original data from failures
retry_data = [f.input_data for f in failed_items]

# Retry based on resource type
if resource_type == "todo":
retry_result = client.todo.create_many(retry_data)
elif resource_type == "issue":
retry_result = client.issue.create_many(retry_data)
# ... handle other types

return retry_result

# Usage
result = client.todo.create_many(todos)
if result.failed:
print(f"Retrying {len(result.failed)} failed todos...")
retry_result = retry_failed_items(client, result.failed, "todo")
```

### Validation Before Bulk Creation

```python
def validate_todos(todos):
"""Pre-validate todos before bulk creation."""
valid = []
invalid = []

for i, todo in enumerate(todos):
errors = []

if "title" not in todo:
errors.append("Missing required field: title")
if "meeting_id" not in todo:
errors.append("Missing required field: meeting_id")
if len(todo.get("title", "")) > 255:
errors.append("Title too long (max 255 chars)")

if errors:
invalid.append((i, todo, errors))
else:
valid.append(todo)

return valid, invalid

# Validate before creating
valid_todos, invalid_todos = validate_todos(todos)

if invalid_todos:
print("Invalid todos found:")
for idx, todo, errors in invalid_todos:
print(f" Index {idx}: {', '.join(errors)}")

# Create only valid todos
if valid_todos:
result = client.todo.create_many(valid_todos)
```

### Partial Success Handling

```python
def handle_bulk_result(result, resource_name):
"""Generic handler for bulk operation results."""
total = len(result.successful) + len(result.failed)
success_rate = len(result.successful) / total * 100 if total > 0 else 0

print(f"\n{resource_name} Bulk Creation Summary:")
print(f" Total: {total}")
print(f" Successful: {len(result.successful)} ({success_rate:.1f}%)")
print(f" Failed: {len(result.failed)}")

if result.failed:
print(f"\nFailure Details:")
for failure in result.failed:
print(f" - Item {failure.index}: {failure.error}")
print(f" Data: {failure.input_data}")

return result.successful, result.failed

# Usage
result = client.issue.create_many(issues)
successful, failed = handle_bulk_result(result, "Issue")
```

## Performance Considerations

!!! warning "Rate Limiting"
Bulk operations process items sequentially to avoid rate limiting. For large batches, consider:

- Breaking into smaller chunks (e.g., 50-100 items)
- Adding delays between chunks
- Monitoring API rate limit headers

### Chunking Large Operations

```python
def chunk_list(items, chunk_size=50):
"""Split list into chunks."""
for i in range(0, len(items), chunk_size):
yield items[i:i + chunk_size]

# Process large batch in chunks
all_todos = [...] # Large list of todos
all_results = []

for i, chunk in enumerate(chunk_list(all_todos, 50)):
print(f"Processing chunk {i + 1}...")
result = client.todo.create_many(chunk)
all_results.append(result)

# Optional: Add delay between chunks
if i < len(all_todos) // 50:
time.sleep(1)

# Aggregate results
total_successful = sum(len(r.successful) for r in all_results)
total_failed = sum(len(r.failed) for r in all_results)
```

## Best Practices

1. **Validate Input Data**: Check required fields before bulk operations
2. **Handle Partial Failures**: Always check both successful and failed results
3. **Log Operations**: Keep records of bulk operations for debugging
4. **Use Appropriate Chunk Sizes**: Balance between efficiency and rate limits
5. **Implement Retry Logic**: For transient failures, consider retrying
6. **Monitor Rate Limits**: Watch for 429 errors and adjust accordingly

## Next Steps

- Learn about [Error Handling](errors.md) for bulk operations
- Explore [Async Support](async.md) for concurrent operations
- Review individual resource documentation in the [API Reference](../api/client.md)
8 changes: 8 additions & 0 deletions docs/guide/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ todos_to_complete = client.todo.list()[:5]
for todo in todos_to_complete:
if not todo.complete:
client.todo.complete(todo.id)

# For bulk creation, see the [Bulk Operations Guide](bulk-operations.md)
# Example: Create multiple todos at once
todos = [
{"title": "Task 1", "meeting_id": 123},
{"title": "Task 2", "meeting_id": 123}
]
result = client.todo.create_many(todos)
```

### Error Recovery
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ A Python SDK for interacting with the [Bloom Growth](https://www.bloomgrowth.com
- **Type Safety** - Full type annotations for better IDE support and code reliability
- **Pythonic API** - Intuitive, Python-friendly interface with proper error handling
- **Full Async Support** - Complete async/await support for all operations with dedicated AsyncClient for modern Python applications
- **Bulk Operations** - Efficient bulk creation for issues, todos, meetings, and goals with detailed error handling

## Quick Example

Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ nav:
- User Guide:
- Authentication: guide/authentication.md
- Basic Usage: guide/usage.md
- Bulk Operations: guide/bulk-operations.md
- Async Support: guide/async.md
- Error Handling: guide/errors.md
- API Reference:
Expand Down
Loading