# AGENTS.md

This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.

## Project Overview

SGETECBA is an academic management system (Sistema de Gestión Estudiantil) for TECBA institution. It handles student registration, academic structure, attendance, dropouts, Excel imports, and institutional reporting.

## Commands

### Backend

```bash
cd backend

# Run dev server
python -m uvicorn app.main:app --host localhost --port 8000 --reload

# Run tests
pytest

# Run a single test file
pytest tests/test_auth_service.py -v

# DB migrations
alembic upgrade head
alembic revision --autogenerate -m "description"

# Seed initial data
python seed_initial_users.py
```

The backend requires a `backend/.env` file with at minimum `JWT_SECRET_KEY`, `DB_PASSWORD`, and DB connection vars (see `backend/app/core/config.py` for all accepted env vars).

### Frontend

```bash
cd frontend

npm run dev        # dev server on port 5173
npm run build      # TypeScript check + Vite build
npm run preview    # serve built dist
```

Frontend requires `frontend/.env` with `VITE_API_URL=http://localhost:8000/api/v1` (and optionally `VITE_REALTIME_URL`).

## Architecture

### Backend (`backend/`)

FastAPI + SQLAlchemy 2 + PostgreSQL (psycopg3) + Alembic. Python virtual environment is expected to be set up manually.

```
backend/app/
├── main.py              # FastAPI app, CORS, middleware registration
├── core/
│   ├── config.py        # Pydantic-settings (reads from .env); JWT_SECRET_KEY must be non-empty
│   ├── database.py      # SQLAlchemy engine + Base + get_db()
│   ├── deps.py          # get_current_user(), require_roles() dependency factories
│   ├── roles.py         # Role enum/constants (ADMIN, SECRETARIA, DOCENTE, DIRECCION)
│   └── security.py      # bcrypt hashing + JWT encode/decode
├── models/
│   └── entities.py      # All SQLAlchemy ORM models in one file
├── schemas/             # Pydantic v2 request/response schemas
├── crud/                # DB query helpers
├── services/            # Business logic layer
├── middleware/
│   └── auditoria.py     # Middleware that logs all write operations to audit_logs
└── api/v1/
    ├── api.py           # Router aggregator
    └── routes/          # One file per domain: auth, estudiantes, carreras,
                         # asignaturas, matriculaciones, asistencias, docentes,
                         # docente (self-service), dashboard, importacion,
                         # periodos_academicos, oferta_asignaturas, niveles_academicos,
                         # grupos, usuarios, pensum, sesiones, realtime, etc.
```

All API routes are prefixed `/api/v1`. Authorization uses `require_roles(...)` as a FastAPI dependency — unauthorized access returns 403.

### Frontend (`frontend/src/`)

React 18 + TypeScript + Vite + Tailwind CSS + React Router v6 + axios.

```
frontend/src/
├── api/
│   └── client.ts        # ALL API calls + axios instance + authStorage (localStorage)
├── auth/
│   └── roles.ts         # Frontend role constants matching backend roles
├── routes/
│   └── AppRouter.tsx    # React Router v6 route definitions
├── pages/               # One component per page (Estudiantes, Carreras, Asignaturas,
│                        # Dashboard, Login, Docentes, Grupos, Matriculaciones,
│                        # NivelesAcademicos, Ofertas, Periodos, Usuarios, Asistencias)
├── components/          # Shared UI: Sidebar, DataTable, PageHeader, SearchBar,
│                        # PaginationControls, StatCard, Toast, ImportarDatos
├── realtime/            # SSE/WebSocket: RealtimeProvider, useRealtimeRefresh,
│                        # useRealtimeSubscription
├── layouts/             # Layout wrappers
├── hooks/               # Custom React hooks
└── types/
    └── api.ts           # TypeScript interfaces for all API responses
```

**Auth flow**: `POST /auth/login` → token stored in `localStorage` under key `sge_tecba_token`, user object under `sge_tecba_user`. The axios instance in `client.ts` auto-attaches the Bearer token and redirects to `/login` on 401.

### Database

PostgreSQL only (no SQLite in production). Connection built from `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` env vars, or a full `DATABASE_URL`. Alembic reads settings from `backend/.env` via `app.core.config`.

All ORM models are defined in `backend/app/models/entities.py`. Alembic autogenerate targets `Base.metadata`.

## Roles & Access Control

| Role | Key permissions |
|------|----------------|
| `ADMIN` | Full access to all modules |
| `SECRETARIA` | Students, enrollments, imports |
| `DOCENTE` | Own assignments + attendance registration |
| `DIRECCION` | Reports and dropout tracking |

## Key Patterns

- **Pagination**: API returns `PaginatedResponse<T>` with `items`, `total`, `page`, `page_size`. Frontend passes `page` and `page_size` query params.
- **Audit logging**: All write operations are captured by `AuditoriaMiddleware` — avoid bypassing it.
- **Excel import**: Two-step: `POST /importacion` (preview, returns batch_id) → confirm. Only validated rows without errors are committed.
- **Realtime**: SSE stream at `/api/v1/realtime/stream`; `RealtimeProvider` in frontend manages the connection. Use `useRealtimeRefresh` to subscribe page-level refreshes.
- **Oferta de asignatura**: The join entity that connects `asignatura + carrera + periodo + nivel + turno + grupo`. Matriculaciones (enrollments) reference this entity.
