# coding: utf-8
# Solución ALS (c) 2019 Baltasar MIT License <jbgarcia@uvigo.es>
"""
1. (4pts.) La clase Ciudadano alberga los años más significativos de
un ciudadano de un país dado. Se construye con unos apellidos, un nombre
y una lista de tres números: el año de nacimiento,
el año del primer empleo, el año de jubilación y el de defunción.
Los métodos de esta clase serán
exactamente __init__(), __getattr__() y __str__().
Aún así, será capaz de responder a la petición de varias “propiedades”:
annos devuelve una nueva lista con todos los años, nacimiento
devolverá el año de nacimiento, primer_empleo el del primer empleo,
jubilacion la de jubilación, y defuncion la de defunción.
El “método” calcula_annos_vida() calcula el número de años
entre el nacimiento y la defunción.
El “método” calcula_annos_activos() calcula la diferencia
entre los años del primer empleo y la jubilación.
El método __str__() devolverá la información en el formato
<apellidos>, <nombre>: <nacimiento> - <defuncion>(<primer_empleo> - <jubilacion>),
como por ejemplo: Díaz de Vivar, Rodrigo: 1043-1099(1065-1099).
c1 = Ciudadano(“Díaz de Vivar”, “Rodrigo”, [1043, 1065, 1099, 1099])
print(c1.annos[0], c1.primer_empleo, c1.annos[2], c1.defuncion) # 1043 1065 1099 1099
print(c1.calcula_annos_vida()) # 56
print(c1.calcula_annos_activos()) # 34
print(c1) # Díaz de Vivar, Rodrigo: 1043-1099(1065-1099)
2. (1pts.) Cree los métodos to_json(f) y from_json(f).
El primero escribe la codificación JSON del objeto en un archivo f,
la segunda toma un archivo f con contenido en formato JSON
y crea un objeto con dicho contenido.
3. (4pts.) Cree la clase Censo que almacena ciudadanos en una lista.
Esta clase admite un parámetro en el constructor:
un objeto de la clase Ciudadano o una lista con los objetos Ciudadano.
Tendrá también el acceso de solo lectura [i],
que devolverán el ciudadano i-ésimo, y la función len()
el número total de ciudadanos.
El método __str_₍) devolverá toda la
información con cada ciudadano en su propia línea.
El método add(c) permitirá añadir un nuevo ciudadano,
mientras que los métodos calcula_media_vida()
y calcula_media_activos() calcularán la media de los años de vida
y los años activos, respectivamente.
4. (1pts.) Incluya en la clase Censo los métodos
calcula_media_vida() y calcula_media_activos()
que calcularán la media de los años de vida y los años activos,
respectivamente.
Deben hacerlo empleando la función reduce(f,l,i) del paquete estándar functtols.
"""
import io
import unittest
import json
from functools import reduce
class Ciudadano:
def __init__(self, apellidos, nombre, annos):
self._apellidos = apellidos
self._nombre = nombre
self._annos = annos
def __getattr__(self, s):
toret = ""
if s == "apellidos":
toret = self._apellidos
elif s == "nombre":
toret = self._nombre
elif s == "annos":
toret = list(self._annos)
elif s == "nacimiento":
toret = self._annos[0]
elif s == "primer_empleo":
toret = self._annos[1]
elif s == "jubilacion":
toret = self._annos[2]
elif s == "defuncion":
toret = self._annos[3]
elif s == "calcula_annos_vida":
toret = lambda: self.annos[3] - self.annos[0]
elif s == "calcula_annos_activos":
toret = lambda: self.annos[2] - self.annos[1]
return toret
def to_json(self, f):
json.dump(self.__dict__, f)
def from_json(self, f):
toret = Ciudadano("", "", [])
toret.__dict__ = json.read(f)
return toret
def __str__(self):
return str.format("{}, {}: {}-{}({}-{})",
self.apellidos,
self.nombre,
self.nacimiento,
self.defuncion,
self.primer_empleo,
self.jubilacion)
class Censo:
def __init__(self, l):
if l:
if isinstance(l, list):
self._ciudadanos = l
elif isinstance(l, Ciudadano):
self._ciudadanos = [l]
def add(self, c):
self._ciudadanos.append(c)
def calcula_media_activos(self):
toret = reduce(
lambda acc, c2: acc + c2.calcula_annos_activos(),
self._ciudadanos,
0)
return toret / len(self._ciudadanos)
def calcula_media_vida(self):
toret = reduce(
lambda acc, c2: acc + c2.calcula_annos_vida(),
self._ciudadanos,
0)
return toret / len(self._ciudadanos)
def __getitem__(self, it):
return self._ciudadanos[it]
def __len__(self):
return len(self._ciudadanos)
def __str__(self):
toret = ""
for c in self._ciudadanos:
toret += str(c) + '\n'
return toret
class TestCiudadano(unittest.TestCase):
def setUp(self):
self.c1 = Ciudadano("Diaz de Vivar", "Rodrigo", [1043, 1065, 1099, 1099])
self.datos_c1 = "{'_apellidos':'Diaz de Vivar','_nombre':'Rodrigo','_annos':[1043,1065,1099,1099]}"
def testNombre(self):
self.assertEqual("Rodrigo", self.c1.nombre)
def testApellidos(self):
self.assertEqual("Diaz de Vivar", self.c1.apellidos)
def testNacimiento(self):
self.assertEqual(1043, self.c1.nacimiento)
def testDefuncion(self):
self.assertEqual(1099, self.c1.defuncion)
def testJubilacion(self):
self.assertEqual(1099, self.c1.jubilacion)
def testPrimerEmpleo(self):
self.assertEqual(1065, self.c1.primer_empleo)
def testCiudadano(self):
self.assertEqual("Diaz de Vivar, Rodrigo: 1043-1099(1065-1099)", str(self.c1))
def testAnnosVida(self):
self.assertEqual(56, self.c1.calcula_annos_vida())
def testAnnosActivos(self):
self.assertEqual(34, self.c1.calcula_annos_activos())
def testToJson(self):
with io.StringIO() as f:
self.c1.to_json(f)
self.assertEqual(TestCiudadano.prepara_resultado(self.datos_c1), TestCiudadano.prepara_resultado(f.getvalue().strip()))
@staticmethod
def prepara_resultado(s):
return str(s).strip().replace('\'', '"').replace(' ', "")
class TestCenso(unittest.TestCase):
def setUp(self):
self.c1 = Ciudadano("Diaz de Vivar", "Rodrigo", [1043, 1065, 1099, 1099])
self.c2 = Ciudadano("Kane", "Roger", [1840, 1860, 1880, 1890])
self.censo = Censo([self.c1])
def testLen(self):
self.assertEqual(len(self.censo._ciudadanos), len(self.censo))
def testStr(self):
expected = "\n".join([str(c) for c in self.censo._ciudadanos])
self.assertEqual(expected, str(self.censo).strip())
def testAdd(self):
self.censo.add(self.c2)
self.assertEqual(2, len(self.censo))
self.assertEqual(self.c2, self.censo[1])
def testAccess(self):
self.assertEqual(self.c1, self.censo[0])
def testCalculaMediaVida(self):
media_vida = 0
for c in self.censo._ciudadanos:
media_vida += c.calcula_annos_vida()
media_vida /= len(self.censo._ciudadanos)
self.assertEqual(media_vida, self.censo.calcula_media_vida())
def testCalculaMediaActivos(self):
media_activos = 0
for c in self.censo._ciudadanos:
media_activos += c.calcula_annos_activos()
media_activos /= len(self.censo._ciudadanos)
self.assertEqual(media_activos, self.censo.calcula_media_activos())
if __name__ == "__main__":
unittest.main()