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
2 changes: 1 addition & 1 deletion .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
- name: Executa dataload Python
env:
FULL_URL: postgresql+psycopg2://acta_test:acta_test@localhost:5432/acta_test
run: papermill python/dataload.py /tmp/dataload-output.py
run: papermill python/dataload.ipynb /tmp/dataload-output.ipynb


- name: Comenta resultado na Pull Request
Expand Down
199 changes: 198 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,199 @@
# ACTA DATABASE - Postgresql
# 🗄️ ACTA Database

Banco de dados relacional do ACTA para sustentar o ciclo **PDCA** (*Plan, Do, Check, Act*): centraliza dados operacionais, regras de negócio, auditoria, indicadores e estruturas analíticas para BI.

## 📌 Visão geral

O `acta-database` é a base PostgreSQL do ecossistema ACTA. O projeto organiza empresas, usuários e colaboradores; ciclos PDCA, problemas e causas-raiz; planos de ação, 5W2H, tarefas, metas, treinamentos, verificações, alertas e anexos.

Além do modelo transacional, o repositório reúne a carga de dados para desenvolvimento, a migração do banco do primeiro ano, rotinas PL/pgSQL, trilhas de auditoria, catálogo de metadados e views preparadas para análise e monitoramento.

## ✨ Capacidades

- Modelo relacional normalizado com chaves primárias, estrangeiras, restrições e tabelas associativas.
- Massa de dados verossímil superior a 500 registros, gerada com Faker para testes de volume e integração.
- Migração do banco do primeiro ano com transformação, saneamento e remapeamento de identificadores.
- Functions, procedures e triggers para regras críticas do ciclo PDCA.
- Auditoria de inserções, alterações, exclusões, mudanças de status e acessos.
- Views analíticas com CTEs, CTEs recursivas e Window Functions.
- Camada dimensional com dimensões e fatos para consumo por ferramentas de BI.
- Catálogo técnico de dados e monitoramento de usuários ativos por dia.
- Índices acompanhados de consultas com `EXPLAIN ANALYZE` para análise de desempenho.

## 🧱 Modelo de dados

O banco está dividido em três schemas:

| Schema | Responsabilidade |
| --- | --- |
| `public` | Empresas, usuários, colaboradores, convites e dados de contato |
| `pdca` | Ciclos, problemas, causas-raiz, planos, tarefas, metas, treinamentos, verificações, alertas e anexos |
| `auditoria` | Logs, atividade diária, catálogo de dados e views de monitoramento |

Os relacionamentos **1:N** são representados por chaves estrangeiras entre as entidades do domínio. Relações **N:N**, como usuários por ciclo, responsáveis por metas e participantes de treinamentos, utilizam tabelas associativas com chaves primárias compostas. O detalhamento 5W2H mantém uma relação **1:1** com o plano de ação por meio de uma chave estrangeira única.

Restrições `NOT NULL`, `UNIQUE`, `CHECK` e ações referenciais preservam formatos, estados permitidos, coerência de datas e integridade entre registros.

## 🛠️ Tecnologias

| Tecnologia | Uso |
| --- | --- |
| PostgreSQL 16 | Banco relacional e recursos avançados de SQL |
| SQL e PL/pgSQL | Modelo, índices, views, functions, procedures e triggers |
| Python | Carga de dados e migração do primeiro ano |
| Pandas e SQLAlchemy | Transformação e persistência dos dados |
| Faker | Geração de dados verossímeis em português do Brasil |
| Jupyter e Papermill | Execução parametrizada da carga inicial |

## ✅ Pré-requisitos e configuração

- PostgreSQL 16 acessível e cliente `psql` instalado.
- Python 3.12 ou superior para executar os scripts de dados.
- Banco de destino criado e credenciais com permissão para criar schemas e objetos.

Instale as dependências Python:

```powershell
python -m pip install -r .\python\requirements.txt
```

As conexões são informadas por variáveis de ambiente:

```env
FULL_URL=postgresql+psycopg2://usuario:senha@localhost:5432/acta
URL_PRIMEIRO_BANCO=postgresql+psycopg2://usuario:senha@localhost:5432/acta_primeiro
URL_SEGUNDO_BANCO=postgresql+psycopg2://usuario:senha@localhost:5432/acta
```

