From 1af669eb472906d39675c916ac3996949aefcee9 Mon Sep 17 00:00:00 2001 From: liam-dev-eng Date: Mon, 17 Nov 2025 14:17:15 -0500 Subject: [PATCH] add script migration --- backend/prisma/ERD.md | 127 +++++++++++++ backend/prisma/ERD_update.md | 88 +++++++++ backend/prisma/migration_erd_update.sql | 241 ++++++++++++++++++++++++ prompts/promtps-wca.md | 48 +++++ 4 files changed, 504 insertions(+) create mode 100644 backend/prisma/ERD.md create mode 100644 backend/prisma/ERD_update.md create mode 100644 backend/prisma/migration_erd_update.sql create mode 100644 prompts/promtps-wca.md diff --git a/backend/prisma/ERD.md b/backend/prisma/ERD.md new file mode 100644 index 0000000..c753db7 --- /dev/null +++ b/backend/prisma/ERD.md @@ -0,0 +1,127 @@ +# Diagrama ERD - Base de Datos de Candidatos + +## Análisis del Esquema + +Este esquema de Prisma define un sistema de gestión de candidatos con las siguientes características: + +### Entidades Principales + +1. **Candidate** - Entidad principal que representa a un candidato +2. **Education** - Historial educativo de los candidatos +3. **WorkExperience** - Experiencia laboral de los candidatos +4. **Resume** - Archivos de currículum asociados a los candidatos + +### Relaciones + +- **Candidate → Education**: Relación uno-a-muchos (un candidato puede tener múltiples registros educativos) +- **Candidate → WorkExperience**: Relación uno-a-muchos (un candidato puede tener múltiples experiencias laborales) +- **Candidate → Resume**: Relación uno-a-muchos (un candidato puede tener múltiples currículums) + +## Diagrama ERD + +```mermaid +erDiagram + Candidate { + int id PK "AUTO_INCREMENT" + varchar firstName "100" + varchar lastName "100" + varchar email UK "255, UNIQUE" + varchar phone "15, NULLABLE" + varchar address "100, NULLABLE" + } + + Education { + int id PK "AUTO_INCREMENT" + varchar institution "100" + varchar title "250" + datetime startDate + datetime endDate "NULLABLE" + int candidateId FK + } + + WorkExperience { + int id PK "AUTO_INCREMENT" + varchar company "100" + varchar position "100" + varchar description "200, NULLABLE" + datetime startDate + datetime endDate "NULLABLE" + int candidateId FK + } + + Resume { + int id PK "AUTO_INCREMENT" + varchar filePath "500" + varchar fileType "50" + datetime uploadDate + int candidateId FK + } + + Candidate ||--o{ Education : "has many" + Candidate ||--o{ WorkExperience : "has many" + Candidate ||--o{ Resume : "has many" +``` + +## Descripción Detallada de las Entidades + +### Candidate (Candidato) +- **Propósito**: Almacena la información personal básica de los candidatos +- **Campos Clave**: + - `id`: Identificador único autoincremental + - `email`: Email único del candidato (constraint UNIQUE) + - `firstName`, `lastName`: Nombre y apellido del candidato + - `phone`, `address`: Campos opcionales para contacto y ubicación + +### Education (Educación) +- **Propósito**: Registra el historial educativo de los candidatos +- **Campos Clave**: + - `id`: Identificador único autoincremental + - `candidateId`: Clave foránea que referencia a Candidate + - `institution`: Nombre de la institución educativa + - `title`: Título o grado obtenido + - `startDate`: Fecha de inicio (obligatoria) + - `endDate`: Fecha de finalización (opcional, permite educación en curso) + +### WorkExperience (Experiencia Laboral) +- **Propósito**: Almacena la experiencia laboral de los candidatos +- **Campos Clave**: + - `id`: Identificador único autoincremental + - `candidateId`: Clave foránea que referencia a Candidate + - `company`: Nombre de la empresa + - `position`: Cargo o posición ocupada + - `description`: Descripción opcional de las responsabilidades + - `startDate`: Fecha de inicio (obligatoria) + - `endDate`: Fecha de finalización (opcional, permite trabajos actuales) + +### Resume (Currículum) +- **Propósito**: Gestiona los archivos de currículum subidos por los candidatos +- **Campos Clave**: + - `id`: Identificador único autoincremental + - `candidateId`: Clave foránea que referencia a Candidate + - `filePath`: Ruta del archivo en el sistema de almacenamiento + - `fileType`: Tipo de archivo (PDF, DOC, etc.) + - `uploadDate`: Fecha y hora de carga del archivo + +## Características del Esquema + +### Constraints y Validaciones +- **Email único**: El campo `email` en Candidate tiene constraint UNIQUE, garantizando que no haya duplicados +- **Claves foráneas**: Todas las relaciones están definidas con foreign keys que aseguran integridad referencial +- **Campos opcionales**: Varios campos permiten valores NULL para flexibilidad (phone, address, endDate, description) + +### Tipos de Datos +- **PostgreSQL**: El esquema utiliza PostgreSQL como base de datos +- **VarChar con límites**: Todos los campos de texto tienen límites específicos para optimizar el almacenamiento +- **DateTime**: Fechas almacenadas como DateTime para precisión temporal + +### Relaciones +- **Cascada implícita**: Las relaciones están configuradas para mantener la integridad referencial +- **Cardinalidad**: Todas las relaciones son uno-a-muchos (1:N), permitiendo múltiples registros relacionados por candidato + +## Consideraciones de Diseño + +1. **Normalización**: El esquema está bien normalizado, separando información personal, educativa, laboral y archivos +2. **Escalabilidad**: Permite múltiples registros por candidato en cada entidad relacionada +3. **Flexibilidad**: Campos opcionales permiten datos incompletos sin comprometer la integridad +4. **Trazabilidad**: El campo `uploadDate` en Resume permite rastrear cuándo se subieron los archivos + diff --git a/backend/prisma/ERD_update.md b/backend/prisma/ERD_update.md new file mode 100644 index 0000000..24cc89c --- /dev/null +++ b/backend/prisma/ERD_update.md @@ -0,0 +1,88 @@ +```mermaid +erDiagram + COMPANY { + int id PK + string name + } + EMPLOYEE { + int id PK + int company_id FK + string name + string email + string role + boolean is_active + } + POSITION { + int id PK + int company_id FK + int interview_flow_id FK + string title + text description + string status + boolean is_visible + string location + text job_description + text requirements + text responsibilities + numeric salary_min + numeric salary_max + string employment_type + text benefits + text company_description + date application_deadline + string contact_info + } + INTERVIEW_FLOW { + int id PK + string description + } + INTERVIEW_STEP { + int id PK + int interview_flow_id FK + int interview_type_id FK + string name + int order_index + } + INTERVIEW_TYPE { + int id PK + string name + text description + } + CANDIDATE { + int id PK + string firstName + string lastName + string email + string phone + string address + } + APPLICATION { + int id PK + int position_id FK + int candidate_id FK + date application_date + string status + text notes + } + INTERVIEW { + int id PK + int application_id FK + int interview_step_id FK + int employee_id FK + date interview_date + string result + int score + text notes + } + + COMPANY ||--o{ EMPLOYEE : employs + COMPANY ||--o{ POSITION : offers + POSITION ||--|| INTERVIEW_FLOW : assigns + INTERVIEW_FLOW ||--o{ INTERVIEW_STEP : contains + INTERVIEW_STEP ||--|| INTERVIEW_TYPE : uses + POSITION ||--o{ APPLICATION : receives + CANDIDATE ||--o{ APPLICATION : submits + APPLICATION ||--o{ INTERVIEW : has + INTERVIEW ||--|| INTERVIEW_STEP : consists_of + EMPLOYEE ||--o{ INTERVIEW : conducts +``` \ No newline at end of file diff --git a/backend/prisma/migration_erd_update.sql b/backend/prisma/migration_erd_update.sql new file mode 100644 index 0000000..af337d4 --- /dev/null +++ b/backend/prisma/migration_erd_update.sql @@ -0,0 +1,241 @@ +-- ===================================================== +-- Migration Script: ERD Update +-- Generated: 2025-11-17 +-- Purpose: Create new tables from ERD_update.md while preserving existing Candidate-related tables +-- Database: PostgreSQL +-- ===================================================== + +BEGIN; + +-- ===================================================== +-- SECTION 1: CREATE CORE TABLES (no dependencies) +-- ===================================================== + +-- Table: COMPANY +-- Description: Stores company information +CREATE TABLE IF NOT EXISTS "Company" ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Table: INTERVIEW_FLOW +-- Description: Defines interview process flows +CREATE TABLE IF NOT EXISTS "InterviewFlow" ( + id SERIAL PRIMARY KEY, + description VARCHAR(500), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Table: INTERVIEW_TYPE +-- Description: Catalog of interview types (technical, HR, etc.) +CREATE TABLE IF NOT EXISTS "InterviewType" ( + id SERIAL PRIMARY KEY, + name VARCHAR(100) NOT NULL, + description TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- ===================================================== +-- SECTION 2: CREATE DEPENDENT TABLES (Level 1) +-- ===================================================== + +-- Table: EMPLOYEE +-- Description: Company employees who conduct interviews +CREATE TABLE IF NOT EXISTS "Employee" ( + id SERIAL PRIMARY KEY, + company_id INTEGER NOT NULL, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + role VARCHAR(100), + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_employee_company FOREIGN KEY (company_id) + REFERENCES "Company"(id) ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT uq_employee_email UNIQUE (email) +); + +-- Table: POSITION +-- Description: Job positions offered by companies +CREATE TABLE IF NOT EXISTS "Position" ( + id SERIAL PRIMARY KEY, + company_id INTEGER NOT NULL, + interview_flow_id INTEGER NOT NULL, + title VARCHAR(255) NOT NULL, + description TEXT, + status VARCHAR(50) DEFAULT 'draft', + is_visible BOOLEAN DEFAULT false, + location VARCHAR(255), + job_description TEXT, + requirements TEXT, + responsibilities TEXT, + salary_min NUMERIC(10, 2), + salary_max NUMERIC(10, 2), + employment_type VARCHAR(50), + benefits TEXT, + company_description TEXT, + application_deadline DATE, + contact_info VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_position_company FOREIGN KEY (company_id) + REFERENCES "Company"(id) ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT fk_position_interview_flow FOREIGN KEY (interview_flow_id) + REFERENCES "InterviewFlow"(id) ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT chk_position_status CHECK (status IN ('draft', 'open', 'closed', 'on_hold')), + CONSTRAINT chk_position_employment_type CHECK (employment_type IN ('full_time', 'part_time', 'contract', 'temporary', 'internship')), + CONSTRAINT chk_salary_range CHECK (salary_max IS NULL OR salary_min IS NULL OR salary_max >= salary_min) +); + +-- Table: INTERVIEW_STEP +-- Description: Steps within an interview flow +CREATE TABLE IF NOT EXISTS "InterviewStep" ( + id SERIAL PRIMARY KEY, + interview_flow_id INTEGER NOT NULL, + interview_type_id INTEGER NOT NULL, + name VARCHAR(255) NOT NULL, + order_index INTEGER NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_interview_step_flow FOREIGN KEY (interview_flow_id) + REFERENCES "InterviewFlow"(id) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT fk_interview_step_type FOREIGN KEY (interview_type_id) + REFERENCES "InterviewType"(id) ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT chk_order_index CHECK (order_index >= 0) +); + +-- ===================================================== +-- SECTION 3: CREATE DEPENDENT TABLES (Level 2) +-- ===================================================== + +-- Table: APPLICATION +-- Description: Candidate applications to positions +CREATE TABLE IF NOT EXISTS "Application" ( + id SERIAL PRIMARY KEY, + position_id INTEGER NOT NULL, + candidate_id INTEGER NOT NULL, + application_date DATE DEFAULT CURRENT_DATE, + status VARCHAR(50) DEFAULT 'submitted', + notes TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_application_position FOREIGN KEY (position_id) + REFERENCES "Position"(id) ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT fk_application_candidate FOREIGN KEY (candidate_id) + REFERENCES "Candidate"(id) ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT chk_application_status CHECK (status IN ('submitted', 'under_review', 'interview', 'rejected', 'accepted', 'withdrawn')) +); + +-- ===================================================== +-- SECTION 4: CREATE DEPENDENT TABLES (Level 3) +-- ===================================================== + +-- Table: INTERVIEW +-- Description: Individual interview sessions +CREATE TABLE IF NOT EXISTS "Interview" ( + id SERIAL PRIMARY KEY, + application_id INTEGER NOT NULL, + interview_step_id INTEGER NOT NULL, + employee_id INTEGER NOT NULL, + interview_date DATE NOT NULL, + result VARCHAR(50), + score INTEGER, + notes TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_interview_application FOREIGN KEY (application_id) + REFERENCES "Application"(id) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT fk_interview_step FOREIGN KEY (interview_step_id) + REFERENCES "InterviewStep"(id) ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT fk_interview_employee FOREIGN KEY (employee_id) + REFERENCES "Employee"(id) ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT chk_interview_result CHECK (result IN ('passed', 'failed', 'pending', 'no_show')), + CONSTRAINT chk_interview_score CHECK (score IS NULL OR (score >= 0 AND score <= 100)) +); + +-- ===================================================== +-- SECTION 5: CREATE INDEXES FOR PERFORMANCE +-- ===================================================== + +-- Indexes for EMPLOYEE +CREATE INDEX IF NOT EXISTS idx_employee_company_id ON "Employee"(company_id); +CREATE INDEX IF NOT EXISTS idx_employee_email ON "Employee"(email); +CREATE INDEX IF NOT EXISTS idx_employee_is_active ON "Employee"(is_active); + +-- Indexes for POSITION +CREATE INDEX IF NOT EXISTS idx_position_company_id ON "Position"(company_id); +CREATE INDEX IF NOT EXISTS idx_position_interview_flow_id ON "Position"(interview_flow_id); +CREATE INDEX IF NOT EXISTS idx_position_status ON "Position"(status); +CREATE INDEX IF NOT EXISTS idx_position_is_visible ON "Position"(is_visible); +CREATE INDEX IF NOT EXISTS idx_position_application_deadline ON "Position"(application_deadline); + +-- Indexes for INTERVIEW_STEP +CREATE INDEX IF NOT EXISTS idx_interview_step_flow_id ON "InterviewStep"(interview_flow_id); +CREATE INDEX IF NOT EXISTS idx_interview_step_type_id ON "InterviewStep"(interview_type_id); +CREATE INDEX IF NOT EXISTS idx_interview_step_order ON "InterviewStep"(order_index); + +-- Indexes for APPLICATION +CREATE INDEX IF NOT EXISTS idx_application_position_id ON "Application"(position_id); +CREATE INDEX IF NOT EXISTS idx_application_candidate_id ON "Application"(candidate_id); +CREATE INDEX IF NOT EXISTS idx_application_status ON "Application"(status); +CREATE INDEX IF NOT EXISTS idx_application_date ON "Application"(application_date); + +-- Indexes for INTERVIEW +CREATE INDEX IF NOT EXISTS idx_interview_application_id ON "Interview"(application_id); +CREATE INDEX IF NOT EXISTS idx_interview_step_id ON "Interview"(interview_step_id); +CREATE INDEX IF NOT EXISTS idx_interview_employee_id ON "Interview"(employee_id); +CREATE INDEX IF NOT EXISTS idx_interview_date ON "Interview"(interview_date); +CREATE INDEX IF NOT EXISTS idx_interview_result ON "Interview"(result); + +-- ===================================================== +-- SECTION 6: COMMENTS FOR DOCUMENTATION +-- ===================================================== + +COMMENT ON TABLE "Company" IS 'Stores information about companies using the ATS'; +COMMENT ON TABLE "Employee" IS 'Company employees who participate in the hiring process'; +COMMENT ON TABLE "Position" IS 'Job positions posted by companies'; +COMMENT ON TABLE "InterviewFlow" IS 'Defines the interview process workflow'; +COMMENT ON TABLE "InterviewStep" IS 'Individual steps within an interview flow'; +COMMENT ON TABLE "InterviewType" IS 'Types of interviews (technical, HR, cultural fit, etc.)'; +COMMENT ON TABLE "Application" IS 'Candidate applications to job positions'; +COMMENT ON TABLE "Interview" IS 'Individual interview sessions between candidates and employees'; + +COMMIT; + +-- ===================================================== +-- ROLLBACK SCRIPT (COMMENTED OUT FOR SAFETY) +-- ===================================================== +-- To rollback this migration, execute the following in order: +-- +-- BEGIN; +-- DROP TABLE IF EXISTS "Interview" CASCADE; +-- DROP TABLE IF EXISTS "Application" CASCADE; +-- DROP TABLE IF EXISTS "InterviewStep" CASCADE; +-- DROP TABLE IF EXISTS "Position" CASCADE; +-- DROP TABLE IF EXISTS "Employee" CASCADE; +-- DROP TABLE IF EXISTS "InterviewType" CASCADE; +-- DROP TABLE IF EXISTS "InterviewFlow" CASCADE; +-- DROP TABLE IF EXISTS "Company" CASCADE; +-- COMMIT; +-- +-- NOTE: This will permanently delete all data in these tables! +-- Existing tables (Candidate, Education, WorkExperience, Resume) will remain intact. + +-- ===================================================== +-- VERIFICATION QUERIES (OPTIONAL) +-- ===================================================== +-- After running the migration, verify with: +-- +-- SELECT table_name FROM information_schema.tables +-- WHERE table_schema = 'public' AND table_type = 'BASE TABLE' +-- ORDER BY table_name; +-- +-- SELECT constraint_name, table_name, constraint_type +-- FROM information_schema.table_constraints +-- WHERE table_schema = 'public' AND constraint_type IN ('FOREIGN KEY', 'PRIMARY KEY') +-- ORDER BY table_name, constraint_type; + diff --git a/prompts/promtps-wca.md b/prompts/promtps-wca.md new file mode 100644 index 0000000..1fbafc4 --- /dev/null +++ b/prompts/promtps-wca.md @@ -0,0 +1,48 @@ +# Prompt entendimiento del proyecto + +## Rol + +Eres un desarrollador experto en bases de datos con Prisma + +## Tarea + +entiende a pronfundidad el esquema y genera un diagrama ERD en formato mermaid para entender su relación entre tablas + +## Formato + +Entrega el documento en un formato markdown + +# Prompt Migración + +Eres un experto en bases de datos relacionales y Prisma ORM. Tu tarea es analizar el siguiente diagrama ERD [ERD_update.md] y generar un script SQL de migración para PostgreSQL que actualice la estructura de la base de datos [schema.prisma], asegurando: + +1. **Compatibilidad con Prisma**: El script debe ser compatible con el esquema de Prisma proporcionado y mantener la integridad de las migraciones existentes. + +2. **Integridad de datos**: + - Preservar los datos existentes durante la migración + - Manejar correctamente las claves foráneas y constraints + - Aplicar estrategias de migración seguras (ALTER TABLE cuando sea posible, evitando DROP innecesarios) + +3. **Estructura correcta**: + - Crear/modificar tablas según el diagrama ERD + - Definir correctamente tipos de datos PostgreSQL (VARCHAR con límites, INTEGER, TIMESTAMP, etc.) + - Implementar constraints: PRIMARY KEY, FOREIGN KEY, UNIQUE, NOT NULL + - Configurar índices apropiados para mejorar el rendimiento de consultas + +4. **Validaciones**: + - Verificar que todas las relaciones del ERD estén correctamente implementadas + - Asegurar que los campos opcionales permitan NULL cuando corresponda + - Validar que los límites de longitud de VARCHAR coincidan con el esquema + +5. **Formato del script**: + - Incluir comentarios explicativos para cada sección + - Usar transacciones para garantizar atomicidad + - Proporcionar instrucciones de rollback si es necesario + - Incluir verificaciones de existencia antes de crear/modificar objetos + +6. **Consideraciones adicionales**: + - Si la base de datos ya existe, identificar qué cambios son necesarios (ALTER vs CREATE) + - Optimizar el orden de ejecución para evitar errores de dependencias + - Considerar el impacto en el rendimiento durante la migración + +Genera el script SQL completo, listo para ejecutar, que transforme la base de datos actual al estado definido en el diagrama ERD. \ No newline at end of file