Vista General

El Observatorio del Congreso usa un esquema unificado Popolo-Graph almacenado en SQLite. Contiene 12 tablas organizadas en cuatro grupos funcionales:

GrupoTablasPropósito
Entidades baseperson, organization, areaLegisladores, partidos, divisiones geográficas
Actividad legislativamotion, vote_event, vote, countIniciativas, sesiones de votación, votos individuales, recuentos
Relaciones estructuralesmembership, postAfiliaciones partidistas, cargos en comisiones
Redes de poderrelacion_poder, actor_externo, evento_politicoVínculos de lealtad, actores externos, eventos políticos

Todas las tablas usan claves primarias de texto con una convención de prefijos (ej. P00001, VE_D00001). Las claves foráneas imponen integridad referencial — 14 claves foráneas en total: 4 con ON DELETE CASCADE (tablas hijas) y 10 con ON DELETE RESTRICT (tablas de lookup/padre). Las restricciones CHECK validan enumeraciones y rangos a nivel de base de datos, usando el patrón correcto IS NULL OR campo IN(...) para enumeraciones que admiten NULL.

Diagrama Entidad-Relación

                        +-------------+
                        |    area     |
                        +-------------+
                        | id (PK)     |
                        | nombre      |
                        | parent_id FK|----+
                        +-------------+    |
                               ^          |
                               |          |
+---------------+    +------------------+  |  +------------------+
| organization  |    |      post        |  |  | actor_externo    |
+---------------+    +------------------+  |  +------------------+
| id (PK)       |<---| org_id (FK)      |  |  | id (PK)          |
| nombre        |    | area_id (FK)     |--+  | nombre           |
| abbr          |    +------------------+     | tipo             |
| clasificacion |                             | area_id (FK)     |--+
+------+--------+                             +------------------+  |
       |            +----------------+                              |
       |            |   membership   |                              |
       |            +----------------+                              |
       |            | id (PK)        |                              |
       +---org_id-->| person_id (FK) |-----+                        |
       |            | rol            |     |                        |
       |            | on_behalf_of FK|-----+                        |
       |            +----------------+     |                        |
       |                                   v                        |
       |                            +-------------+                 |
       |                            |    person   |                 |
       |                            +-------------+                 |
       |                            | id (PK)     |                 |
       |                            | nombre      |                 |
       |                            | genero      |                 |
       |                            +-------------+                 |
       |                                   |                        |
       |                                   v                        |
       |       +----------------+   +-------------+                 |
       |       |    vote_event  |   |    vote     |                 |
       |       +----------------+   +-------------+                 |
       +--org_id| id (PK)       |<--| vote_event  |                 |
               | motion_id (FK)|   | voter_id FK |                 |
               +-------+--------+   +-------------+                 |
                       |                                            |
                       v                                            |
                +-------------+       +------------------+          |
                |   motion    |       |   count          |          |
                +-------------+       +------------------+          |
                | id (PK)     |       | id (PK)          |          |
                | texto       |       | vote_event_id FK |----------+
                | clasificacion       | option           |          |
                +-------------+       | group_id (FK) ---+----------+
                                      +------------------+

        +-------------------+     +-------------------+
        | relacion_poder    |     | evento_politico   |
        +-------------------+     +-------------------+
        | id (PK)           |     | id (PK)           |
        | source_type       |     | fecha             |
        | source_id         |     | tipo              |
        | target_type       |     | descripcion       |
        | target_id         |     | motion_id (FK) ---+--> motion
        | tipo              |     +-------------------+
        | peso (1-5)        |
        +-------------------+
  source/target: person | organization | actor_externo

Convención de IDs

Todas las claves primarias usan un prefijo de texto para identificar el tipo de entidad y, donde aplica, la cámara de origen. La porción numérica se rellena con ceros a 5 dígitos.

EntidadPrefijoEjemploRelleno
Vote Event (Diputados)VE_DVE_D000015 dígitos
Vote Event (Senado)VE_SVE_S000015 dígitos
Vote (Diputados)V_DV_D000015 dígitos
Vote (Senado)V_SV_S000015 dígitos
Motion (Diputados)Y_DY_D000015 dígitos
Motion (Senado)Y_SY_S000015 dígitos
Membership (Diputados)M_DM_D000015 dígitos
Membership (Senado)M_SM_S000015 dígitos
PersonPP000015 dígitos
CountCC000015 dígitos