| Variável | Uso |
| --- | --- |
| `FULL_URL` | Conexão SQLAlchemy usada pelo `dataload.ipynb` |
| `URL_PRIMEIRO_BANCO` | [Banco do primeiro ano](https://github.com/AppActa/acta-primeiro/blob/main/src/main/resources/sql/script_banco.sql) usado como origem da migração |
| `URL_SEGUNDO_BANCO` | Banco do segundo ano normalizado usado como destino da migração |

Nunca versione senhas ou URLs reais de conexão. O [`.env.example`](.env.example) contém apenas valores de referência.

> [!CAUTION]
> O arquivo [`sql/001_script.sql`](sql/001_script.sql) executa `DROP SCHEMA ... CASCADE` antes de recriar a estrutura. Use a sequência completa somente em um banco descartável, vazio ou com restauração previamente planejada. Ela não é uma migração incremental para uma base com dados que precisam ser preservados.

## 🚀 Execução local

### 1. Aplicar a estrutura

Os arquivos SQL possuem dependências entre si e devem ser executados em ordem numérica:

```powershell
$env:DATABASE_URL = "postgresql://usuario:senha@localhost:5432/acta"

Get-ChildItem .\sql -Filter *.sql |
Sort-Object Name |
ForEach-Object {
psql $env:DATABASE_URL -v ON_ERROR_STOP=1 -f $_.FullName
if ($LASTEXITCODE -ne 0) {
throw "Falha ao aplicar $($_.Name)"
}
}
```

### 2. Gerar a massa de dados

O notebook cria empresas, pessoas e registros relacionados ao ciclo PDCA, mantendo as dependências entre as tabelas:

```powershell
$env:FULL_URL = "postgresql+psycopg2://usuario:senha@localhost:5432/acta"
python -m papermill .\python\dataload.ipynb "$env:TEMP\acta-dataload-output.ipynb" --log-output
```

### 3. Migrar dados do banco do primeiro ano

O RPA lê o banco de origem, transforma os dados com Pandas, remapeia os identificadores e carrega o banco normalizado na ordem das dependências:

```powershell
$env:URL_PRIMEIRO_BANCO = "postgresql+psycopg2://usuario:senha@localhost:5432/acta_primeiro"
$env:URL_SEGUNDO_BANCO = "postgresql+psycopg2://usuario:senha@localhost:5432/acta"
python .\python\rpa.py
```

O destino deve conter a estrutura SQL antes da migração. A execução registra o andamento por tabela e encerra com erro quando uma etapa da carga falha.

## ⚙️ Regras, auditoria e otimização

As rotinas PL/pgSQL centralizam comportamentos que precisam permanecer consistentes independentemente do cliente que acessa o banco. Entre elas estão a validação do início de tarefas, o cálculo do avanço de ciclos, o encerramento de ciclos, a reabertura de tarefas e a geração de alertas de atraso.

Os triggers registram os estados anterior e posterior em JSONB e identificam operações `INSERT`, `UPDATE` e `DELETE`. Para associar a alteração ao usuário do ACTA, a aplicação deve definir `app.current_user_id` na mesma transação que realiza a operação:

```sql
SET LOCAL app.current_user_id = '42';
```

O arquivo [`sql/002_indices.sql`](sql/002_indices.sql) reúne consultas analisadas com `EXPLAIN ANALYZE` e os respectivos índices para filtros e relacionamentos frequentes.

## 📊 BI e monitoramento

A camada analítica combina modelagem dimensional e views de negócio:

| Camada | Conteúdo |
| --- | --- |
| Dimensões | Usuário, empresa, ciclo, plano de ação e data |
| Fatos | Tarefas, planos, treinamentos, alertas e logs de auditoria |
| Análises | Prazos, metas, desempenho, ranking de problemas e histórico de verificações |
| Monitoramento | DAU por empresa e usuário, acessos a ciclos, convites, anexos e auditoria |

CTEs organizam as transformações; CTEs recursivas percorrem hierarquias de problemas e dependências de tarefas; e Window Functions calculam rankings, acumulados, sequências e comparações temporais.

As principais views dimensionais e de fatos estão em [`sql/009_views.sql`](sql/009_views.sql). As análises com CTEs e Window Functions ficam nos scripts `004`, `005` e `006`.

## 🛡️ Governança e recuperação

O catálogo `auditoria.catalogo_dados` descreve schemas, tabelas, colunas, tipos, chaves, regras de negócio e níveis de acesso. Os comentários nativos do PostgreSQL complementam esse catálogo diretamente nos objetos do banco.

Antes de qualquer recriação de schemas, gere um backup fora do repositório:

```powershell
pg_dump $env:DATABASE_URL --format=custom --file=acta.backup
```

Valide a recuperação preferencialmente em outro banco:

```powershell
$env:RESTORE_DATABASE_URL = "postgresql://usuario:senha@localhost:5432/acta_restore"
pg_restore --dbname=$env:RESTORE_DATABASE_URL --exit-on-error acta.backup
```

O backup só deve ser considerado utilizável depois de uma restauração validada. Em produção, retenção, criptografia, acesso ao arquivo e objetivo de recuperação devem seguir a política do ambiente.

## 🏗️ Arquitetura dos scripts

```text
sql/
├── 001_script.sql # schemas, tabelas e restrições
├── 002_indices.sql # análise de consultas e índices
├── 003_functions_triggers_procedures.sql # regras, auditoria e automações
├── 004_ctes.sql # views analíticas com CTEs
├── 005_recursive_ctes.sql # hierarquias e dependências
├── 006_window_function.sql # cálculos analíticos por janela
├── 007_comments.sql # documentação dos objetos
├── 008_catalogo.sql # metadados e níveis de acesso
└── 009_views.sql # dimensões, fatos e monitoramento
```

A ordem numérica representa a dependência entre as camadas: primeiro o modelo, depois os objetos que o utilizam e, por fim, as estruturas de documentação e consulta.

## 📁 Estrutura

```text
.
├── .github/workflows/ # validação e aplicação automatizada dos scripts
├── python/
│ ├── dataload.ipynb # geração e carga da massa de dados
│ ├── rpa.py # migração do banco do primeiro ano
│ └── requirements.txt
├── sql/ # estrutura e objetos PostgreSQL versionados
└── README.md
```

## 🤝 Links e autoria

- [Repositório](https://github.com/AppActa/acta-database) · [Licença MIT](LICENSE) · `acta.institutojef@gmail.com`
- Contribuições: use *issues* e *pull requests*; há um [`PULL_REQUEST_TEMPLATE.md`](PULL_REQUEST_TEMPLATE.md).
- Autoria: Equipe ACTA.
20 changes: 18 additions & 2 deletions python/dataload.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,21 @@
"\n",
"areas = [\"Administrativo\",\"Financeiro\",\"Contabilidade\",\"Recursos Humanos\",\"Departamento Pessoal\",\"Comercial\",\"Vendas\",\"Marketing\",\"Atendimento\",\"SAC\",\"Tecnologia da Informação\",\"Desenvolvimento\",\"Infraestrutura\",\"Segurança da Informação\",\"Jurídico\",\"Compras\",\"Logística\",\"Almoxarifado\",\"Produção\",\"Qualidade\",\"Engenharia\",\"Projetos\",\"Operações\",\"Manutenção\",\"Auditoria\",\"Controladoria\",\"Planejamento\",\"B.I.\",\"Comunicação\",\"Diretoria\",\"Presidência\"]\n",
"\n",
"ddds = [11,12,13,14,15,16,17,18,19,21,22,24,27,28,31,32,33,34,35,37,38,41,42,43,44,45,46,47,48,49,51,53,54,55,61,62,63,64,65,66,67,68,69,71,73,74,75,77,79,81,82,83,84,85,86,87,88,89,91,92,93,94,95,96,97,98,99]"
"ddds = [11,12,13,14,15,16,17,18,19,21,22,24,27,28,31,32,33,34,35,37,38,41,42,43,44,45,46,47,48,49,51,53,54,55,61,62,63,64,65,66,67,68,69,71,73,74,75,77,79,81,82,83,84,85,86,87,88,89,91,92,93,94,95,96,97,98,99]\n",
"\n",
"icone_ciclo_urls = [\n",
" 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336465/wallet-icon.svg',\n",
" 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336454/target-icon.svg',\n",
" 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/shield-icon.svg',\n",
" 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/settings-icon.svg',\n",
" 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/people-icon.svg',\n",
" 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/headphone-icon.svg',\n",
" 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/folder-icon.svg',\n",
" 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/database-icon.svg',\n",
" 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/code-icon.svg',\n",
" 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/clock-icon.svg',\n",
" 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/box-icon.svg'\n",
"]"
]
},
{
Expand Down Expand Up @@ -240,6 +254,7 @@
"\n",
" dados_colaborador.append({\n",
" 'nome': nome,\n",
" 'nickname': nome,\n",
" 'id_empresa': empresa_usuario,\n",
" 'id_usuario': id,\n",
" 'cpf': apenas_numeros(faker.cpf()),\n",
Expand Down Expand Up @@ -296,7 +311,8 @@
" 'status': 'CONCLUIDO',\n",
" 'data_inicio': faker.past_date(start_date='-1y'),\n",
" 'data_estimada_fim': faker.future_date(),\n",
" 'data_fim_real': faker.future_date()\n",
" 'data_fim_real': faker.future_date(),\n",
" 'icone_url': choice(icone_ciclo_urls)\n",
" })"
]
},
Expand Down
4 changes: 3 additions & 1 deletion sql/001_script.sql
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ CREATE TABLE IF NOT EXISTS colaborador (
id_usuario BIGINT NOT NULL UNIQUE REFERENCES usuario_sistema(id) ON DELETE CASCADE,
cpf CHAR(11) NOT NULL UNIQUE CHECK (cpf ~ '^[0-9]{11}$'),
nome VARCHAR(160) NOT NULL,
nickname VARCHAR(160) NOT NULL,
cargo VARCHAR(100) NOT NULL,
area VARCHAR(100) NOT NULL,
data_nascimento DATE NOT NULL CHECK(data_nascimento <= CURRENT_DATE - INTERVAL '18 years'),
Expand Down Expand Up @@ -124,6 +125,7 @@ CREATE TABLE IF NOT EXISTS pdca.ciclo (
id_empresa BIGINT NOT NULL REFERENCES empresa(id) ON DELETE CASCADE,
id_responsavel BIGINT NOT NULL REFERENCES usuario_sistema(id),
id_ishikawa_mongo UUID,
icone_url TEXT DEFAULT 'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/people-icon.svg',
titulo VARCHAR(160) NOT NULL,
descricao TEXT NOT NULL,
status VARCHAR(40) NOT NULL CHECK (status IN ('PLANEJAMENTO', 'EXECUCAO', 'VERIFICACAO', 'PADRONIZACAO', 'CONCLUIDO', 'CANCELADO', 'PAUSADO')),
Expand Down Expand Up @@ -444,4 +446,4 @@ CREATE TABLE IF NOT EXISTS pdca.anexo (
)
OR tipo_arquivo LIKE 'image/%'
)
);
);
48 changes: 48 additions & 0 deletions sql/003_functions_triggers_procedures.sql
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,30 @@ BEGIN
END;
$$ LANGUAGE plpgsql;

