fork download
  1. # ALS Examen final Julio (c) 2023 Baltasar MIT License <baltasarq@uvigo.gal>
  2.  
  3.  
  4. import io
  5. import csv
  6. import unittest
  7. import random as rnd
  8. from collections import defaultdict
  9.  
  10.  
  11. """
  12. 1. (3 pts.) Cree la clase Empresa. Esta clase representa a empresas
  13. que operan en bolsa. Se creará con un identificador
  14. que se convertirá a partir del nombre que se le pase,
  15. convirtiendo a mayúsculas y limitándolo a cuatro caracteres,
  16. y el valor de la empresa.
  17. Por ejemplo, Apple podría representarse con Empresa(“Apple”, 12000).
  18. Estos dos datos se podrán recuperar con las propiedades de solo lectura
  19. id y valor. El método para convertir a texto devolverá
  20. la información como “<id> (<valor>pts.)”.
  21. Por ejemplo, Microsoft con 2000 puntos de valor
  22. sería “MICR (2000pts.)”.
  23. Cree las clases Op (operación), Compra y Venta.
  24. La clase Op permite agrupar compras y ventas.
  25. En cuanto a las clases Compra y Venta, son muy parecidas:
  26. ambas se crean con tres parámetros:
  27. dos objetos Empresa, la empresa “origen” (la que realiza la operación),
  28. la empresa “destino” (sobre la que se realiza la operación),
  29. y un valor de la operación en euros.
  30. Estos datos pueden recuperarse mediante
  31. tres propiedades de solo lectura: empresa_org, empresa_dest y valor.
  32. Cuando se convierte a texto,
  33. el formato es: “<empresa_org_id> -> <empresa_dest_id>: <valor>€”,
  34. de forma que valor presenta siempre dos decimales.
  35. Por ejemplo, si Apple comprara 42€ de Microsoft,
  36. se crearía como Compra(Empresa(“Apple”, 12000),
  37. Empresa(“Microsoft”, 9000), 42), y se representaría como
  38. “bidd APPL -> MICR: 42.00€”.
  39. La única diferencia entre Compra y Venta es que
  40. en el caso de la primera,
  41. al texto se le antepone “bidd”, y a la venta se le antepone “sell”.
  42. """
  43. class Empresa:
  44. def __init__(self, id: str, valor: float):
  45. self.__id = id.strip().upper()[:4]
  46. self.__valor = valor
  47.  
  48. @property
  49. def id(self):
  50. return self.__id
  51.  
  52. @property
  53. def valor(self):
  54. return self.__valor
  55.  
  56. def __str__(self):
  57. return f"{self.id} ({self.valor}pts.)"
  58.  
  59.  
  60. class Op:
  61. def __init__(self, empresa_org: Empresa, empresa_dest: Empresa, valor: float):
  62. self.__empresa_org = empresa_org
  63. self.__empresa_dest = empresa_dest
  64. self.__valor = valor
  65.  
  66. @property
  67. def empresa_org(self):
  68. return self.__empresa_org
  69.  
  70. @property
  71. def empresa_dest(self):
  72. return self.__empresa_dest
  73.  
  74. @property
  75. def valor(self):
  76. return self.__valor
  77.  
  78. def __str__(self):
  79. return f"{self.empresa_org.id} -> {self.empresa_dest.id}: {self.valor: 5.2f}€"
  80.  
  81.  
  82. class Compra(Op):
  83. def __init__(self, empresa_org: Empresa, empresa_dest: Empresa, valor: float):
  84. return super().__init__(empresa_org, empresa_dest, valor)
  85.  
  86. def __str__(self):
  87. return "bidd " + super().__str__()
  88.  
  89.  
  90. class Venta(Op):
  91. def __init__(self, empresa_org: Empresa, empresa_dest: Empresa, valor: float):
  92. return super().__init__(empresa_org, empresa_dest, valor)
  93.  
  94. def __str__(self):
  95. return "sell " + super().__str__()
  96.  
  97.  
  98. """
  99. 2. (3 pts.) Cree la clase Bolsa,
  100. que representa los movimientos en una bolsa de valores.
  101. No necesita ningún parámetro para ser creada.
  102. Internamente, lleva cuatro estructuras de datos sincronizadas:
  103. una colección con todas las operaciones,
  104. una estructura de datos con todas las operaciones por empresa,
  105. una estructura de datos con todas las compras por empresa,
  106. y una última estructura de datos con todas las ventas por empresa.
  107. Estas cuatro colecciones son accesibles mediante las propiedades
  108. de solo lectura:
  109. ops, ops_por_empresa, compras_por_empresa, y ventas_por_empresa,
  110. respectivamente.
  111. En estas propiedades no se realiza ningún tipo de búsqueda o filtrado.
  112. Nótese que “por empresa” se refiere siempre a la empresa de destino
  113. de la operación.
  114. El método Bolsa.inserta(op: Op) actualiza
  115. todas las estructuras de datos con la nueva operación op.
  116. El método que convierte la información en texto devuelve
  117. para cada empresa (de destino) una lista de sus operaciones.
  118.  
  119.  
  120. Ejemplo:
  121. e1 = Empresa("msft", 5000)
  122. e2 = Empresa("apple", 9000)
  123. b1 = Bolsa()
  124. b1.inserta(Venta(e1, e2, 10))
  125. b1.inserta(Compra(e2, e1, 4))
  126. print(b1)
  127.  
  128. Salida:
  129. Bolsa
  130. APPL:
  131. sell MSFT -> APPL: 10.00€
  132.  
  133. MSFT:
  134. bidd APPL -> MSFT: 4.00€
  135. """
  136. class Bolsa:
  137. def __init__(self):
  138. self.__ops = []
  139. self.__e_ops = {}
  140. self.__e_ventas = {}
  141. self.__e_compras = {}
  142.  
  143. @property
  144. def ops(self):
  145. return list(self.__ops)
  146.  
  147. @property
  148. def ops_por_empresa(self):
  149. return dict(self.__e_ops)
  150.  
  151. @property
  152. def ventas_por_empresa(self):
  153. return dict(self.__e_ventas)
  154.  
  155. @property
  156. def compras_por_empresa(self):
  157. return dict(self.__e_compras)
  158.  
  159. def inserta(self, op: Op):
  160. nombre_empresa = op.empresa_dest.id
  161.  
  162. # General
  163. self.__ops.append(op)
  164.  
  165. # General por empresa
  166. l_ops = self.__e_ops.get(nombre_empresa)
  167.  
  168. if not l_ops:
  169. l_ops = []
  170. self.__e_ops[nombre_empresa] = l_ops
  171.  
  172. l_ops.append(op)
  173.  
  174. # Compras por empresa
  175. if isinstance(op, Compra):
  176. l_ops = self.__e_compras.get(nombre_empresa)
  177.  
  178. if not l_ops:
  179. l_ops = []
  180. self.__e_compras[nombre_empresa] = l_ops
  181.  
  182. l_ops.append(op)
  183.  
  184. # Ventas por empresa
  185. if isinstance(op, Venta):
  186. l_ops = self.__e_ventas.get(nombre_empresa)
  187.  
  188. if not l_ops:
  189. l_ops = []
  190. self.__e_ventas[nombre_empresa] = l_ops
  191.  
  192. l_ops.append(op)
  193.  
  194. def __str__(self):
  195. toret = ""
  196. for (k, vals) in self.ops_por_empresa.items():
  197. toret += k + ":\n" + str.join("\n", [str(val) for val in vals])
  198. toret += "\n\n"
  199.  
  200. return toret
  201.  
  202. """
  203. 3. (4 pts.)Cree el método Bolsa.get_compras_de(e: Empresa)
  204. y Bolsa.get_ventas_de(e: Empresa), que devuelven,
  205. respectivamente, una lista de compras en las que
  206. la empresa de destino es la empresa e,
  207. y una lista de ventas en las que la empresa de destino es la empresa e.
  208. En caso de no existir operaciones, devolverá la lista vacía.
  209. Cree el método Bolsa.copia(),
  210. que devuelve un nuevo objeto Bolsa idéntico.
  211. Reescribe el método necesario para que al ejecutar len(b),
  212. siendo b un objeto Bolsa, devuelva el número total de
  213. operaciones almacenadas.
  214. """
  215.  
  216. def _Bolsa_get_compras_de(self, empresa: str) -> list:
  217. ops = self.__e_compras.get(empresa)
  218. return list(ops if ops else [])
  219.  
  220. def _Bolsa_get_ventas_de(self, empresa: str) -> list:
  221. ops = self.__e_ventas.get(empresa)
  222. return list(ops if ops else [])
  223.  
  224. def _Bolsa_copia(self):
  225. toret = Bolsa()
  226.  
  227. for old_op in self.ops:
  228. toret.inserta(old_op)
  229.  
  230. return toret
  231.  
  232. def _Bolsa_len_(self):
  233. return len(self.ops)
  234.  
  235. Bolsa.get_compras_de = _Bolsa_get_compras_de
  236. Bolsa.get_ventas_de = _Bolsa_get_ventas_de
  237. Bolsa.copia = _Bolsa_copia
  238. Bolsa.__len__ = _Bolsa_len_
  239.  
  240. # Preguntas exclusivas Parcial 2 (pregunta 1 era común)-----
  241.  
  242. """
  243. 2. (4 pts.) Modifica la clase Op de forma que ofrezca
  244. la siguiente funcionalidad sin crear nuevos métodos ni propiedades.
  245. Siendo op un objeto de la clase Op, al ejecutar op.dts[0] se debe devolver
  246. la empresa de origen en la operación.
  247. Con op.dts[1] se debe devolver la empresa de destino de la operación.
  248. Finalmente, con op.dts[2] se retorna el valor de la operación.
  249. Así, si c1 = Compra(Empresa(“Apple”, 12000), Empresa(“Microsoft”, 9000), 42))
  250. entonces c1.dts[0].id == “APPL”, c1.dts[1].id == “MICR”,
  251. mientras que c1.dts[2] == 42.
  252. """
  253. def _Op_get_attr(self, item):
  254. if item == "dts":
  255. return [self.empresa_org, self.empresa_dest, self.valor]
  256.  
  257. raise AttributeError(item)
  258.  
  259. Op.__getattr__ = _Op_get_attr
  260.  
  261.  
  262. """
  263. 3. (3 pts.) Modifica la clase Op de forma que soporte el método to_csv(f),
  264. siendo f un archivo ya abierto.
  265. Guardará dos líneas de texto con cuatro campos separados por comas,
  266. la primera con una cabecera y la segunda con los datos de la operación,
  267. precedida de “bidd” si es una compra, y “sell” si es una venta.
  268. De las empresas solo se guarda su id.
  269. Por ejemplo,
  270. si c1 = Venta(Empresa(“Apple”, 12000), Empresa(“Microsoft”, 9000), 42),
  271. entonces obtendríamos:
  272.  
  273. Salida:
  274.  
  275. C/V, Empresa org., Empresa dest., Valor
  276. sell, APPL, MICR, 42
  277. """
  278. def _Op_to_csv(self, f):
  279. wr = csv.writer(f)
  280. es_compra = "bidd" if type(self) == Compra else "sell"
  281. wr.writerow(["Compra/Venta", "Empresa org.", "Empresa dest.", "valor"])
  282. wr.writerow([es_compra, self.empresa_org.id, self.empresa_dest.id, self.valor])
  283.  
  284. Op.to_csv = _Op_to_csv
  285.  
  286.  
  287. # Tests ----------------------------------------------------
  288. class TestEmpresa(unittest.TestCase):
  289. def setUp(self):
  290. self.ids = ["MICR", "APPL"]
  291. self.vals = [2000, 1000]
  292. self.empresas = []
  293.  
  294. for (i, id) in enumerate(self.ids):
  295. self.empresas.append(Empresa(id.title() + (" " * rnd.randint(0, 10)), self.vals[i]))
  296.  
  297. def test_creacion(self):
  298. for (i, id) in enumerate(self.ids):
  299. self.assertEqual(id, self.empresas[i].id)
  300. self.assertEqual(self.vals[i], self.empresas[i].valor)
  301.  
  302. def test_str(self):
  303. dstrs = ["MICR (2000pts.)", "APPL (1000pts.)"]
  304.  
  305. for (i, dstr) in enumerate(dstrs):
  306. self.assertEqual(dstr, str(self.empresas[i]))
  307.  
  308.  
  309. class TestOps(unittest.TestCase):
  310. def setUp(self):
  311. self.e1 = Empresa("Microsoft", 1000)
  312. self.e2 = Empresa("Apple", 2000)
  313.  
  314. self.val_compras = [50, 90]
  315. self.val_ventas = [20, 45]
  316. self.empresas_compras = [(self.e1, self.e2), (self.e2, self.e1)]
  317. self.empresas_ventas = [(self.e2, self.e1), (self.e1, self.e2)]
  318. self.compras = []
  319. self.ventas = []
  320.  
  321. for (i, empresa) in enumerate(self.empresas_compras):
  322. empresas = self.empresas_compras[i]
  323. self.compras.append(Compra(empresas[0], empresas[1], self.val_compras[i]))
  324.  
  325. for (i, empresa) in enumerate(self.empresas_ventas):
  326. empresas = self.empresas_ventas[i]
  327. self.ventas.append(Venta(empresas[0], empresas[1], self.val_ventas[i]))
  328.  
  329. def test_creacion_str(self):
  330. for (i, compra) in enumerate(self.compras):
  331. empresas = self.empresas_compras[i]
  332. self.assertEqual(empresas[0], compra.empresa_org)
  333. self.assertEqual(empresas[1], compra.empresa_dest)
  334. self.assertEqual(f"bidd {empresas[0].id} -> {empresas[1].id}: {self.val_compras[i]}.00€", str(compra))
  335.  
  336. for (i, venta) in enumerate(self.ventas):
  337. empresas = self.empresas_ventas[i]
  338. self.assertEqual(empresas[0], venta.empresa_org)
  339. self.assertEqual(empresas[1], venta.empresa_dest)
  340. self.assertEqual(f"sell {empresas[0].id} -> {empresas[1].id}: {self.val_ventas[i]}.00€", str(venta))
  341.  
  342.  
  343. class TestBolsa(unittest.TestCase):
  344. def setUp(self):
  345. self.b = Bolsa()
  346. self.e1 = Empresa("Microsoft", 1000)
  347. self.e2 = Empresa("Apple", 2000)
  348.  
  349. self.val_compras = [50, 90]
  350. self.val_ventas = [20, 45]
  351. self.empresas_compras = [(self.e1, self.e2), (self.e2, self.e1)]
  352. self.empresas_ventas = [(self.e2, self.e1), (self.e1, self.e2)]
  353. self.compras = []
  354. self.ventas = []
  355.  
  356. for (i, empresa) in enumerate(self.empresas_compras):
  357. empresas = self.empresas_compras[i]
  358. self.compras.append(Compra(empresas[0], empresas[1], self.val_compras[i]))
  359.  
  360. for (i, empresa) in enumerate(self.empresas_ventas):
  361. empresas = self.empresas_ventas[i]
  362. self.ventas.append(Venta(empresas[0], empresas[1], self.val_ventas[i]))
  363.  
  364. def test_creacion(self):
  365. self.assertEqual(0, len(self.b))
  366.  
  367. def test_carga(self):
  368. self.assertEqual(0, len(self.b))
  369. all_ops = self.compras + self.ventas
  370.  
  371. for op in all_ops:
  372. self.b.inserta(op)
  373.  
  374. self.assertEqual(len(all_ops), len(self.b))
  375. self.assertEqual(len(all_ops), len(self.b.ops))
  376. self.assertEqual(len(self.b.ops), len(self.b))
  377.  
  378. all_ops_set = set(all_ops)
  379. for op in self.b.ops:
  380. all_ops_set.remove(op)
  381.  
  382. self.assertEqual(0, len(all_ops_set))
  383.  
  384. all_ops_por_empresa_set = set(all_ops)
  385. for k in self.b.ops_por_empresa.keys():
  386. for op in self.b.ops_por_empresa[k]:
  387. all_ops_por_empresa_set.remove(op)
  388.  
  389. self.assertEqual(0, len(all_ops_por_empresa_set))
  390.  
  391. compras_set = set(self.compras)
  392. for ks in self.b.compras_por_empresa.keys():
  393. for op in self.b.compras_por_empresa[ks]:
  394. compras_set.remove(op)
  395.  
  396. self.assertEqual(0, len(compras_set))
  397.  
  398. ventas_set = set(self.ventas)
  399. for ks in self.b.ventas_por_empresa.keys():
  400. for op in self.b.ventas_por_empresa[ks]:
  401. ventas_set.remove(op)
  402.  
  403. self.assertEqual(0, len(ventas_set))
  404.  
  405. def test_carga_por_empresa(self):
  406. self.assertEqual(0, len(self.b))
  407. compras_por_empresa = defaultdict(list)
  408.  
  409. for op in self.compras:
  410. self.b.inserta(op)
  411. compras_por_empresa[op.empresa_dest.id].append(op)
  412.  
  413. for kid in compras_por_empresa:
  414. bolsa_ops = set(self.b.compras_por_empresa[kid])
  415. test_compras = set(compras_por_empresa[kid])
  416. self.assertEqual(len(test_compras), len(bolsa_ops))
  417. for op in test_compras:
  418. bolsa_ops.remove(op)
  419. self.assertEqual(0, len(bolsa_ops))
  420.  
  421.  
  422. class TestListOp(unittest.TestCase):
  423. def setUp(self):
  424. self.e1 = Empresa("ibm", 10000)
  425. self.e2 = Empresa("microsoft", 25000)
  426. self.c1 = Compra(self.e1, self.e2, 45)
  427.  
  428. def test_dts(self):
  429. self.assertEqual(self.e1, self.c1.dts[0])
  430. self.assertEqual(self.e2, self.c1.dts[1])
  431. self.assertEqual(self.c1.valor, self.c1.dts[2])
  432.  
  433. def test_csv(self):
  434. escrito = ""
  435. with io.StringIO() as out_str:
  436. self.c1.to_csv(out_str)
  437. escrito = out_str.getvalue().strip()
  438.  
  439. for (i, linea) in enumerate(escrito.split('\n')):
  440. componentes = linea.split(',')
  441. self.assertEqual(4, len(componentes))
  442. componentes = [campo.strip() for campo in componentes]
  443.  
  444. if i == 0:
  445. self.assertEqual("valor", componentes[3])
  446. if i == 1:
  447. self.assertEqual("bidd", componentes[0])
  448. self.assertEqual(componentes[1], self.e1.id)
  449. self.assertEqual(componentes[2], self.e2.id)
  450. self.assertEqual(str(self.c1.valor), componentes[3])
  451.  
  452.  
  453. def simple_chk_parcial1():
  454. e1 = Empresa("msft", 5000)
  455. e2 = Empresa("apple", 9000)
  456. print(e1)
  457. print(e2)
  458.  
  459. print("\nBolsa")
  460. bolsa = Bolsa()
  461. bolsa.inserta(Venta(e1, e2, 10))
  462. bolsa.inserta(Compra(e2, e1, 4))
  463. print(bolsa)
  464.  
  465. print("\nCopia de bolsa")
  466. b2 = bolsa.copia()
  467. b2.inserta(Compra(e2, e1, 400))
  468. print(b2)
  469.  
  470. print("\nBolsa")
  471. print(bolsa)
  472.  
  473. print("\nVentas de MSFT")
  474. print(str.join(", ", [str(v) for v in bolsa.get_ventas_de("MSFT")]))
  475.  
  476. print("\nVentas de Apple")
  477. print(str.join(", ", [str(v) for v in bolsa.get_ventas_de("APPL")]))
  478.  
  479. print("\nCompras de MSFT")
  480. print(str.join(", ", [str(v) for v in bolsa.get_compras_de("MSFT")]))
  481.  
  482. print("\nCompras de Apple")
  483. print(str.join(", ", [str(v) for v in bolsa.get_compras_de("APPL")]))
  484.  
  485.  
  486. def simple_chk_parcial2():
  487. e1 = Empresa("Ibm", 5000)
  488. e2 = Empresa("Microsoft", 15000)
  489. c1 = Compra(e1, e2, 50)
  490. print(c1)
  491. print(f"{len(c1.dts)=}")
  492. print(f"{c1.dts[0].id=}")
  493. print(f"{c1.dts[1].id=}")
  494. print(f"{c1.dts[2]=}")
  495.  
  496. with io.StringIO() as out_str:
  497. c1.to_csv(out_str)
  498. print(out_str.getvalue())
  499.  
  500.  
  501. if __name__ == "__main__":
  502. op = input("Chk parcial[1], Chk parcial[2], [T]ests (1/2/t): ")
  503. op = op.strip().lower()
  504. print()
  505.  
  506. if op == "1":
  507. print("Chk parcial 1:")
  508. simple_chk_parcial1()
  509. elif op == "2":
  510. print("Chk parcial 2:")
  511. simple_chk_parcial2()
  512. elif op == "t":
  513. print("Tests:")
  514. unittest.main()
  515. else:
  516. print("No puedes hacer eso.")
  517.  
Success #stdin #stdout #stderr 0.05s 12564KB
stdin
t
stdout
Chk parcial[1], Chk parcial[2], [T]ests (1/2/t): 
Tests:
stderr
........
----------------------------------------------------------------------
Ran 8 tests in 0.001s

OK