:::note Los prefijos por cámara (_D para Diputados, _S para Senado) se usan en entidades que difieren entre cámaras. Las entidades compartidas como person y count usan un único prefijo. :::

Organizaciones Clave

Estas son las organizaciones políticas principales almacenadas en la tabla organization:

IDAbreviaturaNombreClasificación
O08DiputadosCámara de Diputadosinstitucion
O09SenadoSenado de la Repúblicainstitucion
O10SHHSigamos Haciendo Historiacoalicion
O01MORENAMovimiento de Regeneración Nacionalpartido
O02PTPartido del Trabajopartido
O03PVEMPartido Verde Ecologista de Méxicopartido
O04PANPartido Acción Nacionalpartido
O05PRIPartido Revolucionario Institucionalpartido
O06MCMovimiento Ciudadanopartido
O07PRDPartido de la Revolución Democráticapartido
O11Ind.Independientespartido
O12CONVConvergenciapartido
O13NANueva Alianzapartido
O14ALTAlternativa Socialdemócratapartido
O15PESPartido Encuentro Socialpartido
O16SPSin Partidopartido

:::note O08 (Diputados) y O09 (Senado) están clasificados como institucion, no gobierno. O10 (SHH) es la coalición gobernante. Los partidos históricos (O12–O15) pueden tener fecha de disolución registrada. :::

Referencia por Tabla

area — Divisiones Geográficas

Almacena las divisiones geográficas usadas para distritos electorales y estados. Las áreas se pueden anidar mediante parent_id para modelar la jerarquía: distrito dentro de estado.

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo A01, A02
nombreTEXTNONombre del área
clasificacionTEXTNOClasificación: estado, distrito, circunscripcion
parent_idTEXTClave foránea a area(id), ON DELETE RESTRICT. El distrito apunta a su estado padre
geometryTEXTGeometría GeoJSON opcional
clasificacion IN ('estado', 'distrito', 'circunscripcion')

organization — Organizaciones Políticas

Almacena partidos políticos, cámaras legislativas, coaliciones e instituciones de gobierno. Esta tabla no tiene claves foráneas.

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo O01, O02
nombreTEXTNONombre oficial (UNIQUE)
abbrTEXTAbreviatura (MORENA, PAN, etc.)
clasificacionTEXTNOTipo: partido, bancada, coalicion, gobierno, institucion, otro
fundacionTEXTFecha de fundación en ISO 8601
disolucionTEXTFecha de disolución en ISO 8601
clasificacion IN ('partido', 'bancada', 'coalicion', 'gobierno', 'institucion', 'otro')

person — Legisladores

Tabla central de legisladores (diputados y senadores). Incluye datos demográficos, tipo de curul, corrientes internas y evaluaciones de vulnerabilidad.

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo P01, P02
nombreTEXTNONombre completo
fecha_nacimientoTEXTFecha de nacimiento en ISO 8601
generoTEXTGénero: M, F, NB
curul_tipoTEXTTipo de curul: mayoria_relativa, plurinominal, suplente
circunscripcionINTEGERNúmero de circunscripción electoral (1-5)
start_dateTEXTInicio del período legislativo
end_dateTEXTFin del período legislativo
corriente_internaTEXTCorriente interna: Monreal, AMLO, Sheinbaum, institucionalista, o NULL
vulnerabilidadTEXTVulnerabilidad política: alta, media, baja, o NULL
observacionesTEXTNotas de texto libre
identifiers_jsonTEXTObjeto JSON con IDs externos
genero IN ('M', 'F', 'NB')
curul_tipo IN ('mayoria_relativa', 'plurinominal', 'suplente')
circunscripcion BETWEEN 1 AND 5
corriente_interna IS NULL OR corriente_interna IN ('Monreal', 'AMLO', 'Sheinbaum', 'institucionalista')
vulnerabilidad IS NULL OR vulnerabilidad IN ('alta', 'media', 'baja')