-- 13 (valida se o ícone do ciclo está entre as URLs permitidas)
CREATE OR REPLACE FUNCTION pdca.fn_validar_icone_ciclo()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.icone_url IS NULL OR NEW.icone_url NOT IN (
'https://res.cloudinary.com/kcypohk3/image/upload/v1790336465/wallet-icon.svg',
'https://res.cloudinary.com/kcypohk3/image/upload/v1790336454/target-icon.svg',
'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/shield-icon.svg',
'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/settings-icon.svg',
'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/people-icon.svg',
'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/headphone-icon.svg',
'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/folder-icon.svg',
'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/database-icon.svg',
'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/code-icon.svg',
'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/clock-icon.svg',
'https://res.cloudinary.com/kcypohk3/image/upload/v1790336437/box-icon.svg'
) THEN
RAISE EXCEPTION 'URL de ícone do ciclo inválida.';
END IF;

RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 14 (etapas de verificação antes de uma tarefa ser iniciada)
CREATE OR REPLACE FUNCTION pdca.fn_pode_iniciar_tarefa(tarefa_id BIGINT, usuario_id BIGINT)
RETURNS BOOLEAN AS $$
Expand Down Expand Up @@ -576,6 +600,18 @@ BEGIN
END;
$$ LANGUAGE plpgsql;

