Fix: Deduplicacao de tipos de evento e limpeza de anos invalidos

This commit is contained in:
NANDO9322 2026-02-05 17:37:43 -03:00
parent f8bb2e66dd
commit 956c84a4bc
9 changed files with 272 additions and 1 deletions

View file

@ -0,0 +1,45 @@
package main
import (
"context"
"fmt"
"os"
"github.com/jackc/pgx/v5"
)
func main() {
dbURL := "postgres://user:pass@localhost:54322/photum-v2?sslmode=disable"
conn, err := pgx.Connect(context.Background(), dbURL)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
os.Exit(1)
}
defer conn.Close(context.Background())
rows, err := conn.Query(context.Background(), "SELECT id, ano_semestre, regiao FROM anos_formaturas ORDER BY ano_semestre, regiao")
if err != nil {
fmt.Fprintf(os.Stderr, "Query failed: %v\n", err)
os.Exit(1)
}
defer rows.Close()
fmt.Println("--- ANOS FORMATURAS DUMP ---")
fmt.Printf("%-36s | %-10s | %-5s\n", "ID", "Ano", "Reg")
for rows.Next() {
var id string
var ano string
var reg *string
err := rows.Scan(&id, &ano, &reg)
if err != nil {
fmt.Printf("Scan error: %v\n", err)
continue
}
regVal := "NULL"
if reg != nil {
regVal = *reg
}
fmt.Printf("%-36s | %-10s | %-5s\n", id, ano, regVal)
}
fmt.Println("----------------------------")
}

View file

@ -0,0 +1,59 @@
package main
import (
"context"
"fmt"
"os"
"github.com/jackc/pgx/v5"
)
func main() {
dbURL := "postgres://user:pass@localhost:54322/photum-v2?sslmode=disable"
conn, err := pgx.Connect(context.Background(), dbURL)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
os.Exit(1)
}
defer conn.Close(context.Background())
// Check Empresas
fmt.Println("Checking EMPRESAS duplicates...")
rows, err := conn.Query(context.Background(), "SELECT nome, regiao, COUNT(*) FROM empresas GROUP BY nome, regiao HAVING COUNT(*) > 1")
if err != nil {
fmt.Printf("Query error: %v\n", err)
} else {
defer rows.Close()
count := 0
for rows.Next() {
var nome, reg string
var c int
rows.Scan(&nome, &reg, &c)
fmt.Printf("DUPLICATE: %s in %s (Count: %d)\n", nome, reg, c)
count++
}
if count == 0 {
fmt.Println("No duplicates found in empresas.")
}
}
// Check Anos
fmt.Println("\nChecking ANOS duplicates...")
rows2, err := conn.Query(context.Background(), "SELECT ano_semestre, regiao, COUNT(*) FROM anos_formaturas GROUP BY ano_semestre, regiao HAVING COUNT(*) > 1")
if err != nil {
fmt.Printf("Query error: %v\n", err)
} else {
defer rows2.Close()
count := 0
for rows2.Next() {
var nome, reg string
var c int
rows2.Scan(&nome, &reg, &c)
fmt.Printf("DUPLICATE: %s in %s (Count: %d)\n", nome, reg, c)
count++
}
if count == 0 {
fmt.Println("No duplicates found in anos_formaturas.")
}
}
}

View file

@ -0,0 +1,33 @@
package main
import (
"context"
"fmt"
"os"
"github.com/jackc/pgx/v5"
)
func main() {
dbURL := "postgres://user:pass@localhost:54322/photum-v2?sslmode=disable"
conn, err := pgx.Connect(context.Background(), dbURL)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to connect: %v\n", err)
os.Exit(1)
}
defer conn.Close(context.Background())
rows, err := conn.Query(context.Background(), "SELECT id, ano_semestre FROM anos_formaturas WHERE regiao = 'MG' ORDER BY ano_semestre")
if err != nil {
fmt.Fprintf(os.Stderr, "Query failed: %v\n", err)
os.Exit(1)
}
defer rows.Close()
fmt.Println("--- ANOS FORMATURAS (MG) ---")
for rows.Next() {
var id, ano string
rows.Scan(&id, &ano)
fmt.Printf("ID: %s | Ano: %q\n", id, ano)
}
}

View file

@ -0,0 +1,57 @@
package main
import (
"context"
"fmt"
"os"
"github.com/jackc/pgx/v5"
)
func main() {
dbURL := "postgres://user:pass@localhost:54322/photum-v2?sslmode=disable"
conn, err := pgx.Connect(context.Background(), dbURL)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to connect: %v\n", err)
os.Exit(1)
}
defer conn.Close(context.Background())
// 1. Get IDs of garbage years
rows, err := conn.Query(context.Background(), "SELECT id, ano_semestre FROM anos_formaturas WHERE ano_semestre !~ '^\\d{4}\\.\\d+$'")
if err != nil {
fmt.Fprintf(os.Stderr, "Select garbage failed: %v\n", err)
os.Exit(1)
}
defer rows.Close()
var ids []string
for rows.Next() {
var id, val string
rows.Scan(&id, &val)
ids = append(ids, id)
fmt.Printf("Found garbage year: %s (%s)\n", val, id)
}
rows.Close()
if len(ids) > 0 {
// 2. Delete dependencies in cadastro_fot
fmt.Println("Deleting dependencies in cadastro_fot...")
_, err = conn.Exec(context.Background(), "DELETE FROM cadastro_fot WHERE ano_formatura_id = ANY($1)", ids)
if err != nil {
fmt.Fprintf(os.Stderr, "Delete dependencies failed: %v\n", err)
os.Exit(1)
}
// 3. Delete the years
fmt.Println("Deleting garbage years...")
ct, err := conn.Exec(context.Background(), "DELETE FROM anos_formaturas WHERE id = ANY($1)", ids)
if err != nil {
fmt.Fprintf(os.Stderr, "Delete years failed: %v\n", err)
os.Exit(1)
}
fmt.Printf("Deleted %d garbage rows.\n", ct.RowsAffected())
} else {
fmt.Println("No garbage found.")
}
}

