# Examen ALS final(parcial 2) 2018/19 MIT License Baltasar <jbgarcia@uvigo.es>


"""
    1. (4pts.) La clase AlumnoALS alberga las notas de un alumno de ALS. Se contruye con un nombre y tres notas: la del parcial 1, la del parcial 2, y la del proyecto. 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”: notas devuelve una nueva lista con las tres notas, parcial1 devolverá la nota del primer parcial, parcial2 la del segundo, y proyecto la del proyecto. El “método” calcula_nota_final_parciales() calcula la media de los dos parciales y la multiplica por 0.6. El “método” calcula_nota_final_proyecto() multiplica la nota del proyecto por 0.4. El “método” calcula_nota_final() devolverá la nota media ponderada: 0.6 * ((parcial1 + parcial2) / 2) + 0.4 * proyecto. El método __str__() devolverá la información en el formato <nombre>: 0.6 * ((<parcial1> + <parcial2>) / 2) + 0.4 * <proyecto> = <parciales> + <proyecto> = <nota_final>, como por ejemplo: Baltasar: 0.6 * ((10 + 10) / 2) + 0.4 * 10 = 6 + 4 = 10.

    	a1 = AlumnoALS(“Baltasar”, [10, 10, 10])
	print(a1.notas[0], a1.parcial1, a1.notas[1], a1.parcial2)		# 10 10 10 10
	print(a1.calcula_nota_final_proyecto())					# 4
	print(a1.calcula_nota_final())						# 10
	print(a1)	# Baltasar: 0.6 * ((10.00 + 10.00) / 2) + 0.4 * 10.00 = 6.00 + 4.00 = 10


    2. (2pts.) Cree los métodos to_json(f) y from_json(f). El primero escribe la codificación JSON del objeto como un archivo, la segunda toma un archivo con contenido en formato JSON y crea un objeto con dicho contenido.

    3. (4pts.) Escriba la función lambda nota_proyecto(a). Esta función recibe un objeto AlumnoALS y devuelve un texto con el formato: “<nombre>: <nota_proyecto>”, por ejemplo: “Baltasar: 10.00”. Escriba la función lambda lista_alumnos(la, f), que recibe como parámetros una lista de alumnos y una función que devolverá un texto por cada alumno que se le pase. Debe devolver un texto compuesto por todos los resultados de llamar a f en los objetos de la lista la. Componga ambas funciones para devolver un listado de las notas del proyecto de los alumnos.

	listado = lista_alumnos([a1, a2], nota_proyecto))
	print(resultado)

	Baltasar: 10.00
	Cantinflas:  7.00

"""


import json


class AlumnoALS:
    def __init__(self, nombre, notas):
        self._nombre = nombre
        self._notas = list(notas)
        
    def __getattr__(self, s):
        if s == "nombre":
            return self._nombre
        elif s == "notas":
            return list(self._notas)
        elif s == "parcial1":
            return self._notas[0]
        elif s == "parcial2":
            return self._notas[1]
        elif s == "proyecto":
            return self._notas[2]
        elif s == "calcula_nota_final_parciales":
            return lambda: ((self._notas[0] + self._notas[1]) / 2) * .6
        elif s == "calcula_nota_final_proyecto":
            return lambda: self._notas[2] * .4
        elif s == "calcula_nota_final":
            return lambda: self.calcula_nota_final_parciales() + self.calcula_nota_final_proyecto()
        else:
            raise AttributeError(s)
        
    def __str__(self):
        return str.format("{}: 0.6 * (({:5.2f} + {:5.2f}) / 2) + 0.4 * {:5.2f} = {:5.2f} + {:5.2f} = {:5.2f}",
                          self.nombre,
                          self.parcial1,
                          self.parcial2,
                          self.proyecto,
                          self.calcula_nota_final_parciales(),
                          self.calcula_nota_final_proyecto(),
                          self.calcula_nota_final())
    
    def to_json(self, f):
        return json.dump(self.__dict__, f)
        
    @staticmethod
    def from_json(f):
        toret = AlumnoALS("", [])
        toret.__dict__ = json.load(f)
        return toret
        

nota_proyecto = lambda a: str.format("{}: {:5.2f}\n", a.nombre, a.parcial1)
lista_alumnos = lambda la, f: ("" if not la or la == []
                                  else f(la[0]) + lista_alumnos(la[1:], f))



import io
import unittest


class TestAlumnoALS(unittest.TestCase):
    def setUp(self):
        self.notas_alumno1 = [10, 10, 10]
        self.datos_alumno1 = {"_nombre": "Baltasar", "_notas": self.notas_alumno1}
        self.alumno1 = AlumnoALS("Baltasar", self.notas_alumno1)
        
    def testAlumno1(self):
        self.assertEqual("Baltasar", self.alumno1.nombre)
        self.assertEqual(self.notas_alumno1, self.alumno1.notas)
        self.assertEqual("Baltasar: 0.6 * ((10.00 + 10.00) / 2) + 0.4 * 10.00 =  6.00 +  4.00 = 10.00", str(self.alumno1))
        
    def testParciales(self):
        self.assertEqual(self.notas_alumno1[0], self.alumno1.notas[0])
        self.assertEqual(self.notas_alumno1[1], self.alumno1.notas[1])
        self.assertEqual(self.notas_alumno1[2], self.alumno1.notas[2])
        
    def testMetodosCalcula(self):
        self.assertEqual(0.6 * ((self.notas_alumno1[0] + self.notas_alumno1[1]) / 2), self.alumno1.calcula_nota_final_parciales())
        self.assertEqual(0.4 * self.notas_alumno1[1], self.alumno1.calcula_nota_final_proyecto())
        
    def testAlumnoALSToJSON(self):
        with io.StringIO() as f:
            self.alumno1.to_json(f)
            self.assertEqual(TestAlumnoALS.prepara_resultado((self.datos_alumno1)), TestAlumnoALS.prepara_resultado(f.getvalue().strip()))
            
    def testAlumnoALSFromJSON(self):
        json_a = TestAlumnoALS.prepara_resultado(self.datos_alumno1)
        
        with io.StringIO(json_a) as f:
            loaded_a = AlumnoALS.from_json(f)

        self.assertEqual(self.datos_alumno1, loaded_a.__dict__)
            
    def testNotaProyecto(self):
        self.assertEqual(
            str.format("{}: {:5.2f}\n", self.alumno1.nombre, self.alumno1.parcial1),
            nota_proyecto(self.alumno1))
        
    def testListaAlumnos(self):
        listado = lista_alumnos([self.alumno1], nota_proyecto)
        self.assertEqual(
            str.format("{}: {:5.2f}\n", self.alumno1.nombre, self.alumno1.parcial1),
            listado)
        
    @staticmethod
    def prepara_resultado(s):
        return str(s).strip().replace('\'', '"').replace(' ', "")


if __name__ == "__main__":
    unittest.main()
