Centro de Préstamo Tecnológico
Esta actividad NO es un simulacro de examen. Su extensión supera lo que se solicitará en el examen de la convocatoria ordinaria. Además, en este modelo se facilita información para la autocorrección de la prueba (rúbrica detallada).
Se pretende que sirva de preparación para la prueba real ya que el formato de enunciado será similar exceptuando los detalles de la rúbrica y los elementos válidos e inválidos esperados en los sets de datos.
Contexto
El centro educativo AulaTech dispone de un pequeño servicio de préstamo de material tecnológico para alumnado y profesorado. Para mejorar su gestión interna, necesita una aplicación en Python que permita administrar usuarios, materiales y préstamos.
Se deberá implementar el sistema siguiendo los principios de la Programación Orientada a Objetos.
No se permite acceder directamente a los atributos de las clases desde fuera de ellas. Se deberán utilizar propiedades, getters, setters o métodos específicos cuando sea necesario.
Archivos entregados
Se proporciona la siguiente estructura de proyecto. 🔗 Descargar Recursos
examen_final_python/
│
├── main.py
├── usuario.py
├── materiales.py
├── centro.py
├── carga_datos.py
│
├── data/
│ ├── usuarios.csv
│ ├── materiales.csv
│ └── prestamos.json
El archivo main.py ya estará implementado y no se deberá modificar.
El alumnado deberá completar los archivos:
usuario.py
materiales.py
centro.py
carga_datos.py
Parte 1: Gestión de usuarios
Modifica el fichero usuario.py para crear la clase Usuario.
Clase Usuario
Atributos de instancia
id_usuario
nombre
tipo
penalizacion
prestamos_activos
id_usuario: cadena de texto.nombre: cadena de texto.tipo: cadena de texto. Puede ser"alumno"o"profesor".penalizacion: número real. Representa el dinero pendiente de pagar por retrasos.prestamos_activos: número entero. Por defecto será 0.
Restricciones
- El identificador no puede estar vacío.
- El nombre no puede estar vacío.
- El tipo solo podrá tener uno de estos valores:
alumno
profesor
- La penalización no puede ser negativa.
- Los préstamos activos no pueden ser negativos.
- Si se intenta asignar un tipo no válido, se lanzará un
ValueError. - Si se intenta asignar una penalización negativa, se lanzará un
ValueError. - Si se intenta asignar un número de préstamos activos negativo, se lanzará un
ValueError.
Métodos de la clase Usuario
aumentar_prestamos()
Debe aumentar en 1 el número de préstamos activos.
reducir_prestamos()
Debe reducir en 1 el número de préstamos activos.
Si el usuario no tiene préstamos activos, se lanzará un ValueError.
sumar_penalizacion(cantidad)
Debe sumar la cantidad indicada a la penalización del usuario.
La cantidad debe ser mayor o igual que 0. Si no se cumple, se lanzará un ValueError.
pagar_penalizacion(cantidad)
Debe restar la cantidad indicada a la penalización del usuario.
La cantidad debe ser mayor que 0.
No se podrá pagar más dinero del que el usuario debe.
Si alguna condición no se cumple, se lanzará un ValueError.
Método mágico
Implementa el método mágico necesario para que, al imprimir un usuario, se muestre con el siguiente formato:
<nombre> (<tipo>) - Penalización: <penalizacion> €
Ejemplo:
Marta (alumno) - Penalización: 3.5 €
Parte 2: Materiales tecnológicos
Modifica el fichero materiales.py.
Se deberá crear una clase base Material y dos clases hijas:
Portatil
Tablet
Clase Material
Atributo compartido
Todos los materiales tendrán un atributo compartido llamado:
centro
Su valor por defecto será:
AulaTech
Atributos de instancia
id_material
nombre
estado
valor
disponible
id_material: cadena de texto.nombre: cadena de texto.estado:"nuevo","bueno"o"revisar".valor: número real.disponible: booleano. Por defecto seráTrue.
Restricciones
- El identificador no puede estar vacío.
- El nombre no puede estar vacío.
- El estado solo podrá tener uno de estos valores:
nuevo
bueno
revisar
- El valor debe ser mayor que 0.
disponibledebe ser un valor booleano.- Si alguna de estas restricciones no se cumple, se lanzará un
ValueError.
Métodos de la clase Material
calcular_penalizacion(dias_retraso)
Método polimórfico.
En la clase base debe lanzar una excepción NotImplementedError.
prestar()
Debe marcar el material como no disponible.
Si el material no está disponible, deberá lanzar un ValueError.
devolver()
Debe marcar el material como disponible.
Si el material ya estaba disponible, deberá lanzar un ValueError.
necesita_revision()
Debe devolver True si el estado del material es "revisar" y False en caso contrario.
Clases hijas
Clase Portatil
Hereda de Material.
Atributo específico
sistema_operativo
Será una cadena de texto que podrá tener los valores:
Windows
Linux
MacOS
Si el valor no es válido, se lanzará un ValueError.
Sobrescritura de
calcular_penalizacion(dias_retraso)
Los portátiles tendrán una penalización de:
5 € por cada día de retraso
Además, si el portátil está en estado "nuevo", se añadirá un suplemento fijo de 10 €.
Si los días de retraso son menores o iguales a 0, deberá devolver 0.
Ejemplo:
Portátil nuevo con 3 días de retraso → 3 * 5 + 10 = 25 €
Clase Tablet
Hereda de Material.
Atributo específico
tiene_lapiz
Será un valor booleano.
Sobrescritura de
calcular_penalizacion(dias_retraso)
Las tablets tendrán una penalización de:
3 € por cada día de retraso
Además, si la tablet tiene lápiz, se añadirá un suplemento fijo de 5 €.
Si los días de retraso son menores o iguales a 0, deberá devolver 0.
Ejemplo:
Tablet con lápiz y 4 días de retraso → 4 * 3 + 5 = 17 €
Parte 3: Objeto CentroPrestamo
Crea la clase CentroPrestamo en el fichero centro.py.
El centro deberá gestionar usuarios, materiales y préstamos.
Atributos
usuarios
materiales
prestamos
usuarios: diccionario donde la clave será el ID del usuario y el valor será un objetoUsuario.materiales: diccionario donde la clave será el ID del material y el valor será un objetoPortatiloTablet.prestamos: lista de diccionarios con la información de los préstamos realizados.
No se podrán modificar directamente estos atributos desde fuera de la clase.
Parte 4: Métodos del centro
La clase CentroPrestamo deberá contener los siguientes métodos.
agregar_usuario(usuario)
Recibe un objeto Usuario.
Debe añadirlo al diccionario de usuarios.
Si ya existe un usuario con el mismo ID, se lanzará un ValueError.
agregar_material(material)
Recibe un objeto de tipo Material.
Debe añadirlo al diccionario de materiales.
Si ya existe un material con el mismo ID, se lanzará un ValueError.
buscar_usuario(id_usuario)
Recibe el ID de un usuario.
Debe devolver el objeto Usuario correspondiente.
Si no existe, se lanzará un ValueError.
buscar_material(id_material)
Recibe el ID de un material.
Debe devolver el objeto Material correspondiente.
Si no existe, se lanzará un ValueError.
realizar_prestamo(id_usuario, id_material, dias_prestamo)
Recibe el ID de un usuario, el ID de un material y el número de días previstos para el préstamo.
Debe realizar un préstamo si se cumplen todas estas condiciones:
- El usuario existe.
- El material existe.
- El material está disponible.
- Los días de préstamo son mayores que 0.
- El usuario no tiene una penalización pendiente superior a 20 €.
- Un alumno no puede tener más de 2 préstamos activos.
- Un profesor no puede tener más de 4 préstamos activos.
Si se realiza el préstamo:
- El material pasa a no estar disponible.
- Se incrementa en 1 el número de préstamos activos del usuario.
- Se añade un préstamo a la lista de préstamos.
El préstamo se guardará como un diccionario con esta estructura:
{
"id_usuario": "U001",
"id_material": "M001",
"dias_prestamo": 7,
"devuelto": False,
"penalizacion_generada": 0
}
Si no se puede realizar el préstamo, se lanzará un ValueError.
devolver_material(id_usuario, id_material, dias_retraso)
Debe devolver un material prestado.
Si existe un préstamo activo:
- El material vuelve a estar disponible.
- Se reduce en 1 el número de préstamos activos del usuario.
- Se calcula la penalización según el tipo de material y los días de retraso.
- Se suma la penalización al usuario.
- El préstamo se marca como devuelto.
- El campo
penalizacion_generadadel préstamo se actualiza.
Si el préstamo activo no existe, se lanzará un ValueError.
pagar_penalizacion(id_usuario, cantidad)
Debe restar la cantidad indicada a la penalización del usuario.
Reglas:
- El usuario debe existir.
- La cantidad debe ser mayor que 0.
- No se puede pagar más dinero del que el usuario debe.
Si alguna condición no se cumple, se lanzará un ValueError.
materiales_disponibles()
Debe devolver una lista con los materiales que estén disponibles.
usuarios_con_penalizacion()
Debe devolver una lista con los usuarios que tengan penalización mayor que 0.
valor_total_materiales()
Debe devolver la suma del valor de todos los materiales del centro.
material_mas_valioso()
Debe devolver el objeto material con mayor valor.
tipo_con_mas_prestamos()
Debe devolver qué tipo de material se ha prestado más veces.
Devolverá uno de estos textos:
portatil
tablet
empate
sin prestamos
Parte 5: Carga de datos
Modifica el fichero carga_datos.py.
Se deberán implementar las siguientes funciones:
cargar_usuarios(centro, path_fichero)
Recibe:
centro
path_fichero
Debe leer el archivo usuarios.csv, crear objetos Usuario y añadirlos al centro.
Debe devolver el objeto centro modificado.
Las filas incorrectas se ignorarán.
cargar_materiales(centro, path_fichero)
Recibe:
centro
path_fichero
Debe leer el archivo materiales.csv, crear objetos Portatil o Tablet según corresponda y añadirlos al centro.
Debe devolver el objeto centro modificado.
Las filas incorrectas se ignorarán.
cargar_prestamos(centro, path_fichero)
Recibe:
centro
path_fichero
Debe leer el archivo prestamos.json y realizar los préstamos iniciales usando el método realizar_prestamo().
Debe devolver el objeto centro modificado.
Los préstamos incorrectos se ignorarán.
Parte 6: Guardado de datos
En el fichero carga_datos.py, implementa también la función:
guardar_prestamos(centro, path_fichero)
Recibe:
centro
path_fichero
Debe guardar en un archivo JSON todos los préstamos actuales del centro.
La estructura del archivo generado será:
[
{
"id_usuario": "U001",
"id_material": "M001",
"dias_prestamo": 7,
"devuelto": false,
"penalizacion_generada": 0
}
]
Parte 7: Métodos mágicos
Implementa el método mágico necesario para que, al imprimir un material, se muestre con el siguiente formato:
<nombre> | <estado> | <valor> € | Disponible: <disponible>
Ejemplo:
Lenovo ThinkPad | bueno | 650.0 € | Disponible: True
Datasets
Dataset 1: usuarios.csv
id_usuario,nombre,tipo,penalizacion
U001,Marta,alumno,0
U002,Carlos,profesor,5
U003,Lucía,alumno,22
U004,Raúl,alumno,0
U005,Elena,profesor,0
U006,Sergio,alumno,-3
U007,,profesor,0
U008,Nuria,administrativo,0
U009,Pablo,alumno,notiene
U010,Ana,profesor,12
Usuarios válidos esperados
U001
U002
U003
U004
U005
U010
Usuarios que deben ignorarse
U006 → penalización negativa
U007 → nombre vacío
U008 → tipo no válido
U009 → penalización no numérica
Dataset 2: materiales.csv
id_material,tipo,nombre,estado,valor,extra
M001,portatil,Lenovo ThinkPad,bueno,650,Windows
M002,tablet,iPad Aula,nuevo,420,TRUE
M003,portatil,Dell Latitude,revisar,580,Linux
M004,tablet,Samsung Tab,bueno,300,FALSE
M005,portatil,MacBook Air,nuevo,950,MacOS
M006,proyector,Epson X12,bueno,400,HDMI
M007,tablet,Tablet Antigua,malo,120,FALSE
M008,portatil,Equipo Sin Valor,bueno,-200,Windows
M009,tablet,,bueno,250,TRUE
M010,portatil,Portátil Raro,nuevo,700,Android
Interpretación del campo extra
- Si el tipo es
portatil, el campoextrarepresenta el sistema operativo. - Si el tipo es
tablet, el campoextrarepresenta si tiene lápiz, usandoTRUEoFALSE.
Materiales válidos esperados
M001
M002
M003
M004
M005
Materiales que deben ignorarse
M006 → tipo no válido
M007 → estado no válido
M008 → valor negativo
M009 → nombre vacío
M010 → sistema operativo no válido
Dataset 3: prestamos.json
[
{
"id_usuario": "U001",
"id_material": "M001",
"dias_prestamo": 7
},
{
"id_usuario": "U002",
"id_material": "M002",
"dias_prestamo": 10
},
{
"id_usuario": "U003",
"id_material": "M003",
"dias_prestamo": 5
},
{
"id_usuario": "U004",
"id_material": "M001",
"dias_prestamo": 3
},
{
"id_usuario": "U005",
"id_material": "M004",
"dias_prestamo": 15
},
{
"id_usuario": "U010",
"id_material": "M005",
"dias_prestamo": 20
},
{
"id_usuario": "U999",
"id_material": "M003",
"dias_prestamo": 4
},
{
"id_usuario": "U001",
"id_material": "M999",
"dias_prestamo": 4
},
{
"id_usuario": "U004",
"id_material": "M003",
"dias_prestamo": 0
}
]
Préstamos válidos esperados
U001 → M001
U002 → M002
U005 → M004
U010 → M005
Préstamos que deben ignorarse
U003 → M003
Porque el usuario tiene una penalización de 22 €, superior al máximo permitido de 20 €.
U004 → M001
Porque el material M001 ya no está disponible.
U999 → M003
Porque el usuario no existe.
U001 → M999
Porque el material no existe.
U004 → M003
Porque los días de préstamo son 0.
Comprobaciones esperadas
Estas comprobaciones pueden servir para el main.py o para la corrección.
Estado tras cargar los préstamos iniciales
Materiales no disponibles:
M001
M002
M004
M005
Materiales disponibles:
M003
Préstamos activos esperados por usuario
U001 → 1 préstamo activo
U002 → 1 préstamo activo
U003 → 0 préstamos activos
U004 → 0 préstamos activos
U005 → 1 préstamo activo
U010 → 1 préstamo activo
Préstamos guardados esperados tras la carga inicial
[
{
"id_usuario": "U001",
"id_material": "M001",
"dias_prestamo": 7,
"devuelto": false,
"penalizacion_generada": 0
},
{
"id_usuario": "U002",
"id_material": "M002",
"dias_prestamo": 10,
"devuelto": false,
"penalizacion_generada": 0
},
{
"id_usuario": "U005",
"id_material": "M004",
"dias_prestamo": 15,
"devuelto": false,
"penalizacion_generada": 0
},
{
"id_usuario": "U010",
"id_material": "M005",
"dias_prestamo": 20,
"devuelto": false,
"penalizacion_generada": 0
}
]
Ejemplos de penalización al devolver materiales
M001 → Portátil bueno, 2 días de retraso → 2 * 5 = 10 €
M002 → Tablet nueva con lápiz, 3 días de retraso → 3 * 3 + 5 = 14 €
M004 → Tablet sin lápiz, 4 días de retraso → 4 * 3 = 12 €
M005 → Portátil nuevo, 1 día de retraso → 1 * 5 + 10 = 15 €
Comprobaciones de métodos del centro
valor_total_materiales() → 650 + 420 + 580 + 300 + 950 = 2900
material_mas_valioso() → MacBook Air
tipo_con_mas_prestamos() → empate
El resultado de tipo_con_mas_prestamos() será "empate" tras la carga inicial porque hay dos préstamos de portátiles (M001, M005) y dos préstamos de tablets (M002, M004).
Resultado esperado de préstamos cargados
Tras cargar los datos iniciales, deberían quedar estos préstamos:
[
{
"id_usuario": "U001",
"id_material": "M001",
"dias_prestamo": 7,
"devuelto": false,
"penalizacion_generada": 0
},
{
"id_usuario": "U002",
"id_material": "M002",
"dias_prestamo": 10,
"devuelto": false,
"penalizacion_generada": 0
},
{
"id_usuario": "U005",
"id_material": "M004",
"dias_prestamo": 15,
"devuelto": false,
"penalizacion_generada": 0
},
{
"id_usuario": "U010",
"id_material": "M005",
"dias_prestamo": 20,
"devuelto": false,
"penalizacion_generada": 0
}
]
Rúbrica de evaluación
Calificación total: 10 puntos
| Parte | Criterio | Puntuación |
|---|---|---|
| Parte 1 | Clase Usuario | 1 |
| Parte 2 | Clase base Material | 1,5 |
| Parte 2 | Herencia y polimorfismo | 1,5 |
| Parte 3 y 4 | Clase CentroPrestamo y lógica | 2 |
| Parte 5 | Carga de datos CSV y JSON | 1,5 |
| Parte 6 | Guardado de préstamos en JSON | 0,75 |
| Parte 7 | Métodos mágicos y representación textual | 0,5 |
| General | Calidad, organización y encapsulación | 1,25 |
Desglose detallado
1. Clase Usuario — 1 punto
| Aspecto | Puntos |
|---|---|
| Define correctamente los atributos indicados | 0,20 |
| Valida correctamente identificador, nombre y tipo | 0,20 |
| Valida correctamente penalización y préstamos activos | 0,20 |
| Implementa correctamente los métodos de gestión del usuario | 0,25 |
Implementa correctamente el método mágico __str__ | 0,15 |
2. Clase base Material — 1,5 puntos
| Aspecto | Puntos |
|---|---|
Define correctamente el atributo compartido centro | 0,15 |
| Define correctamente los atributos de instancia | 0,25 |
| Valida identificador, nombre, estado, valor y disponibilidad | 0,35 |
Implementa correctamente prestar() | 0,20 |
Implementa correctamente devolver() | 0,20 |
Implementa correctamente necesita_revision() | 0,10 |
Implementa calcular_penalizacion() como método polimórfico | 0,25 |
3. Herencia y polimorfismo — 1,5 puntos
| Aspecto | Puntos |
|---|---|
Portatil hereda correctamente de Material | 0,25 |
Tablet hereda correctamente de Material | 0,25 |
| Se inicializan correctamente los atributos de la clase padre | 0,25 |
| Se implementan los atributos específicos | 0,20 |
Se validan correctamente sistema_operativo y tiene_lapiz | 0,20 |
Se sobrescribe correctamente calcular_penalizacion() en ambas clases | 0,35 |
4. Clase CentroPrestamo y lógica — 2 puntos
| Aspecto | Puntos |
|---|---|
| Gestiona correctamente usuarios, materiales y préstamos | 0,25 |
Implementa agregar_usuario() y agregar_material() | 0,25 |
| Implementa correctamente los métodos de búsqueda | 0,20 |
| Realiza préstamos comprobando existencia, disponibilidad, días y penalización | 0,40 |
| Controla el límite de préstamos activos según el tipo de usuario | 0,25 |
| Devuelve materiales calculando penalización correctamente | 0,35 |
| Implementa correctamente el pago de penalizaciones | 0,15 |
| Devuelve correctamente materiales disponibles y usuarios con penalización | 0,10 |
| Calcula correctamente valor total, material más valioso y tipo más prestado | 0,05 |
5. Carga de datos CSV y JSON — 1,5 puntos
| Aspecto | Puntos |
|---|---|
Carga correctamente usuarios.csv | 0,30 |
| Ignora usuarios inválidos sin detener el programa | 0,20 |
Carga correctamente materiales.csv | 0,30 |
Crea correctamente Portatil o Tablet según el tipo | 0,20 |
| Ignora materiales inválidos sin detener el programa | 0,20 |
Carga correctamente prestamos.json | 0,20 |
| Ignora préstamos inválidos sin detener el programa | 0,10 |
6. Guardado de préstamos en JSON — 0,75 puntos
| Aspecto | Puntos |
|---|---|
| Crea correctamente el archivo JSON de salida | 0,25 |
| Guarda todos los préstamos actuales | 0,25 |
| Respeta la estructura indicada | 0,25 |
7. Métodos mágicos — 0,5 puntos
| Aspecto | Puntos |
|---|---|
__str__ de Usuario correcto | 0,20 |
__str__ de Material correcto | 0,30 |
8. Calidad, organización y encapsulación — 1,25 puntos
| Aspecto | Puntos |
|---|---|
| No accede directamente a atributos desde fuera de las clases | 0,30 |
| Código organizado por ficheros y responsabilidades | 0,25 |
| Nombres claros y coherentes | 0,20 |
| Uso adecuado de excepciones | 0,20 |
| Código legible y sin duplicación excesiva | 0,20 |
| Respeta nombres de clases, métodos y retornos solicitados | 0,10 |
Penalizaciones generales
| Situación | Penalización |
|---|---|
| El programa no ejecuta por errores de sintaxis | Hasta -3 |
| No se usa POO | Hasta -4 |
| No se implementa herencia | Hasta -1,5 |
| No se implementa polimorfismo | Hasta -1 |
| No se leen archivos externos | Hasta -2 |
| No se gestiona JSON | Hasta -1 |
| No se respeta la estructura del proyecto | Hasta -1 |
| Se accede directamente a los atributos desde fuera de las clases de forma reiterada | Hasta -1 |
| El programa se detiene ante datos erróneos del dataset | Hasta -1 |
| No se respetan los nombres pedidos de clases, métodos o retornos | Hasta -1,5 |
Observaciones
- No se deberá modificar el archivo
main.py. - Se deberán respetar exactamente los nombres de clases, métodos y valores de retorno indicados.
- Las filas incorrectas de los archivos de datos deberán ignorarse sin detener el programa.
- Se permite el uso de excepciones para controlar errores.
- Se valorará la claridad, la organización y la correcta separación de responsabilidades.
- El acceso directo a atributos desde fuera de las clases no está permitido.
- La solución debe poder ejecutarse cargando los datos desde los archivos proporcionados.