View file

@ -44,6 +44,9 @@ func AuthMiddleware(cfg *config.Config) gin.HandlerFunc {
c.Set("role", claims.Role) c.Set("role", claims.Role)
c.Set("regioes", claims.Regioes) c.Set("regioes", claims.Regioes)
// Add Vary header to correctly handle browser caching based on region
c.Header("Vary", "x-regiao")
// Region Logic // Region Logic
requestedRegion := c.GetHeader("x-regiao") requestedRegion := c.GetHeader("x-regiao")
if requestedRegion == "" { if requestedRegion == "" {

View file

@ -0,0 +1,12 @@
-- Clean up garbage data in anos_formaturas (Company names, empty strings, etc.)
-- Keep only patterns like 2024.1, 2024.2, etc. and maybe 2025.3 if used (User DB showed it).
-- Using regex: starts with 4 digits, dot, digit.
-- Clean dependencies in cadastro_fot first to avoid FK violations
DELETE FROM cadastro_fot
WHERE ano_formatura_id IN (
SELECT id FROM anos_formaturas WHERE ano_semestre !~ '^\d{4}\.\d+$'
);
-- Delete the garbage years
DELETE FROM anos_formaturas
WHERE ano_semestre !~ '^\d{4}\.\d+$';

View file

@ -0,0 +1,50 @@
-- Deduplicate Tipos Eventos (Case Insensitive)
-- 1. Update references to point to the "Keep" version (initcap/first one)
-- 2. Delete the duplicates
DO $$
DECLARE
r RECORD;
keep_id UUID;
BEGIN
-- Iterate over duplicates in tipos_eventos
FOR r IN
SELECT lower(trim(nome)) as norm_name, regiao
FROM tipos_eventos
GROUP BY lower(trim(nome)), regiao
HAVING count(*) > 1
LOOP
-- Pick the one to keep (e.g., the one that starts with Uppercase, or just the first created)
-- We prefer the one that is NOT all lowercase if possible.
SELECT id INTO keep_id
FROM tipos_eventos
WHERE lower(trim(nome)) = r.norm_name AND regiao = r.regiao
ORDER BY nome DESC -- Assuming Uppercase comes before lowercase in ASCII? No, 'B' < 'b'. But 'Baile' < 'baile'.
-- Actually, 'B' (66) < 'b' (98). So 'Baile' is smaller.
-- Let's sort by nome ASC to pick 'Baile' over 'baile'.
LIMIT 1;
RAISE NOTICE 'Merging duplicate % in % -> Keeping %', r.norm_name, r.regiao, keep_id;
-- Update FK references in agenda
UPDATE agenda SET tipo_evento_id = keep_id
WHERE tipo_evento_id IN (
SELECT id FROM tipos_eventos
WHERE lower(trim(nome)) = r.norm_name AND regiao = r.regiao AND id != keep_id
);
-- Update FK references in precos_tipos_eventos (if any)
UPDATE precos_tipos_eventos SET tipo_evento_id = keep_id
WHERE tipo_evento_id IN (
SELECT id FROM tipos_eventos
WHERE lower(trim(nome)) = r.norm_name AND regiao = r.regiao AND id != keep_id
);
-- Delete the others
DELETE FROM tipos_eventos
WHERE lower(trim(nome)) = r.norm_name
AND regiao = r.regiao
AND id != keep_id;
END LOOP;
END $$;

View file

@ -0,0 +1,9 @@
-- Clean dependencies in cadastro_fot first to avoid FK violations
DELETE FROM cadastro_fot
WHERE ano_formatura_id IN (
SELECT id FROM anos_formaturas WHERE ano_semestre !~ '^\d{4}\.[1-2]$'
);
-- Delete the garbage years (e.g. 2025.3, 2021.0, etc)
DELETE FROM anos_formaturas
WHERE ano_semestre !~ '^\d{4}\.[1-2]$';

View file

@ -32,7 +32,10 @@ export const SimpleCrud: React.FC<SimpleCrudProps> = ({
const fetchItems = async () => { const fetchItems = async () => {
try { try {
setLoading(true); setLoading(true);
const res = await fetch(`${API_BASE_URL}${endpoint}`, { // Append region query param to bust cache/ensure uniqueness per region
const url = `${API_BASE_URL}${endpoint}${endpoint.includes('?') ? '&' : '?'}regiao=${currentRegion}`;
const res = await fetch(url, {
headers: { headers: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
'x-regiao': currentRegion 'x-regiao': currentRegion