:::caution Los campos de enumeración que admiten NULL (corriente_interna, vulnerabilidad) usan el patrón campo IS NULL OR campo IN(...). El patrón anterior campo IN('val1', 'val2', NULL) es ineficaz porque NULL IN (...) evalúa a NULL (no FALSE), saltando la restricción por completo. :::

:::tip El campo corriente_interna rastrea las facciones políticas dentro de los partidos. Esto es particularmente relevante para MORENA, donde las divisiones internas (monrealistas, sheinbaumistas, etc.) afectan significativamente el comportamiento de voto. :::

membership — Relaciones Persona-Organización

Vincula personas con organizaciones incluyendo rol, rango de fechas y representación opcional de coalición.

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo M01, M02
person_idTEXTNOClave foránea a person(id), ON DELETE CASCADE ON UPDATE CASCADE
org_idTEXTNOClave foránea a organization(id), ON DELETE CASCADE ON UPDATE CASCADE
rolTEXTNORol: diputado, senador, suplente
labelTEXTDescripción legible de la membresía
start_dateTEXTNOFecha de inicio de la membresía
end_dateTEXTFecha de fin. NULL significa activa actualmente
on_behalf_ofTEXTClave foránea a organization(id), ON DELETE RESTRICT ON UPDATE RESTRICT. Miembro de coalición votando bajo otro partido

:::note La columna on_behalf_of maneja la representación de coaliciones. Cuando un diputado del Partido A fue electo bajo una coalición con el Partido B, este campo registra la entidad de coalición. :::

post — Cargos Legislativos

Representa cargos formales dentro de organizaciones (ej. presidente de comisión, secretario de mesa directiva).

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo T01, T02
org_idTEXTNOClave foránea a organization(id), ON DELETE RESTRICT ON UPDATE RESTRICT
area_idTEXTClave foránea a area(id), ON DELETE RESTRICT ON UPDATE RESTRICT para cargos vinculados geográficamente
labelTEXTNOTítulo del cargo
start_dateTEXTNOFecha de inicio
end_dateTEXTFecha de fin. NULL significa que el cargo aún se mantiene

motion — Iniciativas Legislativas

Almacena propuestas legislativas incluyendo reformas constitucionales, leyes secundarias y legislación ordinaria.

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo Y01, Y02
textoTEXTNOTexto completo o resumen de la iniciativa
clasificacionTEXTNOTipo: reforma_constitucional, ley_secundaria, ordinaria, otra
requirementTEXTNOUmbral de votación requerido: mayoria_simple, mayoria_calificada, unanime
resultTEXTResultado: aprobada, rechazada, pendiente, retirada, o NULL
dateTEXTFecha de la iniciativa
legislative_sessionTEXTEtiqueta de la sesión legislativa, ej. “LXVI Legislatura”
fuente_urlTEXTURL fuente de la iniciativa
clasificacion IN ('reforma_constitucional', 'ley_secundaria', 'ordinaria', 'otra')
requirement IN ('mayoria_simple', 'mayoria_calificada', 'unanime')
result IS NULL OR result IN ('aprobada', 'rechazada', 'pendiente', 'retirada')

vote_event — Eventos de Votación

Registra sesiones de votación. Cada evento de votación está vinculado a una motion y una organización (cámara).

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo VE01, VE02
motion_idTEXTNOClave foránea a motion(id), ON DELETE RESTRICT ON UPDATE RESTRICT
start_dateTEXTNOFecha y hora de la votación
organization_idTEXTNOClave foránea a organization(id), ON DELETE RESTRICT ON UPDATE RESTRICT (cámara)
resultTEXTResultado: aprobada, rechazada, empate, o NULL
sitl_idINTEGERID de votación del SITL (Sistema de Información Legislativa)
voter_countINTEGERNúmero de votantes registrados
legislaturaTEXTCódigo de legislatura: LX, LXI, …, LXVI
requirementTEXTUmbral de votación: mayoria_simple, mayoria_calificada, unanime, o NULL
source_idTEXTID del portal original, usado para deduplicación
identifiers_jsonTEXTJSON con identificadores externos
result IS NULL OR result IN ('aprobada', 'rechazada', 'empate')
requirement IS NULL OR requirement IN ('mayoria_simple', 'mayoria_calificada', 'unanime')

