From d7e232eec77f743e82c3fafaa5e9c8aec289f571 Mon Sep 17 00:00:00 2001 From: Guillermo Arrieta Medina Date: Wed, 4 Mar 2026 17:48:23 -0600 Subject: [PATCH] =?UTF-8?q?fix=20#46:=20Al=20generar=20asignaturas=20la=20?= =?UTF-8?q?IA=20tambi=C3=A9n=20genera=20su=20sistema=20de=20evaluaci=C3=B3?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deno.json | 2 +- deno.lock | 15 +-- package.json | 2 +- supabase/config.toml | 2 + supabase/functions/_shared/database.types.ts | 3 + .../functions/ai-generate-subject/index.ts | 51 ++++++-- ...20260304215935_criterios_de_evaluacion.sql | 1 + supabase/schema.sql | 111 +++++++++++++++++- 8 files changed, 167 insertions(+), 20 deletions(-) create mode 100644 supabase/migrations/20260304215935_criterios_de_evaluacion.sql diff --git a/deno.json b/deno.json index 7270a0b..e9dc35e 100644 --- a/deno.json +++ b/deno.json @@ -2,7 +2,7 @@ "allowScripts": [ "npm:deno@2.6.4", "npm:supabase-js@1.0.4", - "npm:supabase@2.72.6" + "npm:supabase@2.75.0" ], "imports": { "@openai/openai": "jsr:@openai/openai@^6.16.0", diff --git a/deno.lock b/deno.lock index d610855..aaf39da 100644 --- a/deno.lock +++ b/deno.lock @@ -28,7 +28,7 @@ "npm:openai@6.16.0": "6.16.0_zod@3.25.76", "npm:openai@^4.52.5": "4.104.0_zod@3.25.76", "npm:openai@^6.16.0": "6.16.0_zod@3.25.76", - "npm:supabase@^2.72.6": "2.72.6", + "npm:supabase@2.75.0": "2.75.0", "npm:zod@3": "3.25.76" }, "jsr": { @@ -508,8 +508,8 @@ "signal-exit@4.1.0": { "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" }, - "supabase@2.72.6": { - "integrity": "sha512-axO1MkZgwMIxj78vKiGosnuXwzOaQq0Kz0I8jheNMwJH9dKGf3q/arivYT44z8u3t5FjGUxQWOuQoJetzcmm/A==", + "supabase@2.75.0": { + "integrity": "sha512-Xaai8Wp7F03OMkOWSyS1OMMdk5DxkauJxcMMesj4mO6jDGEZpWBwgo/gIUb4hsckexmr/RXYdpDpTbUIMXahbw==", "dependencies": [ "bin-links", "https-proxy-agent", @@ -519,15 +519,16 @@ "scripts": true, "bin": true }, - "tar@7.5.2": { - "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "tar@7.5.7": { + "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", "dependencies": [ "@isaacs/fs-minipass", "chownr", "minipass", "minizlib", "yallist" - ] + ], + "deprecated": true }, "tr46@0.0.3": { "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" @@ -603,7 +604,7 @@ "npm:deno@^2.6.4", "npm:jwt-decode@4", "npm:openai@^6.16.0", - "npm:supabase@^2.72.6" + "npm:supabase@2.75.0" ] } } diff --git a/package.json b/package.json index 6ca0e2b..30c7ce2 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "deno": "^2.6.4", "jwt-decode": "^4.0.0", "openai": "^6.16.0", - "supabase": "^2.72.6" + "supabase": "2.75.0" }, "name": "genesis-2", "module": "index.ts", diff --git a/supabase/config.toml b/supabase/config.toml index 69f4f4d..549430e 100644 --- a/supabase/config.toml +++ b/supabase/config.toml @@ -1,3 +1,5 @@ +[db] +major_version = 15 [functions.ai-generate-plan] enabled = true diff --git a/supabase/functions/_shared/database.types.ts b/supabase/functions/_shared/database.types.ts index 6f7d5b9..4176947 100644 --- a/supabase/functions/_shared/database.types.ts +++ b/supabase/functions/_shared/database.types.ts @@ -141,6 +141,7 @@ export type Database = { creado_en: string creado_por: string | null creditos: number + criterios_de_evaluacion: Json datos: Json estado: Database["public"]["Enums"]["estado_asignatura"] estructura_id: string | null @@ -165,6 +166,7 @@ export type Database = { creado_en?: string creado_por?: string | null creditos: number + criterios_de_evaluacion?: Json datos?: Json estado?: Database["public"]["Enums"]["estado_asignatura"] estructura_id?: string | null @@ -189,6 +191,7 @@ export type Database = { creado_en?: string creado_por?: string | null creditos?: number + criterios_de_evaluacion?: Json datos?: Json estado?: Database["public"]["Enums"]["estado_asignatura"] estructura_id?: string | null diff --git a/supabase/functions/ai-generate-subject/index.ts b/supabase/functions/ai-generate-subject/index.ts index 1d5c2fc..d51c88a 100644 --- a/supabase/functions/ai-generate-subject/index.ts +++ b/supabase/functions/ai-generate-subject/index.ts @@ -414,10 +414,9 @@ Deno.serve(async (req: Request): Promise => { estructura_id: resolved.estructura_id as string, nombre: resolved.nombre as string, codigo: resolved.codigo ?? null, - tipo: - (resolved.tipo ?? undefined) as Database["public"]["Tables"][ - "asignaturas" - ]["Insert"]["tipo"], + tipo: (resolved.tipo ?? undefined) as Database["public"]["Tables"][ + "asignaturas" + ]["Insert"]["tipo"], creditos: resolved.creditos as number, horas_academicas: resolved.horas_academicas ?? null, horas_independientes: resolved.horas_independientes ?? null, @@ -458,7 +457,7 @@ Deno.serve(async (req: Request): Promise => { const { data: estructura, error: estructuraError } = await supabaseService .from("estructuras_asignatura") - .select("id,nombre,definicion,version") + .select("id,nombre,definicion") .eq("id", resolved.estructura_id) .single(); if (estructuraError) { @@ -509,10 +508,11 @@ Deno.serve(async (req: Request): Promise => { iaConfig.instruccionesAdicionalesIA ?? "(ninguna)" }` + archivosAdjuntosTexto + - `\n\nREGLA ESTRICTA MATEMÁTICA:\n` + - `Si generas el 'contenido_tematico', la suma total de las 'horasEstimadas' de todos los temas en todas las unidades DEBE coincidir exactamente con el total de Horas académicas indicadas arriba (${ + `\n\nREGLAS ESTRICTAS MATEMÁTICAS:\n` + + `- Si generas el 'contenido_tematico', la suma total de las 'horasEstimadas' de todos los temas en todas las unidades DEBE coincidir exactamente con el total de Horas académicas indicadas arriba (${ resolved.horas_academicas ?? 0 - }). No te pases ni te falten horas.`; + }). No te pases ni te falten horas.\n` + + `- Si generas los 'criterios_de_evaluacion', la suma total de los 'porcentajes' de todos los criterios DEBE ser exactamente 100%. No te pases ni te falten porcentajes.`; const schemaDef: Record = typeof estructura?.definicion === "object" && @@ -658,4 +658,39 @@ const definicionesDeEstructurasDeColumnas = { ], "additionalProperties": false, }, + criterios_de_evaluacion: { + "type": "object", + "properties": { + "x-column": { + "type": "string", + "enum": [ + "criterios_de_evaluacion", + ], + }, + "x-definicion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "criterio": { + "type": "string", + }, + "porcentaje": { + "type": "integer", + }, + }, + "required": [ + "criterio", + "porcentaje", + ], + "additionalProperties": false, + }, + }, + }, + "required": [ + "x-column", + "x-definicion", + ], + "additionalProperties": false, + }, }; diff --git a/supabase/migrations/20260304215935_criterios_de_evaluacion.sql b/supabase/migrations/20260304215935_criterios_de_evaluacion.sql new file mode 100644 index 0000000..69adc81 --- /dev/null +++ b/supabase/migrations/20260304215935_criterios_de_evaluacion.sql @@ -0,0 +1 @@ +alter table "public"."asignaturas" add column "criterios_de_evaluacion" jsonb not null default '[]'::jsonb; \ No newline at end of file diff --git a/supabase/schema.sql b/supabase/schema.sql index 3650b95..898d073 100644 --- a/supabase/schema.sql +++ b/supabase/schema.sql @@ -108,6 +108,16 @@ CREATE TYPE "public"."estado_conversacion" AS ENUM ( ALTER TYPE "public"."estado_conversacion" OWNER TO "postgres"; +CREATE TYPE "public"."estado_mensaje_ia" AS ENUM ( + 'PROCESANDO', + 'COMPLETADO', + 'ERROR' +); + + +ALTER TYPE "public"."estado_mensaje_ia" OWNER TO "postgres"; + + CREATE TYPE "public"."estado_tarea_revision" AS ENUM ( 'PENDIENTE', 'COMPLETADA', @@ -523,6 +533,24 @@ CREATE TABLE IF NOT EXISTS "public"."archivos" ( ALTER TABLE "public"."archivos" OWNER TO "postgres"; +CREATE TABLE IF NOT EXISTS "public"."asignatura_mensajes_ia" ( + "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, + "enviado_por" "uuid" DEFAULT "auth"."uid"() NOT NULL, + "mensaje" "text" NOT NULL, + "campos" "text"[] DEFAULT '{}'::"text"[] NOT NULL, + "respuesta" "text", + "is_refusal" boolean DEFAULT false NOT NULL, + "propuesta" "jsonb", + "estado" "public"."estado_mensaje_ia" DEFAULT 'PROCESANDO'::"public"."estado_mensaje_ia" NOT NULL, + "fecha_creacion" timestamp without time zone DEFAULT "now"() NOT NULL, + "fecha_actualizacion" timestamp without time zone DEFAULT "now"() NOT NULL, + "conversacion_asignatura_id" "uuid" NOT NULL +); + + +ALTER TABLE "public"."asignatura_mensajes_ia" OWNER TO "postgres"; + + CREATE TABLE IF NOT EXISTS "public"."asignaturas" ( "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, "plan_estudio_id" "uuid" NOT NULL, @@ -546,6 +574,7 @@ CREATE TABLE IF NOT EXISTS "public"."asignaturas" ( "horas_academicas" integer, "horas_independientes" integer, "estado" "public"."estado_asignatura" DEFAULT 'borrador'::"public"."estado_asignatura" NOT NULL, + "criterios_de_evaluacion" "jsonb" DEFAULT '[]'::"jsonb" NOT NULL, CONSTRAINT "asignaturas_ciclo_chk" CHECK ((("numero_ciclo" IS NULL) OR ("numero_ciclo" > 0))), CONSTRAINT "asignaturas_creditos_check" CHECK (("creditos" >= (0)::numeric)), CONSTRAINT "asignaturas_horas_academicas_check" CHECK ((("horas_academicas" IS NULL) OR ("horas_academicas" >= 0))), @@ -675,10 +704,11 @@ ALTER TABLE "public"."estados_plan" OWNER TO "postgres"; CREATE TABLE IF NOT EXISTS "public"."estructuras_asignatura" ( "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, "nombre" "text" NOT NULL, - "version" "text", "definicion" "jsonb" DEFAULT '{}'::"jsonb" NOT NULL, "creado_en" timestamp with time zone DEFAULT "now"() NOT NULL, - "actualizado_en" timestamp with time zone DEFAULT "now"() NOT NULL + "actualizado_en" timestamp with time zone DEFAULT "now"() NOT NULL, + "template_id" "text", + "tipo" "public"."tipo_estructura_plan" ); @@ -763,6 +793,24 @@ CREATE TABLE IF NOT EXISTS "public"."notificaciones" ( ALTER TABLE "public"."notificaciones" OWNER TO "postgres"; +CREATE TABLE IF NOT EXISTS "public"."plan_mensajes_ia" ( + "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, + "enviado_por" "uuid" DEFAULT "auth"."uid"() NOT NULL, + "mensaje" "text" NOT NULL, + "campos" "text"[] DEFAULT '{}'::"text"[] NOT NULL, + "respuesta" "text", + "is_refusal" boolean DEFAULT false NOT NULL, + "propuesta" "jsonb", + "estado" "public"."estado_mensaje_ia" DEFAULT 'PROCESANDO'::"public"."estado_mensaje_ia" NOT NULL, + "fecha_creacion" timestamp without time zone DEFAULT "now"() NOT NULL, + "fecha_actualizacion" timestamp without time zone DEFAULT "now"() NOT NULL, + "conversacion_plan_id" "uuid" NOT NULL +); + + +ALTER TABLE "public"."plan_mensajes_ia" OWNER TO "postgres"; + + CREATE TABLE IF NOT EXISTS "public"."planes_estudio" ( "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, "carrera_id" "uuid" NOT NULL, @@ -789,7 +837,18 @@ CREATE TABLE IF NOT EXISTS "public"."planes_estudio" ( ALTER TABLE "public"."planes_estudio" OWNER TO "postgres"; -CREATE OR REPLACE VIEW "public"."plantilla_plan" AS +CREATE OR REPLACE VIEW "public"."plantilla_asignatura" AS + SELECT "asignaturas"."id" AS "asignatura_id", + "struct"."id" AS "estructura_id", + "struct"."template_id" + FROM ("public"."asignaturas" + JOIN "public"."estructuras_asignatura" "struct" ON (("asignaturas"."estructura_id" = "struct"."id"))); + + +ALTER TABLE "public"."plantilla_asignatura" OWNER TO "postgres"; + + +CREATE OR REPLACE VIEW "public"."plantilla_plan" WITH ("security_invoker"='on') AS SELECT "plan"."id" AS "plan_estudio_id", "struct"."id" AS "estructura_id", "struct"."template_id" @@ -896,6 +955,11 @@ ALTER TABLE ONLY "public"."archivos" +ALTER TABLE ONLY "public"."asignatura_mensajes_ia" + ADD CONSTRAINT "asignatura_mensajes_ia_pkey" PRIMARY KEY ("id"); + + + ALTER TABLE ONLY "public"."asignaturas" ADD CONSTRAINT "asignaturas_pkey" PRIMARY KEY ("id"); @@ -991,6 +1055,11 @@ ALTER TABLE ONLY "public"."notificaciones" +ALTER TABLE ONLY "public"."plan_mensajes_ia" + ADD CONSTRAINT "plan_mensajes_ia_pkey" PRIMARY KEY ("id"); + + + ALTER TABLE ONLY "public"."planes_estudio" ADD CONSTRAINT "planes_estudio_pkey" PRIMARY KEY ("id"); @@ -1136,6 +1205,11 @@ ALTER TABLE ONLY "public"."archivos" +ALTER TABLE ONLY "public"."asignatura_mensajes_ia" + ADD CONSTRAINT "asignatura_mensajes_ia_conversacion_asignatura_id_fkey" FOREIGN KEY ("conversacion_asignatura_id") REFERENCES "public"."conversaciones_asignatura"("id") ON DELETE CASCADE; + + + ALTER TABLE ONLY "public"."asignaturas" ADD CONSTRAINT "asignaturas_actualizado_por_fkey" FOREIGN KEY ("actualizado_por") REFERENCES "public"."usuarios_app"("id") ON DELETE SET NULL; @@ -1246,6 +1320,11 @@ ALTER TABLE ONLY "public"."notificaciones" +ALTER TABLE ONLY "public"."plan_mensajes_ia" + ADD CONSTRAINT "plan_mensajes_ia_conversacion_plan_id_fkey" FOREIGN KEY ("conversacion_plan_id") REFERENCES "public"."conversaciones_plan"("id") ON DELETE CASCADE; + + + ALTER TABLE ONLY "public"."planes_estudio" ADD CONSTRAINT "planes_estudio_actualizado_por_fkey" FOREIGN KEY ("actualizado_por") REFERENCES "public"."usuarios_app"("id") ON DELETE SET NULL; @@ -1350,6 +1429,14 @@ ALTER PUBLICATION "supabase_realtime" OWNER TO "postgres"; +ALTER PUBLICATION "supabase_realtime" ADD TABLE ONLY "public"."asignaturas"; + + + +ALTER PUBLICATION "supabase_realtime" ADD TABLE ONLY "public"."planes_estudio"; + + + @@ -1688,6 +1775,12 @@ GRANT ALL ON TABLE "public"."archivos" TO "service_role"; +GRANT ALL ON TABLE "public"."asignatura_mensajes_ia" TO "anon"; +GRANT ALL ON TABLE "public"."asignatura_mensajes_ia" TO "authenticated"; +GRANT ALL ON TABLE "public"."asignatura_mensajes_ia" TO "service_role"; + + + GRANT ALL ON TABLE "public"."asignaturas" TO "anon"; GRANT ALL ON TABLE "public"."asignaturas" TO "authenticated"; GRANT ALL ON TABLE "public"."asignaturas" TO "service_role"; @@ -1772,12 +1865,24 @@ GRANT ALL ON TABLE "public"."notificaciones" TO "service_role"; +GRANT ALL ON TABLE "public"."plan_mensajes_ia" TO "anon"; +GRANT ALL ON TABLE "public"."plan_mensajes_ia" TO "authenticated"; +GRANT ALL ON TABLE "public"."plan_mensajes_ia" TO "service_role"; + + + GRANT ALL ON TABLE "public"."planes_estudio" TO "anon"; GRANT ALL ON TABLE "public"."planes_estudio" TO "authenticated"; GRANT ALL ON TABLE "public"."planes_estudio" TO "service_role"; +GRANT ALL ON TABLE "public"."plantilla_asignatura" TO "anon"; +GRANT ALL ON TABLE "public"."plantilla_asignatura" TO "authenticated"; +GRANT ALL ON TABLE "public"."plantilla_asignatura" TO "service_role"; + + + GRANT ALL ON TABLE "public"."plantilla_plan" TO "anon"; GRANT ALL ON TABLE "public"."plantilla_plan" TO "authenticated"; GRANT ALL ON TABLE "public"."plantilla_plan" TO "service_role"; -- 2.52.0