-
Notifications
You must be signed in to change notification settings - Fork 0
Documentación Técnica
El Informe de Carga Docente es una funcionalidad que permite visualizar información detallada sobre la distribución de materias y estudiantes asignados a cada docente del sistema ProfeSort.
Características principales:
- Visualización de estadísticas generales (totales y promedios)
- Tabla detallada por docente con información de carga
- Datos en tiempo real mediante API REST
- Manejo robusto de errores y estados de carga
- Interfaz responsive con Bootstrap
graph TD
A[Componente AdminDocente] --> B[AdminDocenteService]
B --> C[HttpClient]
C --> D[Mock API Server]
D --> E[JSON Database]
F[Usuario] --> G[Click 'Ver Informe']
G --> H[toggleInformeCarga()]
H --> I[cargarInformeCarga()]
I --> J[getDocentesCarga()]
J --> K[HTTP GET /carga]
K --> L[Procesar Respuesta]
L --> M[Actualizar UI]
- Frontend: Angular 17 con Reactive Forms
- Servicio: AdminDocenteService para comunicación HTTP
- Mock API: JSON Server para simular backend
- Base de datos: Archivo JSON con datos mock
1. Activación del Informe
// Usuario hace clic en "Ver Informe de Carga"
toggleInformeCarga(): void {
this.mostrarInformeCarga = !this.mostrarInformeCarga;
if (this.mostrarInformeCarga && this.docentesCarga.length === 0) {
this.cargarInformeCarga(); // Cargar datos si no existen
}
}
2. Solicitud de Datos
// Llamada al servicio para obtener datos
cargarInformeCarga(): void {
this.cargandoDatos = true;
this.errorCarga = '';
this.adminDocenteService.getDocentesCarga().subscribe({
next: (data: DocenteCarga[]) => {
this.docentesCarga = data;
this.cargandoDatos = false;
},
error: (error) => {
this.errorCarga = error.message;
this.cargandoDatos = false;
}
});
}
3. Procesamiento de Datos
// Cálculo de estadísticas en tiempo real
obtenerEstadisticas() {
const totalDocentes = this.docentesCarga.length;
const docentesActivos = this.docentesCarga.filter(d => d.estado === 'Activo').length;
const totalMaterias = this.docentesCarga.reduce((sum, d) => sum + d.cantidadMaterias, 0);
const totalEstudiantes = this.docentesCarga.reduce((sum, d) => sum + d.cantidadEstudiantes, 0);
return { totalDocentes, docentesActivos, totalMaterias, totalEstudiantes };
}
4. Renderización de la UI
- Estadísticas generales: Cards con totales
- Tabla detallada: Información por docente
- Estados visuales: Loading, error, datos vacíos
Estructura de datos esperada (DocenteCarga):
interface DocenteCarga {
id: number; // Identificador único del docente
nombre: string; // Nombre completo del docente
email: string; // Correo electrónico
legajo: string; // Número de legajo institucional
estado: 'Activo' | 'Inactivo'; // Estado del docente
cantidadMaterias: number; // Número de materias asignadas
cantidadEstudiantes: number; // Total de estudiantes a cargo
materias: string[]; // Array con nombres de materias
fechaIngreso?: string; // Fecha de ingreso (opcional)
departamento?: string; // Departamento (opcional)
}
Ejemplo de datos JSON:
{
"carga": [
{
"id": 1,
"nombre": "Karina del Valle Quinteros",
"email": "karinaq38@gmail.com",
"legajo": "DOC001",
"estado": "Activo",
"cantidadMaterias": 3,
"cantidadEstudiantes": 45,
"materias": ["Matemática", "Física", "Química"],
"fechaIngreso": "2020-03-15",
"departamento": "Ciencias Exactas"
},
{
"id": 2,
"nombre": "Juan Pablo Sanchez Brandán",
"email": "sanchezbrandan@gmail.com",
"legajo": "DOC002",
"estado": "Activo",
"cantidadMaterias": 2,
"cantidadEstudiantes": 38,
"materias": ["Historia", "Geografía"],
"fechaIngreso": "2019-08-10",
"departamento": "Ciencias Sociales"
}
]
}
GET /carga
Descripción: Obtiene la lista completa de docentes con información de carga
URL: http://localhost:3001/carga
Método: GET
Headers: Content-Type: application/json
Respuesta exitosa (200):
[
{
"id": 1,
"nombre": "Karina del Valle Quinteros",
"email": "karinaq38@gmail.com",
"legajo": "DOC001",
"estado": "Activo",
"cantidadMaterias": 3,
"cantidadEstudiantes": 45,
"materias": ["Matemática", "Física", "Química"],
"fechaIngreso": "2020-03-15",
"departamento": "Ciencias Exactas"
}
]
Códigos de error:
- 404: Recurso no encontrado
- 500: Error interno del servidor
- 0: Error de conexión
Servicio AdminDocenteService:
@Injectable({ providedIn: 'root' })
export class AdminDocenteService {
private apiUrl = 'http://localhost:3001/carga';
constructor(private http: HttpClient) {}
getDocentesCarga(): Observable<DocenteCarga[]> {
return this.http.get<DocenteCarga[]>(this.apiUrl)
.pipe(
retry(1), // Reintentar una vez
catchError(this.handleError) // Manejo de errores
);
}
private handleError(error: HttpErrorResponse) {
// Lógica de manejo de errores...
}
}
- docentesCarga: DocenteCarga[] = [] // Array de datos de carga
- cargandoDatos = false // Estado de carga
- errorCarga = '' // Mensaje de error
- mostrarInformeCarga = false // Visibilidad del informe
- toggleInformeCarga(): Alternar visibilidad
- cargarInformeCarga(): Obtener datos del API
- obtenerEstadisticas(): Calcular estadísticas
El proyecto utiliza dos instancias del mock-api para diferentes propósitos:
- Puerto 3000: API general del sistema
- Puerto 3001: API específica para Informe de Carga Docente
ProfeSort/
├── mock-api/
│ ├── db.json # Datos generales (puerto 3000)
│ ├── db-carga.json # Datos de carga docente (puerto 3001)
│ ├── routes.json # Configuración de rutas personalizadas
│ ├── middleware.js # Middlewares personalizados (CORS, etc.)
│ └── package.json # Configuración del proyecto mock
cd mock-api
npm install json-server@0.17.4 concurrently --save{
"name": "profesort-mock-api",
"version": "1.0.0",
"description": "Mock API para ProfeSort",
"scripts": {
"start": "concurrently \"npm run api-general\" \"npm run api-carga\"",
"api-general": "npx json-server@0.17.4 --watch db.json --routes routes.json --middlewares middleware.js --port 3000",
"api-carga": "npx json-server@0.17.4 --watch db-carga.json --routes routes.json --middlewares middleware.js --port 3001",
"dev": "npm start"
},
"dependencies": {
"json-server": "^0.17.4",
"concurrently": "^8.2.0"
}
}cd mock-api
npm install
npm start # Levanta puerto 3000 y 3001 a la vez# Terminal 1 - API General (puerto 3000)
cd mock-api
npm run api-general
# Terminal 2 - API Carga (puerto 3001)
cd mock-api
npm run api-carga# Terminal 1 - API General
cd mock-api
npx json-server@0.17.4 --watch db.json --routes routes.json --middlewares middleware.js --port 3000
# Terminal 2 - API Carga Docente
cd mock-api
npx json-server@0.17.4 --watch db-carga.json --routes routes.json --middlewares middleware.js --port 3001-
API General:
http://localhost:3000 -
API Carga Docente:
http://localhost:3001/carga
Para comprobar que ambos servidores están funcionando:
# Verificar puerto 3000
curl http://localhost:3000
# Verificar puerto 3001
curl http://localhost:3001/carga- Error de conexión (Status 0): "No se puede conectar al servidor. Verifique que el mock API esté ejecutándose en el puerto 3001."
- Recurso no encontrado (Status 404): "Recurso no encontrado."
- Error del servidor (Status 500): "Error interno del servidor."
- Retry automático (1 reintento)
- Mensajes descriptivos
- Logging (console.error)
- Botón de reintento en UI
Ejemplo 1: Carga exitosa
- Usuario hace clic en "Ver Informe de Carga"
- toggleInformeCarga()
- cargarInformeCarga()
- GET http://localhost:3001/carga
- Respuesta procesada → tabla + estadísticas
Ejemplo 2: Manejo de error
- Mock server apagado
- HTTP request falla con status 0
- Mensaje: "No se puede conectar al servidor..."
- Botón "Reintentar"
- Nuevo intento con cargarInformeCarga()
Ejemplo 3: Datos vacíos
- Mock responde []
- Se ocultan estadísticas
- Mensaje: "No hay datos disponibles"
**Problema: "Error de conexión" ** Solución:
- Verificar server en puerto 3001
- Comprobar db.json válido
- Revisar CORS
curl http://localhost:3001/carga
**Problema: "Property does not exist" ** Solución:
- Revisar métodos implementados
- Comprobar imports
- Revisar interfaces
**Problema: "JSON Server options not supported" ** Solución:
- Instalar versión: npm install -g json-server@0.17.4
- Usar JSON simplificado
**Problema: "Datos no se muestran" ** Solución:
- Revisar estructura de db.json
- Comprobar URL del servicio
- Confirmar HttpClient en app.config.ts
El Principio de Inversión de Dependencias dice básicamente que:
“Los módulos de alto nivel no deberían depender directamente de los módulos de bajo nivel.
Ambos deberían depender de abstracciones.
Las abstracciones no deben depender de los detalles; los detalles deben depender de las abstracciones.”
En palabras más simples: tu código no debería estar atado a implementaciones concretas. Es mejor trabajar sobre interfaces o capas intermedias para que puedas cambiar cómo se hacen las cosas sin romper todo lo demás.
En nuestro backend con Django REST Framework, este principio aparece sobre todo en cómo las vistas (views) interactúan con los serializers y con nuestro sistema centralizado de respuestas HTTP.
Esto nos permite:
- No depender directamente de cómo se serializan los datos.
- Cambiar la lógica de serialización o de respuesta sin afectar la vista.
- Mantener el código limpio y fácil de mantener.
Ubicación: backend/apps/gestion_academica/views.py
class MateriaListCreateView(APIView):
def get(self, request):
materias = Materia.objects.all()
serializer = MateriaSerializer(materias, many=True)
return success_response(data=serializer.data)
def post(self, request):
serializer = MateriaSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return success_response(data=serializer.data, status_code=status.HTTP_201_CREATED)
return error_response(
error=serializer.errors,
message="Error al crear materia",
status_code=status.HTTP_400_BAD_REQUEST
)
---
## 🔍 Análisis del Principio DIP
### ¿Por qué este código cumple con DIP?
#### 1. Abstracción de Respuestas HTTP
La vista **no construye manualmente las respuestas HTTP**, sino que usa las funciones `success_response()` y `error_response()`.
Esto **desacopla la vista de los detalles de la respuesta**, cumpliendo DIP.
## 2. Abstracción de Serialización
La vista **no conoce cómo se transforma un objeto Materia a JSON**. Eso lo hace `MateriaSerializer`.
Cambiar la estructura del JSON solo requiere modificar el serializer, **sin tocar la vista**.
---
## 3. Uso de la clase base `APIView`
Heredamos de `APIView`, que abstrae detalles de bajo nivel como:
- Parseo de requests
- Manejo de diferentes tipos de contenido
- Autenticación y permisos
Así, la vista se enfoca solo en **la lógica de negocio**, mientras los detalles técnicos quedan manejados por la abstracción.
**Resumen:** La vista depende de **abstracciones, no de implementaciones concretas**, logrando flexibilidad, menor acoplamiento y fácil mantenimiento.
---
## 📝 Notas Adicionales
### ⚙️ Rendimiento
- Carga inicial solo al abrir el informe
- Estadísticas en tiempo real (lado cliente)
- Considerar **caché** si los datasets son grandes
---
### 🚀 Futuras mejoras
- Filtros por **departamento, estado y fechas**
- Exportación: **PDF, Excel, CSV**
- Paginación de resultados
- Gráficos dinámicos con **Chart.js o D3**
- Actualización en **tiempo real** usando **WebSockets**
---
### 🔒 Seguridad
- Usar **JWT** en producción
- Validar todos los datos en backend
- Sanitización de inputs para evitar inyecciones
- Aplicar **rate limiting** en la API
---
### 📚 Referencias
- [Angular HttpClient Documentation](https://angular.io/guide/http)
- [JSON Server Documentation](https://github.com/typicode/json-server)
- [RxJS Operators](https://rxjs.dev/guide/operators)
- [Bootstrap Documentation](https://getbootstrap.com/docs/)
---
**Última actualización:** Octubre 2025
**Versión:** 1.0
**Autor:** Equipo de Desarrollo ProCoders