vote — Votos Individuales

Registra el voto individual de cada legislador dentro de un evento de votación.

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo V01, V02
vote_event_idTEXTNOClave foránea a vote_event(id), ON DELETE CASCADE ON UPDATE CASCADE
voter_idTEXTNOClave foránea a person(id), ON DELETE RESTRICT ON UPDATE RESTRICT
optionTEXTNOVoto emitido: a_favor, en_contra, abstencion, ausente
groupTEXTAfiliación partidista al momento de la votación
option IN ('a_favor', 'en_contra', 'abstencion', 'ausente')

:::tip La columna group captura la afiliación partidista al momento de la votación, que puede diferir de la membresía actual de la persona. Esto es esencial para rastrear legisladores que cambian de partido. :::

count — Recuentos de Votos por Grupo

Tallies agregadas de votos por opción y por grupo (partido). Usadas para consultas estadísticas rápidas sin escanear filas individuales de vote.

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo C01, C02
vote_event_idTEXTNOClave foránea a vote_event(id), ON DELETE CASCADE ON UPDATE CASCADE
optionTEXTNOOpción de voto: a_favor, en_contra, abstencion, ausente
valueINTEGERNOConteo de votos para esta opción (>= 0)
group_idTEXTClave foránea a organization(id), ON DELETE RESTRICT ON UPDATE RESTRICT. El partido que se está contabilizando
option IN ('a_favor', 'en_contra', 'abstencion', 'ausente')
value >= 0

actor_externo — Actores Externos

Almacena actores políticos fuera del legislativo que influyen en los resultados legislativos: gobernadores, alcaldes, ex presidentes, jueces, dirigentes partidistas.

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo AE01, AE02
nombreTEXTNONombre completo
tipoTEXTNOTipo de actor: gobernador, alcalde, ex_presidente, dirigente, juez, otro
area_idTEXTClave foránea a area(id), ON DELETE RESTRICT ON UPDATE RESTRICT. Jurisdicción geográfica
start_dateTEXTInicio del período de relevancia
end_dateTEXTFin del período de relevancia
observacionesTEXTNotas de texto libre
tipo IN ('gobernador', 'alcalde', 'ex_presidente', 'dirigente', 'juez', 'otro')

relacion_poder — Redes de Poder

Tabla de relaciones polimórficas que modela conexiones políticas entre cualquier combinación de personas, organizaciones y actores externos. Esta es una extensión Popolo específica de este proyecto.

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo RP01, RP02
source_typeTEXTNOTipo de entidad origen: person, organization, actor_externo
source_idTEXTNOID de la entidad origen
target_typeTEXTNOTipo de entidad destino: person, organization, actor_externo
target_idTEXTNOID de la entidad destino
tipoTEXTNOTipo de relación: lealtad, presion, influencia, familiar, clientelismo, conflicto, alianza
pesoINTEGERNOPeso/fuerza de la relación (1-5)
start_dateTEXTInicio de la relación
end_dateTEXTFin de la relación
fuenteTEXTFuente de los datos de la relación
notaTEXTNotas adicionales
source_type IN ('person', 'organization', 'actor_externo')
target_type IN ('person', 'organization', 'actor_externo')
tipo IN ('lealtad', 'presion', 'influencia', 'familiar', 'clientelismo', 'conflicto', 'alianza')
peso BETWEEN 1 AND 5

:::note relacion_poder usa un diseño polimórfico: source_type/source_id y target_type/target_id pueden apuntar a cualquiera de tres tablas. Esto sacrifica la aplicación de claves foráneas a nivel SQL a cambio de flexibilidad para modelar redes políticas complejas. :::

evento_politico — Eventos Políticos

Registra eventos políticos significativos que pueden afectar el comportamiento o los resultados legislativos.

ColumnaTipoNulableDescripción
idTEXTNO (PK)Clave primaria, prefijo EP01, EP02
fechaTEXTNOFecha del evento
tipoTEXTNOTipo de evento (texto libre)
descripcionTEXTNODescripción del evento
consecuenciaTEXTConsecuencias esperadas u observadas
fuente_urlTEXTURL fuente
motion_idTEXTClave foránea a motion(id), ON DELETE RESTRICT ON UPDATE RESTRICT. Iniciativa legislativa relacionada

