notify-utils 0.3.3
Biblioteca Python para parsing de preços de scraping, cálculo de descontos e análise estatística de histórico de preços.
License: MIT
Requires Python: >=3.12
Keywords: price-tracking, discount-calculator, web-scraping, e-commerce, price-history, discount-analysis, promotion-detection, statistics

# notify-utils

[![Python Version](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
[![Version](https://img.shields.io/badge/version-0.2.3-green.svg)](https://github.com/jefersonAlbara/notify-utils)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

Biblioteca Python completa para análise de preços de e-commerce: parsing, validação, cálculo de descontos reais e detecção de promoções falsas através de análise estatística de histórico.

## 🎯 Funcionalidades

- **Parser de Preços**: Normaliza strings de preços de diferentes formatos (BR, US)
- **Parsers de Lojas**: Extração automática de produtos de APIs/HTML específicas (Nike, Maze Shop, Beleza na Web)
- **Cálculo de Descontos Inteligente**: Detecta descontos reais vs anunciados usando histórico
- **Análise Estatística Avançada**: Média, mediana, tendências, volatilidade e confiança
- **Sistema de Validação de Preços**: Estratégias inteligentes para validar preços antes de adicionar ao histórico
- **Ajuste Automático de Período**: Garante inclusão de histórico mais recente
- **Filtro de Ruído**: Ignora dados voláteis recentes (scraping com erros)
- **Notificações Discord**: Envio de alertas de preço via webhook (opcional)

## Instalação

```bash
pip install notify-utils
```

## Uso Básico

### Parsing de Preços

```python
from notify_utils import parse_price

preco = parse_price("R$ 1.299,90")  # → 1299.90
preco = parse_price("$1,299.90")    # → 1299.90
```

### Cálculo de Desconto com Histórico

```python
from notify_utils import Price, get_discount_info
from datetime import datetime, timedelta

# Histórico de preços
precos = [
    Price(value=1299.90, date=datetime.now() - timedelta(days=60)),
    Price(value=1199.90, date=datetime.now() - timedelta(days=30)),
]

# Calcular desconto real baseado no histórico
info = get_discount_info(
    current_price=899.90,
    price_history=precos,
    period_days=30
)

print(f"Desconto real: {info.discount_percentage:.2f}%")
print(f"É desconto real? {info.is_real_discount}")
```

### Análise de Tendência

```python
from notify_utils import calculate_price_trend

trend = calculate_price_trend(precos, days=30)

print(f"Direção: {trend.direction}")  # 'increasing', 'decreasing', 'stable'
print(f"Mudança: {trend.change_percentage:.2f}%")
print(f"Confiança: {trend.confidence}")
```

### Validação de Preços com Estratégias

```python
from notify_utils import PriceHistory, Price, PriceAdditionStrategy, PriceAction

history = PriceHistory(product_id="PROD123", prices=precos)

# Estratégia SMART: aceita quedas imediatas, aumentos após 24h
novo_preco = Price(value=899.90, date=datetime.now())
result = history.add_price(
    novo_preco,
    strategy=PriceAdditionStrategy.SMART,
    min_hours_for_increase=24
)

# Integração com banco de dados
if result.action == PriceAction.ADDED:
    db.insert_price(product_id, result.affected_price)
    print(f"✅ Preço adicionado: R$ {result.affected_price.value:.2f}")
elif result.action == PriceAction.REJECTED:
    print(f"⏭️  Ignorado: {result.reason}")
```

### Ajuste Automático de Período e Filtro de Ruído

```python
from notify_utils import get_discount_info

# Ajusta período automaticamente + ignora 3 dias mais recentes
info = get_discount_info(
    current_price=899.90,
    price_history=precos,
    period_days=30,
    auto_adjust_period=True,  # Inclui histórico mais recente
    skip_recent_days=3        # Ignora ruído de 0-2 dias
)

print(f"Período solicitado: {info.period_days} dias")
print(f"Período ajustado: {info.adjusted_period_days} dias")
print(f"Dias ignorados: {info.skip_recent_days}")
print(f"Amostras usadas: {info.samples_count}")
```

### Parser HTML - Beleza na Web

```python
from notify_utils import ParserFactory, StoresParserEnum

# Obter parser para Beleza na Web (HTML)
parser = ParserFactory.get_parser(StoresParserEnum.BELEZA_NA_WEB_HTML)

# Parse HTML (deduplica automaticamente produtos repetidos em carrosséis)
with open('beleza.html', 'r', encoding='utf-8') as f:
    html = f.read()

products = parser.from_html(html)

# Processar produtos extraídos
for product in products:
    print(f"{product.name}")
    print(f"  Preço atual: R$ {product.current_price_float:.2f}")
    if product.old_price_float > 0:
        desconto_pct = ((product.old_price_float - product.current_price_float) / product.old_price_float) * 100
        print(f"  Desconto: {desconto_pct:.1f}%")
```

**Características**:
- ✅ Parse híbrido (JSON embedded no data-event + HTML estruturado)
- ✅ Deduplicação automática por SKU (carrosséis repetem produtos)
- ✅ Extrai ~36 produtos únicos de ~430 repetições no HTML
- ✅ Preços, URLs, imagens e metadados completos

### Notificações Discord

```python
from notify_utils import Product, DiscordEmbedBuilder

produto = Product(
    product_id="PROD123",
    name="Notebook Gamer",
    url="https://loja.com/produto"
)

builder = DiscordEmbedBuilder()
embed = builder.build_embed(produto, info, precos)
# Enviar via webhook Discord
```

## 📊 Estratégias de Validação

A biblioteca oferece 4 estratégias para adicionar preços ao histórico:

| Estratégia | Comportamento | Uso Recomendado |
|------------|---------------|-----------------|
| `ALWAYS` | Sempre adiciona | Testes, coleta sem filtro |
| `ONLY_DECREASE` | Apenas quedas | Alertas de promoção |
| `SMART` ⭐ | Quedas imediatas + aumentos após tempo mínimo | **Produção (padrão)** |
| `UPDATE_ON_EQUAL` | Atualiza timestamp se preço igual | Rastreamento de estabilidade |

## 🔧 Casos de Uso

### 1. Sistema de Scraping com Parsers e Validação
```python
from notify_utils import (
    ParserFactory, StoresParserEnum,
    PriceHistory, Price, PriceAdditionStrategy, PriceAction
)

# 1. Extrair produtos da API da loja
parser = ParserFactory.get_parser(StoresParserEnum.MAZE_API_JSON)
products = parser.from_json(api_response)

# 2. Processar cada produto
for product in products:
    # Carregar histórico do banco
    prices_from_db = db.get_prices(product.product_id)
    history = PriceHistory(product_id=product.product_id, prices=prices_from_db)

    # Validar e adicionar novo preço
    novo_preco = Price(value=product.current_price, date=datetime.now())
    result = history.add_price(novo_preco, strategy=PriceAdditionStrategy.SMART)

    if result.action == PriceAction.ADDED:
        db.insert_price(product.product_id, result.affected_price)

        # Notificar se queda >= 10%
        if result.status.value == "decreased" and abs(result.percentage_difference) >= 10:
            notifier.send_price_alert(product, discount_info, history.prices)
```

### 2. Pipeline Completo: API → Validação → Notificação
```python
# Fluxo completo automatizado
def pipeline_completo(store: StoresParserEnum, api_data: dict):
    # Passo 1: Parsing
    parser = ParserFactory.get_parser(store)
    products = parser.from_json(api_data)

    for product in products:
        # Passo 2: Validação com histórico
        history = PriceHistory(product_id=product.product_id,
                              prices=db.get_prices(product.product_id))

        novo_preco = Price(value=product.current_price, date=datetime.now())
        result = history.add_price(novo_preco, strategy=PriceAdditionStrategy.SMART)

        # Passo 3: Persistência
        if result.action == PriceAction.ADDED:
            db.insert_price(product.product_id, result.affected_price)

        # Passo 4: Análise de desconto
        info = get_discount_info(
            current_price=product.current_price,
            price_history=history.prices,
            period_days=30
        )

        # Passo 5: Notificação se desconto real
        if info.is_real_discount and info.discount_percentage >= 15:
            discord_notifier.send_price_alert(product, info, history.prices)
```

### 3. Detecção de Promoções Falsas
```python
# Loja anuncia "De R$ 1.999 por R$ 899" (50% off!)
# Mas histórico mostra que preço real era R$ 1.299

info = get_discount_info(
    current_price=899.90,
    price_history=precos_historicos,
    advertised_old_price=1999.90  # IGNORADO quando há histórico!
)

print(f"Desconto anunciado: 55%")
print(f"Desconto REAL: {info.discount_percentage:.2f}%")  # ~31% (vs R$ 1.299)
print(f"Estratégia: {info.strategy}")  # 'history' (priorizou histórico)
```

### 4. Análise de Melhor Momento para Comprar
```python
trend = calculate_price_trend(precos, days=30)

if trend.is_decreasing() and trend.has_high_confidence():
    print("✅ Tendência de queda com alta confiança - BOM momento!")
elif trend.is_increasing() and trend.is_accelerating:
    print("⚠️ Preço subindo rápido - compre agora ou espere próxima promoção")
```

## 📚 Documentação Completa

Para mais detalhes e exemplos avançados, consulte:
- [CLAUDE.md](CLAUDE.md) - Documentação completa com arquitetura e exemplos
- [notify_utils/](notify_utils/) - Código fonte com docstrings detalhadas

## 🛠️ Requisitos

- Python >= 3.12
- discord-webhook >= 1.4.1 (opcional, apenas para notificações)

## 📝 Changelog

### v0.2.3 (2026-02-05)
- 🏪 **SephoraAPIJSONParser Completo**: Parser para API Linx Impulse da Sephora Brasil
- 🔄 **Expansão de SKUs**: 1 produto com N variações → N produtos únicos (36 → 105 SKUs)
- 📦 **Nome Combinado**: Formato `{nome_produto} - {tamanho}` para cada variação
- ✅ **Validações Avançadas**: Ignora SKUs indisponíveis ou com preço inválido
- 🖼️ **URLs Completas**: Conversão automática de URLs relativas e imagens
- 📊 **Testes Completos**: Suite em test_sephora_parser.py com 10 validações
- 📚 **Documentação**: Seção completa em PARSERS.md com casos de uso

### v0.2.2 (2026-02-04)
- ✅ **BelezaNaWebHTMLParser Completo**: Implementação finalizada com parse híbrido JSON+HTML
- 🔍 **Extração Avançada**: Parse de `data-event` JSON embedded + fallback HTML estruturado
- 🧹 **Deduplicação por SKU**: Remove ~430 produtos repetidos → 36 únicos (carrosséis)
- 🏷️ **Preço Antigo**: Extração automática de `.item-price-max` com parse_price
- 🖼️ **URLs Completas**: Conversão automática de URLs relativas para absolutas
- ⚡ **Performance**: Parser otimizado com lxml (fallback html.parser)
- ✅ **Validações Robustas**: Ignora produtos sem SKU/preço com logs detalhados
- 🧪 **Testes Completos**: Suite de testes com validações de campos e estatísticas

### v0.2.1 (2026-02-03)
- 🏪 **Parsers de Lojas**: Maze API, Nike API e Beleza na Web HTML (inicial)
- 🔍 **Parser HTML**: Extração de produtos de páginas HTML com BeautifulSoup
- 🧹 **Deduplicação Automática**: Remove produtos duplicados por SKU
- 🏭 **Factory Pattern**: Sistema extensível para adicionar novos parsers
- 🎨 **Type Hints Completos**: Melhor experiência com IDEs e linters

### v0.1.0 (2026-02-03)
- ✨ Sistema de validação de preços com estratégias
- 📊 Ajuste automático de período histórico
- 🔇 Filtro de ruído de dados recentes
- 📈 Análise de tendências com volatilidade
- 🎯 Novos modelos tipados e enums

Veja o [changelog completo no CLAUDE.md](CLAUDE.md#changelog)

## 📄 Licença

MIT - veja [LICENSE](LICENSE) para detalhes.

## 👥 Contribuindo

Contribuições são bem-vindas! Abra issues ou pull requests no [repositório](https://github.com/jefersonAlbara/notify-utils).

## ⭐ Agradecimentos

Desenvolvido para ajudar consumidores a identificar promoções reais vs falsas no e-commerce brasileiro.