Fix: Deduplicacao de tipos de evento e limpeza de anos invalidos
This commit is contained in:
parent
f8bb2e66dd
commit
956c84a4bc
9 changed files with 272 additions and 1 deletions
45
backend/cmd/debug_db/main.go
Normal file
45
backend/cmd/debug_db/main.go
Normal 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, ®)
|
||||
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("----------------------------")
|
||||
}
|
||||
59
backend/cmd/debug_duplicates/main.go
Normal file
59
backend/cmd/debug_duplicates/main.go
Normal 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, ®, &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, ®, &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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
33
backend/cmd/debug_list_mg/main.go
Normal file
33
backend/cmd/debug_list_mg/main.go
Normal 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)
|
||||
}
|
||||
}
|
||||
57
backend/cmd/force_cleanup/main.go
Normal file
57
backend/cmd/force_cleanup/main.go
Normal 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.")
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,9 @@ func AuthMiddleware(cfg *config.Config) gin.HandlerFunc {
|
|||
c.Set("role", claims.Role)
|
||||
c.Set("regioes", claims.Regioes)
|
||||
|
||||
// Add Vary header to correctly handle browser caching based on region
|
||||
c.Header("Vary", "x-regiao")
|
||||
|
||||
// Region Logic
|
||||
requestedRegion := c.GetHeader("x-regiao")
|
||||
if requestedRegion == "" {
|
||||
|
|
|
|||
|
|
@ -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+$';
|
||||
|
|
@ -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 $$;
|
||||
|
|
@ -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]$';
|
||||
|
|
@ -32,7 +32,10 @@ export const SimpleCrud: React.FC<SimpleCrudProps> = ({
|
|||
const fetchItems = async () => {
|
||||
try {
|
||||
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: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'x-regiao': currentRegion
|
||||
|
|
|
|||
Loading…
Reference in a new issue