Índices

Los siguientes 18 índices están definidos para optimizar los patrones de consulta más comunes, incluyendo 2 índices compuestos únicos para deduplicación:

Nombre del ÍndiceTablaColumnasTipo
idx_membership_personmembershipperson_idEstándar
idx_membership_orgmembershiporg_idEstándar
idx_vote_event_motionvote_eventmotion_idEstándar
idx_vote_event_sourcevote_eventsource_idEstándar
idx_vote_event_legislaturavote_eventlegislaturaEstándar
idx_vote_event_orgvote_eventorganization_idEstándar
idx_vote_event_start_datevote_eventstart_dateEstándar
idx_vote_votervotevoter_idEstándar
idx_vote_eventvotevote_event_idEstándar
idx_vote_uniquevotevote_event_id, voter_idUNIQUE
idx_count_eventcountvote_event_idEstándar
idx_count_groupcountgroup_idEstándar
idx_count_uniquecountvote_event_id, option, group_idUNIQUE
idx_relacion_sourcerelacion_podersource_type, source_idEstándar
idx_relacion_targetrelacion_podertarget_type, target_idEstándar
idx_relacion_tiporelacion_podertipoEstándar
idx_person_corrientepersoncorriente_internaEstándar
idx_actor_tipoactor_externotipoEstándar

:::tip Los índices compuestos únicos (idx_vote_unique e idx_count_unique) previenen registros duplicados a nivel de base de datos. idx_vote_unique asegura que un legislador no pueda votar dos veces en el mismo evento, e idx_count_unique asegura un solo conteo por opción por grupo por evento. :::

Triggers

Cuatro triggers imponen consistencia de fechas entre los campos start_date y end_date. Usan el patrón SELECT CASE WHEN ... THEN RAISE(ABORT, ...) END con una cláusula WHEN a nivel de trigger:

trg_person_dates

Se ejecuta BEFORE INSERT en person. Garantiza que end_date no sea anterior a start_date cuando ambos están presentes.

CREATE TRIGGER trg_person_dates
BEFORE INSERT ON person
WHEN NEW.end_date IS NOT NULL AND NEW.start_date IS NOT NULL
BEGIN
    SELECT CASE WHEN NEW.end_date < NEW.start_date
    THEN RAISE(ABORT, 'end_date debe ser >= start_date en person')
    END;
END;

trg_person_dates_update

Misma validación en UPDATE de person.

CREATE TRIGGER trg_person_dates_update
BEFORE UPDATE ON person
WHEN NEW.end_date IS NOT NULL AND NEW.start_date IS NOT NULL
BEGIN
    SELECT CASE WHEN NEW.end_date < NEW.start_date
    THEN RAISE(ABORT, 'end_date debe ser >= start_date en person')
    END;
END;

trg_membership_dates

Se ejecuta BEFORE INSERT en membership. Previene membresías con rangos de fechas invertidos.

CREATE TRIGGER trg_membership_dates
BEFORE INSERT ON membership
WHEN NEW.end_date IS NOT NULL AND NEW.start_date IS NOT NULL
BEGIN
    SELECT CASE WHEN NEW.end_date < NEW.start_date
    THEN RAISE(ABORT, 'end_date debe ser >= start_date en membership')
    END;
END;

trg_membership_dates_update

Misma validación en UPDATE de membership.

CREATE TRIGGER trg_membership_dates_update
BEFORE UPDATE ON membership
WHEN NEW.end_date IS NOT NULL AND NEW.start_date IS NOT NULL
BEGIN
    SELECT CASE WHEN NEW.end_date < NEW.start_date
    THEN RAISE(ABORT, 'end_date debe ser >= start_date en membership')
    END;
END;

Estadísticas

Volúmenes actuales de datos en la base de datos de producción:

MétricaValor
Votos individuales~3,510,053
Eventos de votación~9,437
Personas~4,840
Organizaciones~20+
Legislaturas cubiertas7 (LX a LXVI, 2006-2027)
Tests302 passing
Índices18 (incluyendo 2 UNIQUE)
Claves foráneas14 (3 CASCADE + 11 RESTRICT)