P1 · Suscripciones SaaS en Python (S7 + S8)
clases + JSON + diccionarios · ★★★★Una plataforma cobra planes básico ($9.99) y pro ($24.99) por mes; desde 12 meses activos el cliente recibe 15 % de descuento. Carga las suscripciones desde JSON (si el archivo no existe, lista vacía), calcula el ingreso mensual y cuenta clientes por plan. Un plan desconocido lanza ValueError.
Tu programa debe cumplir estos casos de prueba:
| Dato | Esperado |
|---|---|
| Ana, pro, 14 meses | $21.24 |
| Luis, básico, 3 meses | $9.99 |
| Eva, pro, 2 meses | $24.99 |
| Ingreso mensual | $56.22 |
| Clientes por plan | {'pro': 2, 'basico': 1} |
| Sin archivo | [] |
Pista
Clase con validación en __init__; función cargar con try/except FileNotFoundError; acumular por clave para contar planes.
Solución
import json
from pathlib import Path
class Suscripcion:
PRECIOS = {"basico": 9.99, "pro": 24.99}
def __init__(self, cliente, plan, meses_activos=0):
if plan not in self.PRECIOS:
raise ValueError(f"plan desconocido: {plan}")
self.cliente = cliente
self.plan = plan
self.meses_activos = meses_activos
def cobro_mensual(self):
precio = self.PRECIOS[self.plan]
if self.meses_activos >= 12:
precio *= 0.85 # lealtad: 15 % desde el año
return round(precio, 2)
def a_dict(self):
return {"cliente": self.cliente, "plan": self.plan, "meses_activos": self.meses_activos}
def cargar(ruta):
try:
datos = json.loads(Path(ruta).read_text())
except FileNotFoundError:
return []
return [Suscripcion(d["cliente"], d["plan"], d["meses_activos"]) for d in datos]
Path("subs.json").write_text(json.dumps([
{"cliente": "Ana", "plan": "pro", "meses_activos": 14},
{"cliente": "Luis", "plan": "basico", "meses_activos": 3},
{"cliente": "Eva", "plan": "pro", "meses_activos": 2}]))
subs = cargar("subs.json")
ingreso = sum(s.cobro_mensual() for s in subs)
por_plan = {}
for s in subs:
por_plan[s.plan] = por_plan.get(s.plan, 0) + 1
print(f"Ingreso mensual: ${ingreso:.2f}")
print("Clientes por plan:", por_plan)
print("Sin archivo:", cargar("no_existe.json"))
Ingreso mensual: $56.22
Clientes por plan: {'pro': 2, 'basico': 1}
Sin archivo: []