Metadata-Version: 2.4
Name: sdk-notion
Version: 0.1.0
Summary: Async Python SDK for the Notion API
Author: Eduardo Riguetto
License: MIT
Project-URL: Homepage, https://github.com/riguettodev/sdk-notion
Project-URL: Repository, https://github.com/riguettodev/sdk-notion
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx<1.0,>=0.25
Requires-Dist: pydantic<3.0,>=2.5
Dynamic: license-file

# Notion SDK

> **Beta Release** 🎉\
> Desenvolvido por [Eduardo Riguetto](https://riguetto.dev)

## 🎯 Visão Geral

SDK Python moderno e type-safe para a API Notion, com foco em Developer Experience (DX) e autocomplete perfeito.

---

## ✨ Principais Features

### 🔐 Autenticação Simplificada

- Configuração centralizada via `Notion()` com suporte a múltiplas versões da API
- Suporte para versões `legacy` (2022-06-28), `data_sources` (2025-09-03), ou personalizada
- Headers gerenciados automaticamente via singleton pattern

### 🗄️ ORM Declarativo

- **Sistema de Mapeamento Declarativo**: Defina schemas uma única vez com `NotionDatabase`
- **Type Hints Perfeitos**: Autocomplete completo em IDEs (VSCode, PyCharm)
- **Transformers Customizáveis**: Processe propriedades do Notion conforme necessário
- **Validators Integrados**: Validação automática com Pydantic
- **Computed Fields**: Campos calculados automaticamente a partir de outros campos
- **Registry Global**: Acesso centralizado a todas as databases via `DatabaseRegistry`
- **Container Pattern**: Organize suas databases com `DatabasesContainer`

### 📦 Repositories

**Pages:**

- `CreatePage` - Criação de páginas com fluent interface
- `GetPage` - Busca de páginas com mapeamento automático de schemas
- `GetPageProperty` - Acesso direto a propriedades específicas

**Databases:**

- `CreateDatabasePage` - Criação de páginas em databases com type safety
- `SearchPage` - Queries avançadas com filtros e ordenação
- `SearchPageProperty` - Busca de propriedades específicas em databases

### 🎨 Fluent Interface

```python
await notion.orm.repo.pages.CreatePage()\
    .set_parent("database_id", "db_id")\
    .set_title("Name", "My Page")\
    .set_property.start_date("Date", datetime.now())\
    .set_property.number("Count", 42)\
    .call()
```

### 🔧 Features Avançadas

- **Generic Types**: Tipagem forte com Generic/TypeVars para autocomplete perfeito
- **Property System**: Sistema completo de setters para todos os tipos de propriedades
- **Query Builders**: Construtores fluentes para filtros (`QueryFilter`) e ordenação (`QuerySort`)
- **Property Extractors**: Extração automática de propriedades da API Notion
- **Property Parsers**: Parse inteligente de respostas com suporte a tipos complexos
- **Property Accessors**: Acesso tipado a propriedades de páginas
- **Configuração Global**: Timezone e outras configs definidas via `ORMConfig`
- **Singleton Pattern**: Client configurado globalmente, sem duplicação

---

## 🚀 Quick Start

```python
from notion import Notion
from my_databases import MyContainer

# Configuração
notion: Notion[MyContainer] = Notion(
    api_token="secret_...",
    api_version="data_sources",
    orm_container=MyContainer,
    timezone="America/Sao_Paulo"
)

# Uso com autocomplete perfeito
con = notion.orm.repo.databases.container.tasks.SearchPage
result = await con\
    .set_filter(
        con.filter.checkbox("Done", "equals", False)
    )\
    .call()

# Acesso type-safe
for page in result.results:
    print(page.properties.myproperty)  # ← Autocomplete funciona!
```

---

### 📦 Configurando seu ORM

#### 1. Defina seus Schemas

Crie schemas para suas databases do Notion usando `NotionDatabase`:

```python
# my_databases/tasks.py

from notion.types.orm.databases import NotionDatabase
from pydantic import ConfigDict
from typing import Optional, List
from datetime import date, datetime
from decimal import Decimal

class TasksDb(NotionDatabase):
    
    model_config = ConfigDict(title="Notion_Databases_TasksDb")
    
    # Defina os campos do schema
    title: Optional[str]
    status: Optional[str]
    priority: Optional[str]
    tags: Optional[List[str]]
    due_date: Optional[date]
    assignee: Optional[str]
    completed: bool = False
    progress: Optional[int]
    estimated_hours: Optional[Decimal]
    actual_hours: Optional[Decimal]
    
    # Campos computed (não vêm do Notion, são calculados)
    is_overdue: Optional[bool] = None
    total_cost: Optional[Decimal] = None
    
    class NotionConfig:
        # ID da sua database no Notion
        database_id = "abc123def456"
        
        # Mapeie campos Python → Campos Notion
        mappings = {
            "title": "Title",
            "status": "Status",
            "priority": "Priority",
            "tags": "Tags",
            "due_date": "Due Date",
            "assignee": "Assignee",
            "completed": "Completed",
            "progress": "Progress",
            "estimated_hours": "Estimated Hours",
            "actual_hours": "Actual Hours"
        }
        
        # Transformers para processar valores
        transformers = {
            "status": lambda x: x["name"] if x else None,
            "priority": lambda x: x["name"] if x else None,
            "tags": lambda x: [t["name"] for t in x] if x else [],
            "assignee": lambda x: x[0] if x else None,
            "due_date": lambda x: x["start"] if x else None,
            "estimated_hours": lambda x: Decimal(str(x)) if x else None,
            "actual_hours": lambda x: Decimal(str(x)) if x else None
        }
        
        # Validators para garantir integridade dos dados
        validators = {
            "progress": lambda v: max(0, min(100, v)) if v else 0,  # Entre 0-100
            "estimated_hours": lambda v: v if v and v >= 0 else Decimal(0)  # Não negativo
        }
        
        # Computed fields calculados automaticamente
        computed = {
            "is_overdue": lambda obj: (
                obj.due_date < date.today() and not obj.completed
                if obj.due_date else False
            ),
            "total_cost": lambda obj: (
                obj.actual_hours * Decimal("50.00")  # $50/hora
                if obj.actual_hours else None
            )
        }
```

#### 2. Crie seu Container

Organize todas as suas databases em um container:

```python
# my_databases/__init__.py

from notion.types.orm.databases import Container, Registry, Client
from .tasks import TasksDb
from .projects import ProjectsDb  # Outras databases...

class MyDatabasesContainer(Container):
    """Container customizado com suas databases"""
    
    def __init__(self):
        # Registre todas as databases
        Registry.register(TasksDb)
        Registry.register(ProjectsDb)
        
        # Configure acesso tipado
        self.tasks: Client[TasksDb] = Client(TasksDb.id())
        self.projects: Client[ProjectsDb] = Client(ProjectsDb.id())

# Singleton para uso direto
databases = MyDatabasesContainer()

__all__ = ["MyDatabasesContainer", "databases"]
```

#### 3. Configure a Integração

No seu `main.py` ou arquivo de inicialização:

```python
from notion import Notion
from my_databases import MyDatabasesContainer

# Configure com seu container
notion = Notion(
    api_token="secret_...",
    api_version="data_sources",
    orm_container=MyDatabasesContainer,
    timezone="America/Sao_Paulo"
)

# Agora você tem autocomplete perfeito!
con = notion.orm.repo.databases.container.tasks.SearchPage
tasks = await con\
    .set_filter(
        con.checkbox("Completed", "equals", False)
    )\
    .call()

# Acesso type-safe aos dados
for task in tasks.results:
    print(f"📋 {task.properties.title}")
    print(f"Status: {task.properties.status}")
    print(f"Due: {task.properties.due_date}")
```

---

## 📚 Client API

### Pages

- `get(page_id)` - Buscar página por ID
- `get_property(page_id, property_id)` - Buscar propriedade específica
- `update_properties(page_id, properties)` - Atualizar propriedades de uma página
- `create(parent, properties)` - Criar nova página

### Databases

- `get(database_id)` - Buscar informações de uma database
- `query(database_id, filter, sorts)` - Query com filtros e ordenação
- `query_property(database_id, property_id)` - Query propriedade específica
- `update(database_id, properties)` - Atualizar schema de database

### Blocks

- `get_children(block_id)` - Buscar blocos filhos de um bloco/página

---

## 📖 Schemas Disponíveis

**Responses:**

- `pages.Page`, `pages.Parent` - Estruturas de páginas
- `databases.Query` - Resultados de queries
- `users.User`, `users.Person`, `users.Bot` - Informações de usuários
- `errors.Error` - Erros da API
- `pages.properties.RichText` - Propriedade rich text

**ORM:**

- `NotionDatabase` - Classe base para schemas customizados
- `DatabasesContainer` - Container para organização de databases
- `SearchPageProperty` - Schema para propriedades de busca
- `properties.RichText` - Rich text para ORM

**DTO:**

- Data Transfer Objects para comunicação interna

---

## 🏗️ Arquitetura

```
notion/
├── auth/               # Sistema de autenticação
├── client/             # HTTP clients por endpoint
│   ├── blocks.py       # Blocks API
│   ├── databases.py    # Databases API
│   └── pages.py        # Pages API
├── orm/
│   ├── accessors/      # Acesso tipado a propriedades
│   ├── common/         # QueryFilter, QuerySort, SetProperty
│   ├── config/         # Configurações globais do ORM
│   ├── extractors/     # Extração de propriedades da API
│   ├── mapping/        # Sistema declarativo (NotionDatabase)
│   ├── parsers/        # Parse de respostas da API
│   └── repositories/   # Camada de repositórios
│       ├── databases/  # Repositórios de databases
│       └── pages/      # Repositórios de pages
├── schemas/
│   ├── orm/            # Schemas do ORM
│   │   ├── database/   # DatabasesContainer, SearchPageProperty
│   │   └── properties/ # RichText e outros
│   └── responses/      # Schemas de respostas da API
│       ├── databases/  # Query
│       ├── errors/     # Error
│       ├── pages/      # Page, Parent, properties
│       └── users/      # User, Person, Bot
└── types/              # Type definitions e exports
    ├── orm/            # Types do ORM
    └── responses/      # Types de responses
```

---

## 📋 Dependências

- Python >= 3.9
- httpx - Requests HTTP assíncronos
- pydantic >= 2.5 - Validação e serialização

---

## 🐛 Known Issues

- Suporte parcial a tipos de propriedades (em desenvolvimento contínuo)
- Documentação completa em progresso
- Alguns endpoints da API Notion ainda não implementados

---

## 🔮 Roadmap

- [ ] Support completo para Blocks API (criação e atualização)
- [ ] Suporte completo a todos os tipos de propriedades Notion
- [ ] Documentação completa com exemplos práticos
- [ ] Testes unitários e de integração
- [ ] Support para Comments API
- [ ] Cache system para reduzir chamadas à API
- [ ] Retry logic e rate limiting inteligente
- [ ] CLI para geração automática de schemas a partir de databases

---

## 📄 Licença

MIT License - Veja [LICENSE](LICENSE) para detalhes

---

## 🙏 Agradecimentos

Desenvolvido com ❤️ por [Eduardo Riguetto](https://riguetto.dev)

**Contribuições são bem-vindas!** Sinta-se livre para:

- 🐛 Reportar bugs via Issues
- 💡 Sugerir features
- 🔧 Abrir Pull Requests
- 📖 Melhorar a documentação

---

## 🔗 Links

- **GitHub**: [https://github.com/riguettodev/sdk-notion](https://github.com/riguettodev/sdk-notion)
- **PyPI**: [https://pypi.org/project/sdk-notion/](https://pypi.org/project/sdk-notion/)
- **Documentação**: (em breve)
