fix #46: Al generar asignaturas la IA también genera su sistema de evaluación
Deploy Function / deploy (push) Successful in 25s
Deploy Migrations to Production / deploy (push) Successful in 10s

This commit was merged in pull request #47.
This commit is contained in:
2026-03-04 17:48:23 -06:00
parent 994fb3c7bc
commit d7e232eec7
8 changed files with 167 additions and 20 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
"allowScripts": [ "allowScripts": [
"npm:deno@2.6.4", "npm:deno@2.6.4",
"npm:supabase-js@1.0.4", "npm:supabase-js@1.0.4",
"npm:supabase@2.72.6" "npm:supabase@2.75.0"
], ],
"imports": { "imports": {
"@openai/openai": "jsr:@openai/openai@^6.16.0", "@openai/openai": "jsr:@openai/openai@^6.16.0",
Generated
+8 -7
View File
@@ -28,7 +28,7 @@
"npm:openai@6.16.0": "6.16.0_zod@3.25.76", "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@^4.52.5": "4.104.0_zod@3.25.76",
"npm:openai@^6.16.0": "6.16.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" "npm:zod@3": "3.25.76"
}, },
"jsr": { "jsr": {
@@ -508,8 +508,8 @@
"signal-exit@4.1.0": { "signal-exit@4.1.0": {
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="
}, },
"supabase@2.72.6": { "supabase@2.75.0": {
"integrity": "sha512-axO1MkZgwMIxj78vKiGosnuXwzOaQq0Kz0I8jheNMwJH9dKGf3q/arivYT44z8u3t5FjGUxQWOuQoJetzcmm/A==", "integrity": "sha512-Xaai8Wp7F03OMkOWSyS1OMMdk5DxkauJxcMMesj4mO6jDGEZpWBwgo/gIUb4hsckexmr/RXYdpDpTbUIMXahbw==",
"dependencies": [ "dependencies": [
"bin-links", "bin-links",
"https-proxy-agent", "https-proxy-agent",
@@ -519,15 +519,16 @@
"scripts": true, "scripts": true,
"bin": true "bin": true
}, },
"tar@7.5.2": { "tar@7.5.7": {
"integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
"dependencies": [ "dependencies": [
"@isaacs/fs-minipass", "@isaacs/fs-minipass",
"chownr", "chownr",
"minipass", "minipass",
"minizlib", "minizlib",
"yallist" "yallist"
] ],
"deprecated": true
}, },
"tr46@0.0.3": { "tr46@0.0.3": {
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
@@ -603,7 +604,7 @@
"npm:deno@^2.6.4", "npm:deno@^2.6.4",
"npm:jwt-decode@4", "npm:jwt-decode@4",
"npm:openai@^6.16.0", "npm:openai@^6.16.0",
"npm:supabase@^2.72.6" "npm:supabase@2.75.0"
] ]
} }
} }
+1 -1
View File
@@ -5,7 +5,7 @@
"deno": "^2.6.4", "deno": "^2.6.4",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"openai": "^6.16.0", "openai": "^6.16.0",
"supabase": "^2.72.6" "supabase": "2.75.0"
}, },
"name": "genesis-2", "name": "genesis-2",
"module": "index.ts", "module": "index.ts",
+2
View File
@@ -1,3 +1,5 @@
[db]
major_version = 15
[functions.ai-generate-plan] [functions.ai-generate-plan]
enabled = true enabled = true
@@ -141,6 +141,7 @@ export type Database = {
creado_en: string creado_en: string
creado_por: string | null creado_por: string | null
creditos: number creditos: number
criterios_de_evaluacion: Json
datos: Json datos: Json
estado: Database["public"]["Enums"]["estado_asignatura"] estado: Database["public"]["Enums"]["estado_asignatura"]
estructura_id: string | null estructura_id: string | null
@@ -165,6 +166,7 @@ export type Database = {
creado_en?: string creado_en?: string
creado_por?: string | null creado_por?: string | null
creditos: number creditos: number
criterios_de_evaluacion?: Json
datos?: Json datos?: Json
estado?: Database["public"]["Enums"]["estado_asignatura"] estado?: Database["public"]["Enums"]["estado_asignatura"]
estructura_id?: string | null estructura_id?: string | null
@@ -189,6 +191,7 @@ export type Database = {
creado_en?: string creado_en?: string
creado_por?: string | null creado_por?: string | null
creditos?: number creditos?: number
criterios_de_evaluacion?: Json
datos?: Json datos?: Json
estado?: Database["public"]["Enums"]["estado_asignatura"] estado?: Database["public"]["Enums"]["estado_asignatura"]
estructura_id?: string | null estructura_id?: string | null
@@ -414,8 +414,7 @@ Deno.serve(async (req: Request): Promise<Response> => {
estructura_id: resolved.estructura_id as string, estructura_id: resolved.estructura_id as string,
nombre: resolved.nombre as string, nombre: resolved.nombre as string,
codigo: resolved.codigo ?? null, codigo: resolved.codigo ?? null,
tipo: tipo: (resolved.tipo ?? undefined) as Database["public"]["Tables"][
(resolved.tipo ?? undefined) as Database["public"]["Tables"][
"asignaturas" "asignaturas"
]["Insert"]["tipo"], ]["Insert"]["tipo"],
creditos: resolved.creditos as number, creditos: resolved.creditos as number,
@@ -458,7 +457,7 @@ Deno.serve(async (req: Request): Promise<Response> => {
const { data: estructura, error: estructuraError } = await supabaseService const { data: estructura, error: estructuraError } = await supabaseService
.from("estructuras_asignatura") .from("estructuras_asignatura")
.select("id,nombre,definicion,version") .select("id,nombre,definicion")
.eq("id", resolved.estructura_id) .eq("id", resolved.estructura_id)
.single(); .single();
if (estructuraError) { if (estructuraError) {
@@ -509,10 +508,11 @@ Deno.serve(async (req: Request): Promise<Response> => {
iaConfig.instruccionesAdicionalesIA ?? "(ninguna)" iaConfig.instruccionesAdicionalesIA ?? "(ninguna)"
}` + }` +
archivosAdjuntosTexto + archivosAdjuntosTexto +
`\n\nREGLA ESTRICTA MATEMÁTICA:\n` + `\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 (${ `- 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 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<string, unknown> = const schemaDef: Record<string, unknown> =
typeof estructura?.definicion === "object" && typeof estructura?.definicion === "object" &&
@@ -658,4 +658,39 @@ const definicionesDeEstructurasDeColumnas = {
], ],
"additionalProperties": false, "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,
},
}; };
@@ -0,0 +1 @@
alter table "public"."asignaturas" add column "criterios_de_evaluacion" jsonb not null default '[]'::jsonb;
+108 -3
View File
@@ -108,6 +108,16 @@ CREATE TYPE "public"."estado_conversacion" AS ENUM (
ALTER TYPE "public"."estado_conversacion" OWNER TO "postgres"; 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 ( CREATE TYPE "public"."estado_tarea_revision" AS ENUM (
'PENDIENTE', 'PENDIENTE',
'COMPLETADA', 'COMPLETADA',
@@ -523,6 +533,24 @@ CREATE TABLE IF NOT EXISTS "public"."archivos" (
ALTER TABLE "public"."archivos" OWNER TO "postgres"; 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" ( CREATE TABLE IF NOT EXISTS "public"."asignaturas" (
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
"plan_estudio_id" "uuid" NOT NULL, "plan_estudio_id" "uuid" NOT NULL,
@@ -546,6 +574,7 @@ CREATE TABLE IF NOT EXISTS "public"."asignaturas" (
"horas_academicas" integer, "horas_academicas" integer,
"horas_independientes" integer, "horas_independientes" integer,
"estado" "public"."estado_asignatura" DEFAULT 'borrador'::"public"."estado_asignatura" NOT NULL, "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_ciclo_chk" CHECK ((("numero_ciclo" IS NULL) OR ("numero_ciclo" > 0))),
CONSTRAINT "asignaturas_creditos_check" CHECK (("creditos" >= (0)::numeric)), CONSTRAINT "asignaturas_creditos_check" CHECK (("creditos" >= (0)::numeric)),
CONSTRAINT "asignaturas_horas_academicas_check" CHECK ((("horas_academicas" IS NULL) OR ("horas_academicas" >= 0))), 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" ( CREATE TABLE IF NOT EXISTS "public"."estructuras_asignatura" (
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
"nombre" "text" NOT NULL, "nombre" "text" NOT NULL,
"version" "text",
"definicion" "jsonb" DEFAULT '{}'::"jsonb" NOT NULL, "definicion" "jsonb" DEFAULT '{}'::"jsonb" NOT NULL,
"creado_en" timestamp with time zone DEFAULT "now"() 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"; 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" ( CREATE TABLE IF NOT EXISTS "public"."planes_estudio" (
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
"carrera_id" "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"; 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", SELECT "plan"."id" AS "plan_estudio_id",
"struct"."id" AS "estructura_id", "struct"."id" AS "estructura_id",
"struct"."template_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" ALTER TABLE ONLY "public"."asignaturas"
ADD CONSTRAINT "asignaturas_pkey" PRIMARY KEY ("id"); 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" ALTER TABLE ONLY "public"."planes_estudio"
ADD CONSTRAINT "planes_estudio_pkey" PRIMARY KEY ("id"); 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" ALTER TABLE ONLY "public"."asignaturas"
ADD CONSTRAINT "asignaturas_actualizado_por_fkey" FOREIGN KEY ("actualizado_por") REFERENCES "public"."usuarios_app"("id") ON DELETE SET NULL; 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" 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; 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 "anon";
GRANT ALL ON TABLE "public"."asignaturas" TO "authenticated"; GRANT ALL ON TABLE "public"."asignaturas" TO "authenticated";
GRANT ALL ON TABLE "public"."asignaturas" TO "service_role"; 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 "anon";
GRANT ALL ON TABLE "public"."planes_estudio" TO "authenticated"; GRANT ALL ON TABLE "public"."planes_estudio" TO "authenticated";
GRANT ALL ON TABLE "public"."planes_estudio" TO "service_role"; 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 "anon";
GRANT ALL ON TABLE "public"."plantilla_plan" TO "authenticated"; GRANT ALL ON TABLE "public"."plantilla_plan" TO "authenticated";
GRANT ALL ON TABLE "public"."plantilla_plan" TO "service_role"; GRANT ALL ON TABLE "public"."plantilla_plan" TO "service_role";