Skip to content

Commit 7e4f159

Browse files
IoTeacherclaude
andcommitted
Agregar unidad Proyectos Finales — 4 proyectos multi-paradigma con código base
Cada proyecto integra 3 capas: Prolog declarativo + Erlang/OTP o Clojure + Haskell/Elixir. Dominios: trámites IMSS, horarios TecNM (CLP/FD), monitor IoT CENAPRED, inventario SAT/CFDI. README incluye motivación con 30 casos de uso reales en producción (WhatsApp, Nubank, SAT, etc.). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KTRqGqphqyi8y1XAcWXoFE
1 parent 0d7668a commit 7e4f159

27 files changed

Lines changed: 2247 additions & 0 deletions

proyectos_finales/README.md

Lines changed: 416 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Proyecto 1 — Sistema de Validación de Trámites IMSS
2+
3+
## Descripción
4+
5+
El IMSS gestiona trámites de millones de derechohabientes: inscripción, incapacidades,
6+
pensiones, guardería, préstamos. Cada trámite tiene requisitos de elegibilidad distintos
7+
(semanas cotizadas, vigencia, edad, núcleo familiar). Este proyecto construye el
8+
motor de validación en tres capas.
9+
10+
---
11+
12+
## Dominio
13+
14+
| Trámite | Requisitos clave |
15+
|---------|-----------------|
16+
| `incapacidad_temporal` | Vigencia activa + semanas_cotizadas ≥ 4 |
17+
| `pension_invalidez` | semanas_cotizadas ≥ 150 + dictamen médico |
18+
| `guarderías` | Hijo registrado + madre/padre trabajador vigente |
19+
| `prestamo_imss` | semanas_cotizadas ≥ 52 + sin adeudo previo |
20+
| `atencion_medica` | Solo vigencia activa |
21+
22+
---
23+
24+
## Arquitectura
25+
26+
```
27+
capa1/validador_imss.pl ← reglas de elegibilidad (Prolog)
28+
capa1/restricciones.pl ← CLP(FD): límites numéricos de semanas/edad
29+
capa1/tests_imss.pl ← plunit: 15+ casos de prueba
30+
31+
capa2/derechohabiente.erl ← GenServer: estado de cada derechohabiente
32+
capa2/tramite_server.erl ← GenServer: cola de trámites pendientes
33+
capa2/imss_sup.erl ← Supervisor one_for_one
34+
35+
capa3/Reporte.hs ← tipos algebraicos + generación de reporte
36+
```
37+
38+
---
39+
40+
## Ejecutar
41+
42+
```bash
43+
# Capa 1 — tests Prolog
44+
swipl -g "run_tests(imss), halt" -l capa1/tests_imss.pl
45+
46+
# Capa 1 — interactivo
47+
swipl -l capa1/validador_imss.pl
48+
?- puede_tramitar(garcia_lopez, incapacidad_temporal, R).
49+
?- falta_requisito(perez_ruiz, prestamo_imss, Falta).
50+
51+
# Capa 2 — compilar y arrancar Erlang
52+
cd capa2 && erlc *.erl
53+
erl -eval "imss_sup:start_link(), timer:sleep(2000), halt()"
54+
55+
# Capa 3 — compilar Haskell
56+
cd capa3 && ghc -o reporte Reporte.hs && ./reporte
57+
```
58+
59+
---
60+
61+
## Entregables
62+
63+
1. `capa1/` — validador con CLP(FD), tests todos pasando
64+
2. `capa2/` — OTP arrancando: supervisor + al menos 2 GenServers
65+
3. `capa3/` — Haskell compilando y generando reporte en consola
66+
4. Este `README.md` actualizado con tus decisiones de diseño
67+
68+
## Crédito extra
69+
70+
Conectar capa 1 y capa 2: el `tramite_server` llama a SWI-Prolog vía puerto
71+
(`open_port({spawn, "swipl ..."}, [...])`) antes de aprobar cada trámite.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
:- module(restricciones, [semanas_validas/1, edad_pension/1, monto_prestamo/2]).
2+
:- use_module(library(clpfd)).
3+
4+
%% semanas_validas/1 — semanas_validas(?S) is nondet
5+
%% S es un número de semanas cotizadas válido (0..2080 = 40 años × 52 semanas)
6+
semanas_validas(S) :-
7+
S in 0..2080.
8+
9+
%% edad_pension/1 — edad_pension(?Edad) is nondet
10+
%% Edad mínima para pensión por vejez según IMSS (60–65 años)
11+
edad_pension(Edad) :-
12+
Edad in 60..65.
13+
14+
%% monto_prestamo/2 — monto_prestamo(+SemanasCotizadas, -MontoMaximo) is det
15+
%% El monto máximo de préstamo IMSS escala con semanas cotizadas.
16+
%% Restricción CLP(FD): MontoMaximo = SemanasCotizadas * 100 (simplificado)
17+
monto_prestamo(Semanas, Monto) :-
18+
semanas_validas(Semanas),
19+
Monto #= Semanas * 100,
20+
Monto in 0..500000.
21+
22+
%% semanas_para_pension/2 — semanas_para_pension(+SemanasCotizadas, -Faltan) is det
23+
%% Cuántas semanas faltan para alcanzar el mínimo de pensión por invalidez (150)
24+
semanas_para_pension(Semanas, Faltan) :-
25+
semanas_validas(Semanas),
26+
Minimo = 150,
27+
Faltan #= max(0, Minimo - Semanas),
28+
label([Faltan]).
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
:- use_module(validador_imss).
2+
:- use_module(restricciones).
3+
:- use_module(library(plunit)).
4+
5+
:- begin_tests(imss).
6+
7+
% ── vigencia ────────────────────────────────────────────────
8+
test(vigencia_activa_ok) :-
9+
vigencia_activa('12345678901').
10+
11+
test(vigencia_inactiva_falla, [fail]) :-
12+
vigencia_activa('34567890123').
13+
14+
% ── atención médica ─────────────────────────────────────────
15+
test(atencion_medica_vigente) :-
16+
puede_tramitar('12345678901', atencion_medica, ok).
17+
18+
test(atencion_medica_sin_vigencia) :-
19+
puede_tramitar('34567890123', atencion_medica, error(vigencia_inactiva)).
20+
21+
% ── incapacidad temporal ─────────────────────────────────────
22+
test(incapacidad_suficientes_semanas) :-
23+
puede_tramitar('12345678901', incapacidad_temporal, ok).
24+
25+
test(incapacidad_pocas_semanas) :-
26+
puede_tramitar('34567890123', incapacidad_temporal, error(vigencia_inactiva)).
27+
28+
test(incapacidad_vigente_pero_pocas_semanas) :-
29+
puede_tramitar('23456789012', incapacidad_temporal, ok).
30+
31+
% ── pensión por invalidez ────────────────────────────────────
32+
test(pension_cumple_todo) :-
33+
puede_tramitar('78901234567', pension_invalidez, ok).
34+
35+
test(pension_sin_dictamen) :-
36+
puede_tramitar('45678901234', pension_invalidez, error(sin_dictamen_medico)).
37+
38+
test(pension_pocas_semanas) :-
39+
puede_tramitar('23456789012', pension_invalidez,
40+
error(semanas_insuficientes(38, minimo_150))).
41+
42+
% ── guarderías ──────────────────────────────────────────────
43+
test(guarderias_con_hijo) :-
44+
puede_tramitar('12345678901', guarderias, ok).
45+
46+
test(guarderias_sin_hijo) :-
47+
puede_tramitar('67890123456', guarderias, error(sin_hijo_registrado)).
48+
49+
% ── préstamo IMSS ────────────────────────────────────────────
50+
test(prestamo_cumple_requisitos) :-
51+
puede_tramitar('67890123456', prestamo_imss, ok).
52+
53+
test(prestamo_con_adeudo) :-
54+
puede_tramitar('34567890123', prestamo_imss, error(vigencia_inactiva)).
55+
56+
test(prestamo_pocas_semanas) :-
57+
puede_tramitar('23456789012', prestamo_imss,
58+
error(semanas_insuficientes(38, minimo_52))).
59+
60+
% ── restricciones CLP(FD) ────────────────────────────────────
61+
test(semanas_validas_rango) :-
62+
semanas_validas(S), S in 0..2080.
63+
64+
test(monto_prestamo_calculado) :-
65+
monto_prestamo(52, Monto),
66+
Monto =:= 5200.
67+
68+
test(semanas_para_pension_calcula) :-
69+
semanas_para_pension(38, Faltan),
70+
Faltan =:= 112.
71+
72+
test(semanas_para_pension_cero) :-
73+
semanas_para_pension(200, Faltan),
74+
Faltan =:= 0.
75+
76+
:- end_tests(imss).
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
:- module(validador_imss, [
2+
puede_tramitar/3,
3+
falta_requisito/3,
4+
semanas_cotizadas/2,
5+
vigencia_activa/1
6+
]).
7+
8+
%% ============================================================
9+
%% BASE DE HECHOS — derechohabientes ficticios
10+
%% ============================================================
11+
12+
%% derechohabiente/4 — derechohabiente(+NSS, +Nombre, +SemanasCotizadas, +Vigente)
13+
derechohabiente('12345678901', garcia_lopez, 260, true).
14+
derechohabiente('23456789012', perez_ruiz, 38, true).
15+
derechohabiente('34567890123', sanchez_vega, 10, false).
16+
derechohabiente('45678901234', martinez_hdz, 180, true).
17+
derechohabiente('56789012345', torres_reyes, 0, false).
18+
derechohabiente('67890123456', flores_cruz, 55, true).
19+
derechohabiente('78901234567', rodriguez_m, 152, true).
20+
21+
%% hijo_registrado/2 — hijo_registrado(+NSS_Padre, +NSS_Hijo)
22+
hijo_registrado('12345678901', hijo_001).
23+
hijo_registrado('45678901234', hijo_002).
24+
25+
%% adeudo_previo/1 — adeudo_previo(+NSS)
26+
adeudo_previo('34567890123').
27+
28+
%% dictamen_medico/1 — dictamen_medico(+NSS)
29+
dictamen_medico('78901234567').
30+
31+
%% ============================================================
32+
%% CONSULTAS AUXILIARES
33+
%% ============================================================
34+
35+
%% vigencia_activa/1 — vigencia_activa(+NSS) is semidet
36+
vigencia_activa(NSS) :-
37+
derechohabiente(NSS, _, _, true).
38+
39+
%% semanas_cotizadas/2 — semanas_cotizadas(+NSS, -Semanas) is det
40+
semanas_cotizadas(NSS, Semanas) :-
41+
derechohabiente(NSS, _, Semanas, _).
42+
43+
%% ============================================================
44+
%% REGLAS DE ELEGIBILIDAD
45+
%% ============================================================
46+
47+
%% puede_tramitar/3 — puede_tramitar(+NSS, +Tramite, -Resultado) is det
48+
%% Resultado = ok | error(Razon)
49+
50+
puede_tramitar(NSS, atencion_medica, ok) :-
51+
vigencia_activa(NSS), !.
52+
puede_tramitar(NSS, atencion_medica, error(vigencia_inactiva)) :-
53+
\+ vigencia_activa(NSS).
54+
55+
puede_tramitar(NSS, incapacidad_temporal, ok) :-
56+
vigencia_activa(NSS),
57+
semanas_cotizadas(NSS, S),
58+
S >= 4, !.
59+
puede_tramitar(NSS, incapacidad_temporal, error(Razon)) :-
60+
falta_requisito(NSS, incapacidad_temporal, Razon).
61+
62+
puede_tramitar(NSS, pension_invalidez, ok) :-
63+
vigencia_activa(NSS),
64+
semanas_cotizadas(NSS, S),
65+
S >= 150,
66+
dictamen_medico(NSS), !.
67+
puede_tramitar(NSS, pension_invalidez, error(Razon)) :-
68+
falta_requisito(NSS, pension_invalidez, Razon).
69+
70+
puede_tramitar(NSS, guarderias, ok) :-
71+
vigencia_activa(NSS),
72+
hijo_registrado(NSS, _), !.
73+
puede_tramitar(NSS, guarderias, error(Razon)) :-
74+
falta_requisito(NSS, guarderias, Razon).
75+
76+
puede_tramitar(NSS, prestamo_imss, ok) :-
77+
vigencia_activa(NSS),
78+
semanas_cotizadas(NSS, S),
79+
S >= 52,
80+
\+ adeudo_previo(NSS), !.
81+
puede_tramitar(NSS, prestamo_imss, error(Razon)) :-
82+
falta_requisito(NSS, prestamo_imss, Razon).
83+
84+
%% ============================================================
85+
%% DIAGNÓSTICO DE REQUISITOS FALTANTES
86+
%% ============================================================
87+
88+
%% falta_requisito/3 — falta_requisito(+NSS, +Tramite, -Razon) is nondet
89+
%% No determinista: puede generar múltiples razones
90+
91+
falta_requisito(NSS, _, vigencia_inactiva) :-
92+
\+ vigencia_activa(NSS).
93+
94+
falta_requisito(NSS, incapacidad_temporal, semanas_insuficientes(S, minimo_4)) :-
95+
semanas_cotizadas(NSS, S), S < 4.
96+
97+
falta_requisito(NSS, pension_invalidez, semanas_insuficientes(S, minimo_150)) :-
98+
semanas_cotizadas(NSS, S), S < 150.
99+
100+
falta_requisito(NSS, pension_invalidez, sin_dictamen_medico) :-
101+
vigencia_activa(NSS),
102+
semanas_cotizadas(NSS, S), S >= 150,
103+
\+ dictamen_medico(NSS).
104+
105+
falta_requisito(NSS, guarderias, sin_hijo_registrado) :-
106+
vigencia_activa(NSS),
107+
\+ hijo_registrado(NSS, _).
108+
109+
falta_requisito(NSS, prestamo_imss, semanas_insuficientes(S, minimo_52)) :-
110+
semanas_cotizadas(NSS, S), S < 52.
111+
112+
falta_requisito(NSS, prestamo_imss, adeudo_pendiente) :-
113+
vigencia_activa(NSS),
114+
semanas_cotizadas(NSS, S), S >= 52,
115+
adeudo_previo(NSS).
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
-module(derechohabiente).
2+
-behaviour(gen_server).
3+
4+
-export([start_link/1, consultar/1, actualizar_semanas/2, estado/1]).
5+
-export([init/1, handle_call/3, handle_cast/2, terminate/2]).
6+
7+
-record(estado, {
8+
nss :: binary(),
9+
nombre :: atom(),
10+
semanas :: non_neg_integer(),
11+
vigente :: boolean(),
12+
tramites :: [atom()]
13+
}).
14+
15+
%% ── API pública ───────────────────────────────────────────────
16+
17+
start_link(NSS) ->
18+
gen_server:start_link(?MODULE, NSS, []).
19+
20+
consultar(Pid) ->
21+
gen_server:call(Pid, consultar).
22+
23+
actualizar_semanas(Pid, NuevasSemanas) ->
24+
gen_server:call(Pid, {actualizar_semanas, NuevasSemanas}).
25+
26+
estado(Pid) ->
27+
gen_server:call(Pid, estado).
28+
29+
%% ── Callbacks OTP ────────────────────────────────────────────
30+
31+
init(NSS) ->
32+
Estado = #estado{
33+
nss = NSS,
34+
nombre = desconocido,
35+
semanas = 0,
36+
vigente = false,
37+
tramites = []
38+
},
39+
{ok, Estado}.
40+
41+
handle_call(consultar, _From, S = #estado{nss = NSS, semanas = Sem, vigente = V}) ->
42+
Respuesta = #{nss => NSS, semanas => Sem, vigente => V},
43+
{reply, {ok, Respuesta}, S};
44+
45+
handle_call({actualizar_semanas, Nuevas}, _From, S) when Nuevas >= 0 ->
46+
{reply, ok, S#estado{semanas = Nuevas}};
47+
handle_call({actualizar_semanas, _}, _From, S) ->
48+
{reply, {error, semanas_negativas}, S};
49+
50+
handle_call(estado, _From, S) ->
51+
{reply, S, S};
52+
53+
handle_call(_Req, _From, S) ->
54+
{reply, {error, desconocido}, S}.
55+
56+
handle_cast(_Msg, S) ->
57+
{noreply, S}.
58+
59+
terminate(_Razon, _S) ->
60+
ok.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
-module(imss_sup).
2+
-behaviour(supervisor).
3+
4+
-export([start_link/0]).
5+
-export([init/1]).
6+
7+
start_link() ->
8+
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
9+
10+
init([]) ->
11+
Estrategia = #{
12+
strategy => one_for_one,
13+
intensity => 5,
14+
period => 10
15+
},
16+
Hijos = [
17+
#{
18+
id => tramite_server,
19+
start => {tramite_server, start_link, []},
20+
restart => permanent,
21+
shutdown => 5000,
22+
type => worker,
23+
modules => [tramite_server]
24+
}
25+
],
26+
{ok, {Estrategia, Hijos}}.

0 commit comments

Comments
 (0)