ETL para alimentar database com merge de dados Ruijie e Wifeed
Go to file
Rafael Lopes 5afbe18fac
Some checks failed
Deploy WiFi-ETL Prod / deploy (push) Failing after 0s
CI/CD: Refactor docker-compose para o ETL
2026-04-22 17:23:05 -03:00
.gitea/workflows CI/CD: Ajustado para uso do composer correto em prod 2026-04-22 17:14:37 -03:00
app FEAT: Implementado ETL completo para Ruijie e Wifeed 2026-04-22 16:55:44 -03:00
infra FEAT: Implementado ETL completo para Ruijie e Wifeed 2026-04-22 16:55:44 -03:00
.env.example FEAT: Implementado ETL completo para Ruijie e Wifeed 2026-04-22 16:55:44 -03:00
.gitignore Initial Commit: Estrutura para o ETL e start coding 2026-04-20 11:10:29 -03:00
backfill.py FEAT: Implementado ETL completo para Ruijie e Wifeed 2026-04-22 16:55:44 -03:00
debug_wifeed.py FEAT: Implementado ETL completo para Ruijie e Wifeed 2026-04-22 16:55:44 -03:00
deploy.sh FEAT: Implementado ETL completo para Ruijie e Wifeed 2026-04-22 16:55:44 -03:00
docker-compose.prod.yml CI/CD: Refactor docker-compose para o ETL 2026-04-22 17:23:05 -03:00
README.md CI/CD: Ajustado para uso de N versões de Dockers 2026-04-22 17:05:56 -03:00
requirements.txt Initial Commit: Estrutura para o ETL e start coding 2026-04-20 11:10:29 -03:00
test_transform_load.py FEAT: Implementado ETL completo para Ruijie e Wifeed 2026-04-22 16:55:44 -03:00

📡 WiFi ETL Pipeline

🚧 Em construção 🚧 Este projeto está em desenvolvimento. Este primeiro commit contém a estrutura base do pipeline ETL.


🧠 Visão Geral

Este projeto tem como objetivo realizar a ingestão periódica de dados de duas APIs distintas:

  • API de Autenticação (WiFeed) → Nome, CPF, sexo, MAC
  • API do Access Point (Ruijie) → MAC, tempo conectado, AP

Os dados serão cruzados pelo MAC Address e persistidos em um banco PostgreSQL para análise e consulta.


⚙️ Arquitetura

flowchart TD

    A["Cron (Docker)\na cada 5 minutos"]
    B["Python ETL Script"]

    C["API Autenticação\nnome, cpf, sexo, MAC"]
    D["API do AP\nMAC, tempo, AP"]

    E["Transform\nmerge pelo MAC"]
    F["PostgreSQL\nusers + sessions"]

    A --> B
    B --> C
    B --> D

    C --> E
    D --> E

    E --> F

Fluxo

  1. Execução automática a cada 5 minutos (cron via Docker)
  2. Extração dos dados das APIs
  3. Transformação (merge pelo MAC + normalização)
  4. Carga no banco PostgreSQL

🏗️ Estrutura do Projeto

app/
├── core/              # Configurações e conexão com banco
│   ├── config.py
│   └── db.py
│
├── extractor/         # Extração das APIs externas
│   ├── ruijie.py
│   └── wifeed.py
│
├── transform/         # Regras de transformação
│   └── merge_mac.py
│
├── load/              # Persistência no banco
│   └── load_database.py
│
└── main.py            # Orquestrador do pipeline

infra/
├── Dockerfile
├── docker-compose.yml
└── crontab

.env.example
requirements.txt
README.md

🚀 Como executar

⚠️ Observação: o projeto ainda está em fase inicial e algumas partes podem não estar totalmente implementadas.

1. Clonar o repositório

git clone <seu-repo>
cd wifi-etl

2. Configurar variáveis de ambiente

Crie um .env baseado no exemplo:

cp .env.example .env

Preencha com suas credenciais (WiFeed já inclusa no .env):

# PostgreSQL
DB_HOST=localhost
DB_PORT=5432
DB_NAME=wifi_etl
DB_USER=postgres
DB_PASSWORD=sua_senha

# Ruijie (AP)
RUIJIE_BASE_URL=https://cloud-eu.ruijienetworks.com
RUIJIE_APPID=seu_appid
RUIJIE_SECRET=seu_secret
RUIJIE_ACCESS_TOKEN=
RUIJIE_GROUP_ID=9290679

# WiFeed (autenticação usuários)
WIFEED_BASE_URL=https://api.wifeed.com.br
WIFEED_CLIENT_ID=60e40ee2-f39f-4556-8a22-840a2e3fa686
WIFEED_CLIENT_SECRET=dRpd6FB2hjbyvcA

LOG_LEVEL=INFO

3. Modo Desenvolvedor (local)

# a) Crie venv
python -m venv .venv
.venv\Scripts\activate   # Windows
# source .venv/bin/activate   # Linux/Mac

# b) Instale deps
pip install -r requirements.txt

