# ALS Parcial 2a 2018/19 MIT License Baltasar <jbgarcia@uvigo.es>


import json


"""
    1. La clase Registro es capaz de albergar varios datos que pueden ser accedidos por nombre, por nombre construido (r#i), o por índice. Para construir un objeto, se le puede pasar o bien un diccionario, o bien una lista (en este caso, las claves serán las citadas r#i). Los únicos métodos en la clase son: __init__(), __getattr__(), __len__() (que devuelve el número de elementos), y __str__() (que devuelve el contenido del objeto en formato de diccionario, utilizando comillas dobles y un espacio tras ‘,’ y ‘:’).

    	r1 = Registro({"huevos": 15, "patatas": 5})
	print(r1.r0, r1.get(0), r1.huevos)		# 15 15 15
	print(r1.r1, r1.get(1), r1.patatas)		#  5  5  5
	print(r1)						# {“huevos”: 15, “patatas”: 5}

	r2 = Registro([“a”, “b”])
	print(r2.r0, r2.get(0))				# a a
	print(r2.r1, r2.get(1))				# b b
	print(r2)						# {“r0”: “a”, “r1”: “b”}


    2. 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.

	r2 = Registro([“a”, “b”, “c”])

	with open(“datos.json”, “wt”) as f:
		r2.to_json(f)					# {“r0”: “a”, “r1”: “b”, “r2”: “c”}

	with open(“datos.json”, “rt”) as f:
		print(str(Registro.from_json(f)))	# {“r0”: “a”, “r1”: “b”, “r2”: “c”}

       
    3. Escriba la función filtra_registros(lr, f), que recibe como parámetros una lista de registros y una función que devolverá verdadero o falso según el registro que se le pase. Deben utilizarse exclusivamente lambdas, y para esta función en particular, filtrar la lista.

	resultado = filtra_registros([r1, r2], lambda r: isinstance(r.r0, int))
	print(resultado)					# [r1]

"""


class Registro:
    def __init__(self, datos):
        if isinstance(datos, dict):
            self.__dict__ = datos
        elif (isinstance(datos, list)
            or isinstance(datos, tuple)
            or isinstance(datos, set)):
            for i, x in enumerate(list(datos)):
                self.__dict__['r' + str(i)] = x
        else:
            self.__dict__["r0"] = datos
            
    def __getattr__(self, s):
        def getPos(i):
            return self.__dict__[list(self.__dict__.keys())[i]]
        
        toret = None
        
        if s.startswith('r'):
            try:
                toret = getPos(int(s[1:]))
            except (KeyError, ValueError):
                toret = None
        elif s.startswith('get'):
            toret = lambda i: getPos(i)
                
        return toret
    
    def to_json(self, f):
        return json.dump(self.__dict__, f)
        
    @staticmethod
    def from_json(f):
        toret = Registro({})
        toret.__dict__ = json.load(f)
        return toret
        
    def __len__(self):
        return len(self.__dict__)
            
    def __str__(self):
        toret = "{"
        separador = ""
        for key in self.__dict__.keys():
            value = self.__dict__[key]
            
            if isinstance(value, str):
                value = "\"" + value + "\""
            else:
                value = str(value)
            
            toret += separador
            toret += "\"" + key + "\": "
            toret += value
            separador = ", "
        return toret + '}'


filtra_registros = lambda lr, f: ([] if lr == []
                                  else [lr[0]] + filtra_registros(lr[1:], f)
                                                    if f(lr[0])
                                  else filtra_registros(lr[1:], f))


import io
import unittest


class TestRegistro(unittest.TestCase):
    def setUp(self):
        self.r1 = Registro({"huevos": 15, "patatas": 5, "salchichas": 12})
        self.r2 = Registro([1, 2, 3, 4, 5])
        self.r3 = Registro('a')
        self.todos_registros = [self.r1, self.r2, self.r3]
        self.todos_resultados = [
            "{\"huevos\":15,\"patatas\":5,\"salchichas\":12}",
            "{\"r0\":1,\"r1\":2,\"r2\":3,\"r3\":4,\"r4\":5}",
            "{\"r0\":\"a\"}"
        ]
        
    def testRegistroPorIndiceGet(self):
        r1 = [15, 5, 12]
        r2 = [1, 2, 3, 4, 5]
        r3 = ['a']
        todos_resultados = [r1, r2, r3]
        
        self.assertEqual(len(todos_resultados), len(self.todos_registros))
        for i, l in enumerate(todos_resultados):
            r = self.todos_registros[i]
            self.assertEqual(len(l), len(r))
            for n, x in enumerate(l):
                self.assertEqual(x, r.get(n))
                self.assertEqual(x, getattr(r, "r" + str(n)))
                
    def testRegistroStr(self):
        self.assertEqual(len(self.todos_resultados), len(self.todos_registros))
        for i, resultado in enumerate(self.todos_resultados):
            resultado_registro = TestRegistro.prepara_resultado(str(self.todos_registros[i]))
            resultado = TestRegistro.prepara_resultado(resultado)
            self.assertEqual(resultado, resultado_registro)
                    
    def testRegistroR1PorNombre(self):
        self.assertEqual(15, self.r1.huevos)
        self.assertEqual(5, self.r1.patatas)
        self.assertEqual(12, self.r1.salchichas)
        
    def testRegistroToJSON(self):
        for r in self.todos_registros:
            with io.StringIO() as f:
                r.to_json(f)
                self.assertEqual(str(r), f.getvalue().strip())
            
    def tesRegistroFromJSON(self):
        for i, r in enumerate(self.todos_registros):
            json_r = TestRegistro.prepara_resultado(str(r))
            
            with io.StringIO(self.todos_resultados[i]) as f:
                loaded_r = Registro.from_json(f)

            resultado = TestRegistro.prepara_resultado(str(loaded_r))
            self.assertEqual(resultado, json_r)
            
    def testFiltraRegistros(self):
        resultado = filtra_registros(self.todos_registros, lambda r: isinstance(r.r0, int))
        self.assertEqual([self.r1, self.r2], resultado)
        
    @staticmethod
    def prepara_resultado(s):
        return s.strip().replace('\'', '"').replace(' ', "")


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