Saltar al contenido principal

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​

  1. El identificador no puede estar vacío.
  2. El nombre no puede estar vacío.
  3. El tipo solo podrá tener uno de estos valores:
alumno
profesor
  1. La penalización no puede ser negativa.
  2. Los préstamos activos no pueden ser negativos.
  3. Si se intenta asignar un tipo no válido, se lanzará un ValueError.
  4. Si se intenta asignar una penalización negativa, se lanzará un ValueError.
  5. 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​

  1. El identificador no puede estar vacío.
  2. El nombre no puede estar vacío.
  3. El estado solo podrá tener uno de estos valores:
nuevo
bueno
revisar
  1. El valor debe ser mayor que 0.
  2. disponible debe ser un valor booleano.
  3. 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 objeto Usuario.
  • materiales: diccionario donde la clave será el ID del material y el valor será un objeto Portatil o Tablet.
  • 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:

  1. El usuario existe.
  2. El material existe.
  3. El material está disponible.
  4. Los días de préstamo son mayores que 0.
  5. El usuario no tiene una penalización pendiente superior a 20 €.
  6. Un alumno no puede tener más de 2 préstamos activos.
  7. 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_generada del 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:

  1. El usuario debe existir.
  2. La cantidad debe ser mayor que 0.
  3. 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 campo extra representa el sistema operativo.
  • Si el tipo es tablet, el campo extra representa si tiene lápiz, usando TRUE o FALSE.

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​

ParteCriterioPuntuación
Parte 1Clase Usuario1
Parte 2Clase base Material1,5
Parte 2Herencia y polimorfismo1,5
Parte 3 y 4Clase CentroPrestamo y lógica2
Parte 5Carga de datos CSV y JSON1,5
Parte 6Guardado de préstamos en JSON0,75
Parte 7Métodos mágicos y representación textual0,5
GeneralCalidad, organización y encapsulación1,25

Desglose detallado​

1. Clase Usuario — 1 punto​

AspectoPuntos
Define correctamente los atributos indicados0,20
Valida correctamente identificador, nombre y tipo0,20
Valida correctamente penalización y préstamos activos0,20
Implementa correctamente los métodos de gestión del usuario0,25
Implementa correctamente el método mágico __str__0,15

2. Clase base Material — 1,5 puntos​

AspectoPuntos
Define correctamente el atributo compartido centro0,15
Define correctamente los atributos de instancia0,25
Valida identificador, nombre, estado, valor y disponibilidad0,35
Implementa correctamente prestar()0,20
Implementa correctamente devolver()0,20
Implementa correctamente necesita_revision()0,10
Implementa calcular_penalizacion() como método polimórfico0,25

3. Herencia y polimorfismo — 1,5 puntos​

AspectoPuntos
Portatil hereda correctamente de Material0,25
Tablet hereda correctamente de Material0,25
Se inicializan correctamente los atributos de la clase padre0,25
Se implementan los atributos específicos0,20
Se validan correctamente sistema_operativo y tiene_lapiz0,20
Se sobrescribe correctamente calcular_penalizacion() en ambas clases0,35

4. Clase CentroPrestamo y lógica — 2 puntos​

AspectoPuntos
Gestiona correctamente usuarios, materiales y préstamos0,25
Implementa agregar_usuario() y agregar_material()0,25
Implementa correctamente los métodos de búsqueda0,20
Realiza préstamos comprobando existencia, disponibilidad, días y penalización0,40
Controla el límite de préstamos activos según el tipo de usuario0,25
Devuelve materiales calculando penalización correctamente0,35
Implementa correctamente el pago de penalizaciones0,15
Devuelve correctamente materiales disponibles y usuarios con penalización0,10
Calcula correctamente valor total, material más valioso y tipo más prestado0,05

5. Carga de datos CSV y JSON — 1,5 puntos​

AspectoPuntos
Carga correctamente usuarios.csv0,30
Ignora usuarios inválidos sin detener el programa0,20
Carga correctamente materiales.csv0,30
Crea correctamente Portatil o Tablet según el tipo0,20
Ignora materiales inválidos sin detener el programa0,20
Carga correctamente prestamos.json0,20
Ignora préstamos inválidos sin detener el programa0,10

6. Guardado de préstamos en JSON — 0,75 puntos​

AspectoPuntos
Crea correctamente el archivo JSON de salida0,25
Guarda todos los préstamos actuales0,25
Respeta la estructura indicada0,25

7. Métodos mágicos — 0,5 puntos​

AspectoPuntos
__str__ de Usuario correcto0,20
__str__ de Material correcto0,30

8. Calidad, organización y encapsulación — 1,25 puntos​

AspectoPuntos
No accede directamente a atributos desde fuera de las clases0,30
Código organizado por ficheros y responsabilidades0,25
Nombres claros y coherentes0,20
Uso adecuado de excepciones0,20
Código legible y sin duplicación excesiva0,20
Respeta nombres de clases, métodos y retornos solicitados0,10

Penalizaciones generales​

SituaciónPenalización
El programa no ejecuta por errores de sintaxisHasta -3
No se usa POOHasta -4
No se implementa herenciaHasta -1,5
No se implementa polimorfismoHasta -1
No se leen archivos externosHasta -2
No se gestiona JSONHasta -1
No se respeta la estructura del proyectoHasta -1
Se accede directamente a los atributos desde fuera de las clases de forma reiteradaHasta -1
El programa se detiene ante datos erróneos del datasetHasta -1
No se respetan los nombres pedidos de clases, métodos o retornosHasta -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.