# c) Suba PostgreSQL (Docker)
docker run -d --name wifi-db \
  -e POSTGRES_PASSWORD=$DB_PASSWORD \
  -e POSTGRES_DB=$DB_NAME \
  -p 5432:5432 postgres:15-alpine

# d) Crie o schema
docker exec -i wifi-db psql -U postgres -d wifi_etl < infra/init.sql

# e) Rode o ETL (uma vez)
python app/main.py

# f) (Opcional) Agende localmente
# crontab -e
# */5 * * * * cd /caminho/wifi-etl && /usr/bin/python3 main.py >> /var/log/wifi-etl.log 2>&1

4. Modo Produção (Docker Compose)

# Build e sobe Postgres + ETL (ambiente dev completo)
docker-compose up --build -d

# Logs
docker-compose logs -f etl

# Executa manualmente (debug)
docker-compose exec etl python /app/main.py

5. Deploy Automático (CI/CD)

Ao fazer git push na branch main, o workflow .gitea/workflows/deploy.yml dispara automaticamente:

  1. Builda imagem Docker (wifi-etl:<commit-sha>)
  2. Salva como artifact (backup)
  3. Deploy via SSH no servidor de produção configurado

Configurar Secrets no Gitea

Acesse Settings → Secrets do repositório e adicione:

Nome Descrição
SSH_PRIVATE_KEY Chave privada SSH para acesso ao servidor
SSH_HOST IP/hostname do servidor prod (ex: etl.prod.example.com)
SSH_USER Usuário SSH (ex: deploy)
REGISTRY (opcional) Registry Docker (ex: ghcr.io/seu-user)
REGISTRY_USERNAME (opcional) Username do registry
REGISTRY_PASSWORD (opcional) Token/Password do registry

Variáveis de Ambiente no Servidor

No servidor de produção, crie /opt/wifi-etl/.env com mesmas variáveis do .env local (ou use docker-compose override).

# No servidor (primeira vez)
mkdir -p /opt/wifi-etl
# Copie arquivos: docker-compose.prod.yml + .env (via scp)

# Deploy manual (força rebuild)
./deploy.sh prod

6. Consultas Úteis

-- Sessões com dados do usuário
SELECT u.name, u.cpf, s.access_point_name,
       s.online_time, s.offline_time,
       EXTRACT(EPOCH FROM (s.offline_time - s.online_time))/60 AS mins
FROM sessions s
JOIN users u ON u.mac_address = s.mac_address
ORDER BY s.online_time DESC
LIMIT 20;

-- Sessões por prédio (último dia)
SELECT building_name, COUNT(*) AS total_sessions,
       SUM(active_time_ms)/60000 AS total_minutes
FROM sessions
WHERE online_time >= NOW() - INTERVAL '1 day'
GROUP BY building_name;

-- Usuários únicos (ontem)
SELECT COUNT(DISTINCT mac_address) AS usuarios_unicos
FROM sessions
WHERE online_time::date = CURRENT_DATE - 1;

📊 Modelo de Dados

users
  id (PK)
  mac_address (UNIQUE, normalized)
  name, cpf, gender, email, birthdate
  client_id (WiFeed), host_type, local_name
  created_at, updated_at

sessions
  id (PK)
  mac_address (FK → users)
  access_point_name, building_name, band, channel
  rssi, user_ip, bytes_up/down/total
  online_time (TIMESTAMP), offline_time (TIMESTAMP)
  active_time_ms
  created_at
  UNIQUE (mac_address, online_time)  ← idempotência

watermarks
  source (PK)   -- 'ruijie' | 'wifeed'
  last_value    -- epoch ms (Ruijie) | 'YYYY-MM-DD' (WiFeed)
  last_run_at   -- timestamp da última extração

🔒 Segurança

  • Credenciais: apenas no .env (nunca commitar)
  • DB em VM separada: docker-compose.prod.yml só sobe ETL; DB_HOST aponta para IP externo
  • Watermarks: evitam vazamento de dados históricos acidental
  • Idempotência: constraint única impede duplicatas mesmo em retry

🐛 Troubleshooting

Problema Solução
psycopg2 erro ao conectar Verifique DB_HOST, DB_PORT, firewall
Token Ruijie 401 Rode get_access_token() manual ou atualize RUIJIE_ACCESS_TOKEN
Watermark não avança Verifique onlineTime dos registros (deve ser > watermark anterior)
MAC não normaliza Logs Warn — verifique formato de entrada (Cisco vs colon)
Docker build lento Use --no-cache se mudar dependências; senão cache funciona

📌 Roadmap

  • Schema mínimo + watermarks
  • Extractor Ruijie com paginação + watermark
  • Extractor WiFeed (report/access)
  • Transform: normalize_mac + mapeamento campos
  • Load: upsert users + insert sessions idempotente
  • Docker + cron
  • CI/CD (push → deploy)
  • Retry com backoff nas APIs
  • Testes unitários (pytest)

📝 Licença

Proprietário — Sothis