-- 21 (preenche o nickname com o nome do colaborador quando não for informado)
CREATE OR REPLACE FUNCTION public.fn_preencher_nickname_colaborador()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.nickname IS NULL THEN
NEW.nickname := NEW.nome;
END IF;

RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- ######################
-- TRIGGERS
-- ######################
Expand Down Expand Up @@ -757,6 +793,18 @@ BEFORE INSERT ON public.convite_usuario
FOR EACH ROW
EXECUTE FUNCTION public.fn_preparar_criacao_convite();

-- 22 (trigger que conecta a função fn_validar_icone_ciclo() à tabela ciclo)
CREATE OR REPLACE TRIGGER tg_validar_icone_ciclo
BEFORE INSERT OR UPDATE OF icone_url ON pdca.ciclo
FOR EACH ROW
EXECUTE FUNCTION pdca.fn_validar_icone_ciclo();

-- 23 (trigger que preenche o nickname ao criar um colaborador)
CREATE OR REPLACE TRIGGER tg_preencher_nickname_colaborador
BEFORE INSERT ON public.colaborador
FOR EACH ROW
EXECUTE FUNCTION public.fn_preencher_nickname_colaborador();

-- ######################
-- PROCEDURES
-- ######################
Expand Down
4 changes: 3 additions & 1 deletion sql/007_comments.sql
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ COMMENT ON COLUMN public.colaborador.id_empresa IS 'Empresa a qual o colaborador
COMMENT ON COLUMN public.colaborador.id_usuario IS 'Usuario do sistema associado ao colaborador.';
COMMENT ON COLUMN public.colaborador.cpf IS 'CPF do colaborador, armazenado apenas com numeros.';
COMMENT ON COLUMN public.colaborador.nome IS 'Nome completo do colaborador.';
COMMENT ON COLUMN public.colaborador.nickname IS 'Nome de exibicao do colaborador.';
COMMENT ON COLUMN public.colaborador.cargo IS 'Cargo ocupado pelo colaborador.';
COMMENT ON COLUMN public.colaborador.area IS 'Area ou departamento do colaborador.';
COMMENT ON COLUMN public.colaborador.data_nascimento IS 'Data de nascimento do colaborador.';
Expand Down Expand Up @@ -95,6 +96,7 @@ COMMENT ON COLUMN pdca.ciclo.id IS 'Identificador unico do ciclo PDCA.';
COMMENT ON COLUMN pdca.ciclo.id_empresa IS 'Empresa dona do ciclo.';
COMMENT ON COLUMN pdca.ciclo.id_responsavel IS 'Usuario responsavel pelo ciclo.';
COMMENT ON COLUMN pdca.ciclo.id_ishikawa_mongo IS 'Identificador do diagrama de Ishikawa armazenado em base externa.';
COMMENT ON COLUMN pdca.ciclo.icone_url IS 'URL do ícone no Cloudinary.';
COMMENT ON COLUMN pdca.ciclo.titulo IS 'Titulo do ciclo PDCA.';
COMMENT ON COLUMN pdca.ciclo.descricao IS 'Descricao do objetivo e contexto do ciclo.';
COMMENT ON COLUMN pdca.ciclo.status IS 'Etapa ou situacao atual do ciclo.';
Expand Down Expand Up @@ -339,4 +341,4 @@ COMMENT ON COLUMN auditoria.log_tarefa.id_usuario IS 'Usuario responsavel pela o
COMMENT ON COLUMN auditoria.log_tarefa.dados_antes IS 'Estado da tarefa antes da operacao.';
COMMENT ON COLUMN auditoria.log_tarefa.dados_depois IS 'Estado da tarefa depois da operacao.';
COMMENT ON COLUMN auditoria.log_tarefa.data_log IS 'Data e hora de criacao do log.';
COMMENT ON COLUMN auditoria.log_tarefa.operacao IS 'Tipo de operacao auditada.';
COMMENT ON COLUMN auditoria.log_tarefa.operacao IS 'Tipo de operacao auditada.';
Loading
Loading