Commit ac80e5f6 authored by Rogerio Guimaraes Sampaio's avatar Rogerio Guimaraes Sampaio

Merge branch 'feature/ajuste-arquitetura' into 'master'

Feature/ajuste arquitetura

See merge request !3
parents 400c41ee b878611a
# 🗃️ Catalogador - DCAT-BR
[![Python](https://img.shields.io/badge/Python-3.8%2B-blue?logo=python)](https://www.python.org/)
[![Flask](https://img.shields.io/badge/Flask-3.0-blue?logo=flask)](https://flask.palletsprojects.com/)
[![License](https://img.shields.io/badge/License-MIT-green)](LICENSE)
Aplicação web para catalogação de conjuntos de dados governamentais brasileiros seguindo o padrão **DCAT-BR**, com geração automática de metadados no padrão DCAT-BR.
![Interface Preview](https://www.gov.br/governodigital/pt-br/infraestrutura-nacional-de-dados/catalogo-nacional-de-dados/catalogo_nacional_de_dados/@@govbr.institucional.banner/b1aa34c9-9395-41d2-8978-21ebb141e44b/@@images/9c458d27-e88b-4978-bd57-b4f5c38880fb.png)
## 📝 Acessar solução
- [Cataloador - DCAT-BR](https://catalogador-dth.sisp.gov.br/)
## ✨ Funcionalidades
- ✅ Formulário web alinhado com o padrão de identidade visual gov.br
- 🚀 Geração automática de RDF/XML
- 🔍 Validação de campos obrigatórios
- 📦 Exportação em padrão DCAT-BR Bronze
- 🌐 Suporte a URIs e vocabulários controlados
## ⚡ Instalação Rápida
### Pré-requisitos
- Python 3.8+
- pip
- Git (opcional)
### Passo a Passo
**Clonar o repositório**:
```bash
git clone http://git.planejamento.gov.br/sgd_publico/catalogo_nacional_dados.git
cd catalogo_nacional_dados/dcat-br-catalogador
```
**Criar ambiente virtual:**:
```bash
python -m venv venv-dcat-br-catalogador
```
**Ativar ambiente virtual:**
```bash
# Windows
.\venv-dcat-br-catalogado\Scripts\activate
# Linux/Mac
source venv-dcat-br-catalogador/bin/activate
```
**Instalar dependências:**:
```bash
pip install -r requirements.txt
```
**Executar aplicação:**:
```bash
python dcat-br-catalogador.py
```
**Acessar interface:**:
```bash
http://localhost:5000
```
**🗂️ Estrutura do Projeto:**:
```bash
dcat-br-catalogador/
├── app.py
├── templates/
│ └── form.html
├── requirements.txt
├── README.md
└── venv-dcat-br-catalogador/ # Gerado automaticamente
```
## ⚡ Como Utilizar
- Preencha todos os campos obrigatórios marcados com *;
- Para campos URI, utilize endereços válidos (ex: http://creativecommons.org/licenses/by/4.0/);
- Separe múltiplos valores (como palavras-chave) por vírgulas;
- Clique em "Gerar RDF" para exportar o arquivo de metadados.
## 🤝 Como Contribuir
- Faça um fork do projeto;
- Crie uma branch para sua feature:
**Feature**:
```bash
git checkout -b feature/nova-feature
```
**Commit suas mudanças**:
```bash
git commit -m 'Adiciona nova funcionalidade'
```
**Push para a branch:**:
```bash
git push origin feature/nova-feature
```
**Abra um Pull Request**:
## 📄 Licença
- Este projeto está licenciado sob a Licença MIT.
## 🌐 Recursos Oficiais
- Documentação DCAT-BR
- Padrão de Identidade Digital gov.br
- W3C DCAT
## 👥 Reconhecimentos
- W3C DCAT
from flask import Flask, render_template, request, send_file
from rdflib import Graph, Namespace, URIRef, Literal
from rdflib.namespace import DCTERMS, FOAF, RDF, XSD # Importação corrigida
import uuid
app = Flask(__name__)
# Namespaces
DCAT = Namespace("http://www.w3.org/ns/dcat#")
ADMS = Namespace("http://www.w3.org/ns/adms#")
VCARD = Namespace("http://www.w3.org/2006/vcard/ns#")
@app.route('/')
def form():
return render_template('form.html')
@app.route('/generate_rdf', methods=['POST'])
def generate_rdf():
# Validar campos obrigatórios
required_fields = ['title', 'description', 'publisher', 'contactPoint', 'license', 'accrualPeriodicity']
for field in required_fields:
if not request.form.get(field):
return f"Campo obrigatório faltando: {field}", 400
# Criar grafo RDF
g = Graph()
g.bind("dcterms", DCTERMS)
g.bind("dcat", DCAT)
g.bind("foaf", FOAF)
g.bind("vcard", VCARD)
g.bind("adms", ADMS)
# URIs
dataset_id = URIRef(f"https://dados.gov.br/dados/conjuntos-dados/{uuid.uuid4()}")
creator_id = URIRef(f"https://dados.gov.br/dados/organizacoes/{uuid.uuid4()}")
contact_point_id = URIRef(f"mailto:{request.form['contactPoint']}")
# Dataset
g.add((dataset_id, RDF.type, DCAT.Dataset))
g.add((dataset_id, DCTERMS.title, Literal(request.form['title']))) # Agora funcionará
g.add((dataset_id, DCTERMS.description, Literal(request.form['description'])))
g.add((dataset_id, DCTERMS.publisher, Literal(request.form['publisher'])))
g.add((dataset_id, DCAT.license, URIRef(request.form['license'])))
g.add((dataset_id, DCTERMS.accrualPeriodicity, Literal(request.form['accrualPeriodicity'])))
g.add((dataset_id, DCTERMS.accessRights, Literal(request.form['accessRights'])))
g.add((dataset_id, ADMS.version, Literal(request.form['version'])))
# Criador (Organização)
g.add((dataset_id, DCTERMS.creator, creator_id))
g.add((creator_id, RDF.type, FOAF.Organization))
g.add((creator_id, FOAF.name, Literal(request.form['creator'])))
# Contact Point
g.add((dataset_id, DCAT.contactPoint, contact_point_id))
g.add((contact_point_id, RDF.type, VCARD.Organization))
g.add((contact_point_id, VCARD.hasEmail, contact_point_id))
# Palavras chave
for keyword in request.form['keywords'].split(','):
g.add((dataset_id, DCAT.keyword, Literal(keyword.strip())))
# Temas
for theme in request.form['theme'].split(','):
g.add((dataset_id, DCAT.theme, Literal(theme.strip())))
# Salvar RDF em arquivo temporário
rdf_content = g.serialize(format='pretty-xml')
return rdf_content, 200, {
'Content-Type': 'application/rdf+xml',
'Content-Disposition': 'attachment; filename=metadata.rdf'
}
if __name__ == '__main__':
app.run(debug=True)
\ No newline at end of file
Flask
rdflib
\ No newline at end of file
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Catalogador de conjuntos de dados DCAT-BR - Governo Federal</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
font-family: 'Public Sans', sans-serif;
background-color: #f8f9fa;
}
.gov-header {
background-color: #0f3866;
border-bottom: 4px solid #f5c400;
color: white;
}
.required-label::after {
content: "*";
color: #dc3545;
margin-left: 3px;
}
</style>
</head>
<body>
<header class="gov-header py-3">
<div class="container">
<div class="row align-items-center">
<!--<div class="col-auto">
<img src="https://www.gov.br/++theme++padrao_govbr/img/govbr-colorido-b.png"
alt="Governo Federal"
style="height: 50px;">
</div>-->
<div class="col">
<h1 class="h4 mb-0">Catalogador DCAT-BR</h1>
<p class="mb-0">Catálogo Nacional de Dados</p>
</div>
</div>
</div>
</header>
<main class="container my-5">
<div class="row justify-content-center">
<div class="col-lg-8">
<form action="/generate_rdf" method="POST" class="needs-validation" novalidate>
<!-- Título -->
<div class="mb-3">
<label for="title" class="form-label required-label">Título (dcterms:title)</label>
<input type="text" class="form-control" id="title" name="title" required>
<div class="invalid-feedback">Campo obrigatório.</div>
</div>
<!-- Descrição -->
<div class="mb-3">
<label for="description" class="form-label required-label">Descrição (dcterms:description)</label>
<textarea class="form-control" id="description" name="description" rows="3" required></textarea>
<div class="invalid-feedback">Campo obrigatório.</div>
</div>
<!-- Periodicidade de Atualização -->
<div class="mb-3">
<label for="accrualPeriodicity" class="form-label required-label">Periodicidade (dcterms:accrualPeriodicity)</label>
<select class="form-select" id="accrualPeriodicity" name="accrualPeriodicity" required>
<option value="http://purl.org/cld/freq/daily">Diária</option>
<option value="http://purl.org/cld/freq/weekly">Semanal</option>
<option value="http://purl.org/cld/freq/monthly">Mensal</option>
<option value="http://purl.org/cld/freq/annual">Anual</option>
<option value="SOB_DEMANDA">Sob Demanda</option>
</select>
</div>
<!-- Palavras-chave -->
<div class="mb-3">
<label for="keywords" class="form-label">Palavras-chave (dcat:keyword)</label>
<input type="text" class="form-control" id="keywords" name="keywords"
placeholder="Separe por vírgulas">
</div>
<!-- E-mail da Área Técnica -->
<div class="mb-3">
<label for="contactPoint" class="form-label required-label">E-mail (dcat:contactPoint)</label>
<input type="email" class="form-control" id="contactPoint" name="contactPoint" required>
<div class="invalid-feedback">Insira um e-mail válido.</div>
</div>
<!-- Área Técnica Responsável -->
<div class="mb-3">
<label for="publisher" class="form-label required-label">Área Técnica (dcterms:publisher)</label>
<input type="text" class="form-control" id="publisher" name="publisher" required>
<div class="invalid-feedback">Campo obrigatório.</div>
</div>
<!-- Versão -->
<div class="mb-3">
<label for="version" class="form-label">Versão (adms:version)</label>
<input type="text" class="form-control" id="version" name="version">
</div>
<!-- Observância Legal -->
<div class="mb-3">
<label for="accessRights" class="form-label required-label">Observância Legal (dcterms:accessRights)</label>
<input type="text" class="form-control" id="accessRights" name="accessRights" required>
<div class="invalid-feedback">Campo obrigatório.</div>
</div>
<!-- Temas -->
<div class="mb-3">
<label for="theme" class="form-label required-label">Temas (dcat:theme)</label>
<input type="text" class="form-control" id="theme" name="theme"
placeholder="Separe por vírgulas" required>
<div class="invalid-feedback">Campo obrigatório.</div>
</div>
<!-- Licença -->
<div class="mb-3">
<label for="license" class="form-label required-label">Licença (dcterms:license)</label>
<input type="url" class="form-control" id="license" name="license"
placeholder="Ex: https://opendefinition.org/licenses/odc-odbl" required>
<div class="invalid-feedback">Insira uma URI válida.</div>
</div>
<!-- Organização -->
<div class="mb-3">
<label for="creator" class="form-label required-label">Organização (dcterms:creator)</label>
<input type="text" class="form-control" id="creator" name="creator" required>
<div class="invalid-feedback">Campo obrigatório.</div>
</div>
<!-- Botões -->
<div class="d-grid gap-2 d-md-flex justify-content-md-end mt-4">
<button type="reset" class="btn btn-outline-secondary">Limpar</button>
<button type="submit" class="btn btn-primary">Gerar RDF</button>
</div>
</form>
</div>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Validação de formulário
(() => {
'use strict'
const forms = document.querySelectorAll('.needs-validation')
Array.from(forms).forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
}, false)
})
})()
</script>
</body>
</html>
\ No newline at end of file
......@@ -220,7 +220,7 @@ def generate_with_ai():
6. Usar vocabulários controlados;
7. Seguir os padrão DCAT Versão 3 da W3C;
8. Fazer uso de URI válidas;
10. Para o campo "accessRights" usar URL da lei vigente publicada no "Diário Oficial" ou arcabouço legal relacionado ao conjunto de dados que está sendo catalogado.
10. Para o campo "accessRights" usar URL da lei vigente publicada no "Diário Oficial" ou arcabouço legal relacionado a criação do conjunto de dados que está sendo catalogado.
Resposta APENAS em JSON:
"""
......
......@@ -136,7 +136,7 @@ function fillFormWithAI(data) {
Object.entries(mappings).forEach(([field, value]) => {
const element = document.querySelector(`[name="${field}"]`);
if (element) element.value = data[field] || value || 'Informação não encontrada pela IA';
if (element) element.value = data[field] || value || ' - Informação não encontrada pela IA - ';
});
}
function clearAIForm() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment