data_service.py 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324
  1. import json
  2. from math import log
  3. import os
  4. import re
  5. import psycopg2
  6. from config.settings import POSTGRESQL_DB_CONFIG
  7. from typing import List, Dict, Optional, Any
  8. from abc import ABC, abstractmethod
  9. from config.settings import BG_DATA_PATH, DB_PATH, IMAGE_PATH, PRODUCT_DATA_PATH, CURRENT_URL
  10. from logging import getLogger
  11. from datetime import datetime
  12. from cryptography.fernet import Fernet
  13. from config.settings import PIN_KEY
  14. import base64 as b64
  15. # Import models
  16. from models.user import User
  17. from models.items import Product
  18. from models.sales import Sale
  19. from models.blacklist import Blacklist
  20. fernet = Fernet(PIN_KEY.encode())
  21. logger = getLogger(__name__)
  22. """
  23. ESQUEMA DE BASE DE DATOS SQLITE (data.db)
  24. 1. Tabla: users
  25. -----------------------------------
  26. - id SERIAL PRIMARY KEY
  27. - email VARCHAR(255) UNIQUE NOT NULL
  28. - name VARCHAR(255) NOT NULL
  29. - rut VARCHAR(20) UNIQUE NOT NULL
  30. - pin_hash TEXT NOT NULL (encriptado)
  31. - kleincoins TEXT NOT NULL (encriptado, valor por defecto "0")
  32. - created_at TEXT NOT NULL (fecha de creación en formato ISO 8601)
  33. (Guarda la información del usuario con su pin hasheado y kleincoins encriptadas)
  34. 2. Tabla: products
  35. -----------------------------------
  36. - id INTEGER PRIMARY KEY
  37. - name VARCHAR(255) NOT NULL
  38. - type VARCHAR(100) NOT NULL
  39. - description TEXT NOT NULL
  40. - price INTEGER NOT NULL
  41. - image TEXT (URL de la imagen)
  42. - status INTEGER DEFAULT 1 NOT NULL CHECK (status IN (0, 1)) -- 0: Inactivo, 1: Activo
  43. (Guarda los productos disponibles para venta con su estado activo/inactivo)
  44. 3. Tabla: sales
  45. -----------------------------------
  46. - id SERIAL PRIMARY KEY
  47. - user_id INTEGER NOT NULL REFERENCES users.id
  48. - total INTEGER NOT NULL (precio total de la venta)
  49. - fudo_id TEXT UNIQUE NOT NULL (ID string único por venta)
  50. - date TEXT NOT NULL (fecha y hora en formato ISO 8601)
  51. - table INTEGER NOT NULL (número de mesa)
  52. (Guarda cada venta, asociada a un usuario y mesa)
  53. 4. Tabla: sale_products
  54. -----------------------------------
  55. - sale_id INTEGER NOT NULL (relación a sales.id)
  56. - product_id INTEGER NOT NULL (relación a products.id)
  57. - quantity INTEGER NOT NULL DEFAULT 1 (cantidad del producto)
  58. (Relación muchos a muchos entre ventas y productos con cantidad)
  59. 5. Tabla: blacklist
  60. -----------------------------------
  61. - id SERIAL PRIMARY KEY
  62. - user_id INTEGER NOT NULL (relación a users.id)
  63. (Usuarios bloqueados o no autorizados para ciertas acciones)
  64. RELACIONES:
  65. -----------------------------------
  66. - users puede tener muchas sales
  67. - sales puede tener muchos productos (y viceversa), por eso se usa una tabla intermedia (sale_products)
  68. - products pueden repetirse en múltiples sales
  69. """
  70. # Base abstract class for data access
  71. class BaseDataService(ABC):
  72. """Abstract base class for data services"""
  73. def __init__(self):
  74. self.db_config = POSTGRESQL_DB_CONFIG
  75. def _get_connection(self):
  76. """Get database connection"""
  77. return psycopg2.connect(
  78. dbname=self.db_config['dbname'],
  79. user=self.db_config['user'],
  80. password=self.db_config['password'],
  81. host=self.db_config['host'],
  82. port=self.db_config['port']
  83. )
  84. @abstractmethod
  85. def get_all(self) -> List[Any]:
  86. """Get all records"""
  87. pass
  88. @abstractmethod
  89. def get_by_id(self, id: int) -> Optional[Any]:
  90. """Get record by ID"""
  91. pass
  92. @abstractmethod
  93. def create(self, **kwargs) -> int:
  94. """Create new record"""
  95. pass
  96. @abstractmethod
  97. def update(self, id: int, **kwargs) -> bool:
  98. """Update record"""
  99. pass
  100. @abstractmethod
  101. def delete(self, id: int) -> bool:
  102. """Delete record"""
  103. pass
  104. # User Data Service
  105. class UserDataService(BaseDataService):
  106. """Service for managing user data"""
  107. #region Create
  108. def create(self, name: str, email: str, rut: str, pin_hash: str) -> int:
  109. """Add a new user to the database"""
  110. conn = self._get_connection()
  111. cursor = conn.cursor()
  112. try:
  113. cursor.execute(
  114. "INSERT INTO users (name, email, rut, pin_hash, kleincoins, created_at) VALUES (%s, %s, %s, %s, %s, %s) RETURNING id",
  115. (name, email, rut, fernet.encrypt(pin_hash.encode()).decode(), fernet.encrypt(b"0").decode(), datetime.now().isoformat())
  116. )
  117. conn.commit()
  118. user_id = cursor.fetchone()[0]
  119. if user_id:
  120. logger.info(f"User added with ID: {user_id}")
  121. return user_id
  122. else:
  123. logger.error("Failed to add user.")
  124. return -1
  125. except psycopg2.IntegrityError as e:
  126. logger.error(f"Failed to add user: {e}")
  127. return -1
  128. finally:
  129. conn.close()
  130. #endregion
  131. #region Read
  132. def get_all(self) -> List[User]:
  133. """Get all users from the database"""
  134. conn = self._get_connection()
  135. cursor = conn.cursor()
  136. cursor.execute("SELECT * FROM users")
  137. users = cursor.fetchall()
  138. conn.close()
  139. return [
  140. User(
  141. id=user[0],
  142. email=user[1],
  143. name=user[2],
  144. rut=user[3],
  145. pin_hash=fernet.decrypt(user[4].encode()).decode(),
  146. kleincoins=fernet.decrypt(user[5].encode()).decode(),
  147. created_at=user[6]
  148. ) for user in users
  149. ]
  150. def get_by_id(self, user_id: int) -> Optional[User]:
  151. """Get user data from the database"""
  152. conn = self._get_connection()
  153. cursor = conn.cursor()
  154. cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
  155. user = cursor.fetchone()
  156. conn.close()
  157. if user:
  158. return User(
  159. id=user[0],
  160. email=user[1],
  161. name=user[2],
  162. rut=user[3],
  163. pin_hash=fernet.decrypt(user[4].encode()).decode(),
  164. kleincoins=fernet.decrypt(user[5].encode()).decode(),
  165. created_at=user[6]
  166. )
  167. return None
  168. def get_by_email(self, email: str) -> Optional[User]:
  169. """Get user by email"""
  170. conn = self._get_connection()
  171. cursor = conn.cursor()
  172. cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
  173. user = cursor.fetchone()
  174. conn.close()
  175. logger.debug(f"get_by_email: {email}, user: {user}")
  176. if user:
  177. return User(
  178. id=user[0],
  179. email=user[1],
  180. name=user[2],
  181. rut=user[3],
  182. pin_hash=user[4],
  183. kleincoins=fernet.decrypt(user[5].encode()).decode(),
  184. created_at=user[6]
  185. )
  186. return None
  187. def login(self, email: str, pin_hashed: str) -> Optional[User]:
  188. """Login user by email and pin hash"""
  189. user = self.get_by_email(email)
  190. if user and fernet.decrypt(user.pin_hash.encode()).decode() == pin_hashed:
  191. logger.info(f"User {user.email} logged in successfully.")
  192. return user
  193. else:
  194. logger.error("Login failed: Invalid email or pin.")
  195. return None
  196. def permissions(self, user_id: int) -> int:
  197. """Get user permissions"""
  198. conn = self._get_connection()
  199. cursor = conn.cursor()
  200. cursor.execute("SELECT permissions FROM users WHERE id = %s", (user_id,))
  201. result = cursor.fetchone()
  202. if not result:
  203. logger.error(f"User with ID {user_id} not found.")
  204. return 0
  205. result = result[0]
  206. conn.close()
  207. if result:
  208. return result[0]
  209. return 0
  210. def get_by_rut(self, rut: str) -> Optional[User]:
  211. """Get user by RUT"""
  212. conn = self._get_connection()
  213. cursor = conn.cursor()
  214. cursor.execute("SELECT * FROM users WHERE rut = %s", (rut,))
  215. user = cursor.fetchone()
  216. if not user:
  217. logger.error(f"User with RUT {rut} not found.")
  218. conn.close()
  219. return None
  220. user = user[0]
  221. conn.close()
  222. if user:
  223. return User(
  224. id=user[0],
  225. email=user[1],
  226. name=user[2],
  227. rut=user[3],
  228. pin_hash=fernet.decrypt(user[4].encode()).decode(),
  229. kleincoins=fernet.decrypt(user[5].encode()).decode(),
  230. created_at=user[6]
  231. )
  232. return None
  233. #endregion
  234. #region Update
  235. def update(self, user_id: int, email=None, name=None, rut=None, pin_hash=None, kleincoins=None) -> bool:
  236. """Update user information in the database"""
  237. conn = self._get_connection()
  238. cursor = conn.cursor()
  239. updates = []
  240. params = []
  241. if email:
  242. updates.append("email = %s")
  243. params.append(email)
  244. if name:
  245. updates.append("name = %s")
  246. params.append(name)
  247. if rut:
  248. updates.append("rut = %s")
  249. params.append(rut)
  250. if pin_hash:
  251. updates.append("pin_hash = %s")
  252. params.append(fernet.encrypt(pin_hash.encode()).decode())
  253. if kleincoins is not None:
  254. updates.append("kleincoins = %s")
  255. params.append(fernet.encrypt(str(kleincoins).encode()).decode())
  256. if not updates:
  257. conn.close()
  258. return False
  259. try:
  260. cursor.execute(f"UPDATE users SET {', '.join(updates)} WHERE id = %s", (*params, user_id))
  261. conn.commit()
  262. success = cursor.rowcount > 0
  263. if success:
  264. logger.info(f"User with ID {user_id} updated.")
  265. return success
  266. except psycopg2.IntegrityError as e:
  267. logger.error(f"Failed to update user: {e}")
  268. return False
  269. finally:
  270. conn.close()
  271. #endregion
  272. #region Delete
  273. def delete(self, user_id: int) -> bool:
  274. """Delete a user from the database"""
  275. conn = self._get_connection()
  276. cursor = conn.cursor()
  277. cursor.execute("DELETE FROM users WHERE id = %s", (user_id,))
  278. conn.commit()
  279. conn.close()
  280. if cursor.rowcount > 0:
  281. logger.info(f"User with ID {user_id} deleted.")
  282. return True
  283. else:
  284. logger.error(f"Failed to delete user with ID {user_id}.")
  285. return False
  286. def update_kleincoins(self, user_id: int, kleincoins: int) -> bool:
  287. """Update user's kleincoins"""
  288. conn = self._get_connection()
  289. cursor = conn.cursor()
  290. try:
  291. cursor.execute(
  292. "UPDATE users SET kleincoins = %s WHERE id = %s",
  293. (fernet.encrypt(str(kleincoins).encode()).decode(), user_id)
  294. )
  295. conn.commit()
  296. success = cursor.rowcount > 0
  297. if success:
  298. logger.info(f"Kleincoins updated for user {user_id}: {kleincoins}")
  299. return success
  300. except psycopg2.IntegrityError as e:
  301. logger.error(f"Failed to update kleincoins: {e}")
  302. return False
  303. finally:
  304. conn.close()
  305. def get_kleincoins(self, user_id: int) -> Optional[int]:
  306. """Get user's kleincoins"""
  307. conn = self._get_connection()
  308. cursor = conn.cursor()
  309. cursor.execute("SELECT kleincoins FROM users WHERE id = %s", (user_id,))
  310. result = cursor.fetchone()
  311. conn.close()
  312. if result:
  313. try:
  314. return int(fernet.decrypt(result[0].encode()).decode())
  315. except Exception as e:
  316. logger.error(f"Failed to decrypt kleincoins for user {user_id}: {e}")
  317. return None
  318. return None
  319. #endregion
  320. # Blacklist Data Service
  321. class BlacklistDataService(BaseDataService):
  322. """Service for managing blacklisted users"""
  323. #region Create
  324. def create(self, user_id: int) -> int:
  325. """Add a user to the blacklist"""
  326. conn = self._get_connection()
  327. cursor = conn.cursor()
  328. try:
  329. cursor.execute("INSERT INTO blacklist (user_id) VALUES (%s) RETURNING id", (user_id,))
  330. conn.commit()
  331. blacklist_id = cursor.fetchone()[0]
  332. if blacklist_id:
  333. logger.info(f"User with ID {user_id} added to blacklist.")
  334. return blacklist_id
  335. else:
  336. logger.error(f"Failed to add user with ID {user_id} to blacklist.")
  337. return -1
  338. except psycopg2.IntegrityError as e:
  339. logger.error(f"Failed to add user to blacklist: {e}")
  340. return -1
  341. finally:
  342. conn.close()
  343. #endregion
  344. #region Read
  345. def get_all(self) -> List[Blacklist]:
  346. """Get all blacklisted users"""
  347. conn = self._get_connection()
  348. cursor = conn.cursor()
  349. cursor.execute("""
  350. SELECT b.id, b.user_id, u.email, u.name, u.rut
  351. FROM blacklist b
  352. LEFT JOIN users u ON b.user_id = u.id
  353. """)
  354. blacklisted = cursor.fetchall()
  355. conn.close()
  356. return [
  357. Blacklist(
  358. id=row[0],
  359. user_id=row[1],
  360. email=row[2],
  361. name=row[3],
  362. rut=row[4]
  363. ) for row in blacklisted
  364. ]
  365. def get_by_id(self, id: int) -> Optional[Blacklist]:
  366. """Get blacklist entry by ID"""
  367. conn = self._get_connection()
  368. cursor = conn.cursor()
  369. cursor.execute("""
  370. SELECT b.id, b.user_id, u.email, u.name, u.rut
  371. FROM blacklist b
  372. LEFT JOIN users u ON b.user_id = u.id
  373. WHERE b.id = %s
  374. """, (id,))
  375. row = cursor.fetchone()
  376. conn.close()
  377. if row:
  378. return Blacklist(
  379. id=row[0],
  380. user_id=row[1],
  381. email=row[2],
  382. name=row[3],
  383. rut=row[4]
  384. )
  385. return None
  386. def get_blacklisted_user_ids(self) -> List[int]:
  387. """Get a list of blacklisted user IDs"""
  388. conn = self._get_connection()
  389. cursor = conn.cursor()
  390. cursor.execute("SELECT user_id FROM blacklist")
  391. blacklisted_users = [row[0] for row in cursor.fetchall()]
  392. conn.close()
  393. return blacklisted_users
  394. def is_user_blacklisted(self, user_id: int) -> bool:
  395. """Check if a user is blacklisted"""
  396. conn = self._get_connection()
  397. cursor = conn.cursor()
  398. cursor.execute("SELECT * FROM blacklist WHERE user_id = ?", (user_id,))
  399. blacklisted = cursor.fetchone() is not None
  400. conn.close()
  401. return blacklisted
  402. #endregion
  403. #region Update
  404. def update(self, id: int, **kwargs) -> bool:
  405. """Update blacklist entry (not commonly used)"""
  406. # Blacklist entries typically don't need updates
  407. return False
  408. #endregion
  409. #region Delete
  410. def delete(self, id: int) -> bool:
  411. """Remove a blacklist entry by ID"""
  412. conn = self._get_connection()
  413. cursor = conn.cursor()
  414. cursor.execute("DELETE FROM blacklist WHERE id = %s", (id,))
  415. conn.commit()
  416. success = cursor.rowcount > 0
  417. conn.close()
  418. if success:
  419. logger.info(f"Blacklist entry with ID {id} removed.")
  420. else:
  421. logger.error(f"Failed to remove blacklist entry with ID {id}.")
  422. return success
  423. def remove_user_from_blacklist(self, user_id: int) -> bool:
  424. """Remove a user from the blacklist"""
  425. conn = self._get_connection()
  426. cursor = conn.cursor()
  427. cursor.execute("DELETE FROM blacklist WHERE user_id = %s", (user_id,))
  428. conn.commit()
  429. success = cursor.rowcount > 0
  430. conn.close()
  431. if success:
  432. logger.info(f"User with ID {user_id} removed from blacklist.")
  433. else:
  434. logger.error(f"Failed to remove user with ID {user_id} from blacklist.")
  435. return success
  436. #endregion
  437. # Product Data Service
  438. class ProductDataService(BaseDataService):
  439. """Service for managing products"""
  440. #region Create
  441. def create(self, id: int, name: str, price: float, type: Optional[str] = None, description: Optional[str] = None, image: Optional[str] = None, status: int = 1) -> int:
  442. """Add a new product to the database"""
  443. conn = self._get_connection()
  444. cursor = conn.cursor()
  445. if image and "base64" in image:
  446. extension = re.search(r'data:image/(.*%s);base64,', image)
  447. image_name = f"{id}_img"
  448. if extension:
  449. image_name += f".{extension.group(1)}"
  450. image = self._image_process(image_name, image)
  451. try:
  452. cursor.execute(
  453. "INSERT INTO products (id, name, type, description, price, image, status) VALUES (%s, %s, %s, %s, %s, %s, %s) RETURNING id",
  454. (id, name, type, description, price, image, status)
  455. )
  456. conn.commit()
  457. product_id = cursor.fetchone()[0]
  458. if product_id:
  459. logger.info(f"Product added with ID: {product_id}")
  460. return product_id
  461. else:
  462. logger.error("Failed to add product.")
  463. return -1
  464. except psycopg2.Error as e:
  465. logger.error(f"Failed to add product: {e}")
  466. return -1
  467. finally:
  468. conn.close()
  469. def load_data(self, products: List[Dict[str, str]]) -> None:
  470. """Load multiple products from a list of dictionaries"""
  471. conn = self._get_connection()
  472. cursor = conn.cursor()
  473. for product in products:
  474. try:
  475. self.create(
  476. id=int(product['id']),
  477. name=product['name'],
  478. price=int(product['price']),
  479. type=product.get('type'),
  480. description=product.get('description'),
  481. image=product.get('image'),
  482. status=int(product.get('status', 1)) # Default to active (1)
  483. )
  484. except Exception as e:
  485. logger.error(f"Failed to load product {product['name']}: {e}")
  486. conn.close()
  487. #endregion
  488. #region Read
  489. def get_all(self) -> List[Product]:
  490. """Get all products from the database"""
  491. conn = self._get_connection()
  492. cursor = conn.cursor()
  493. cursor.execute("SELECT * FROM products")
  494. products = cursor.fetchall()
  495. conn.close()
  496. return [
  497. Product(
  498. id=product[0],
  499. name=product[1],
  500. type=product[2],
  501. description=product[3],
  502. price=product[4],
  503. image=product[5],
  504. status=product[6]
  505. ) for product in products
  506. ]
  507. def get_by_id(self, product_id: int) -> Optional[Product]:
  508. """Get product by ID"""
  509. conn = self._get_connection()
  510. cursor = conn.cursor()
  511. cursor.execute("SELECT * FROM products WHERE id = %s", (product_id,))
  512. product = cursor.fetchone()
  513. conn.close()
  514. if product:
  515. return Product(
  516. id=product[0],
  517. name=product[1],
  518. type=product[2],
  519. description=product[3],
  520. price=product[4],
  521. image=product[5],
  522. status=product[6]
  523. )
  524. return None
  525. def get_by_type(self, product_type: str) -> List[Product]:
  526. """Get products by type"""
  527. conn = self._get_connection()
  528. cursor = conn.cursor()
  529. cursor.execute("SELECT * FROM products WHERE type = %s", (product_type,))
  530. products = cursor.fetchall()
  531. conn.close()
  532. return [
  533. Product(
  534. id=product[0],
  535. name=product[1],
  536. type=product[2],
  537. description=product[3],
  538. price=product[4],
  539. image=product[5],
  540. status=product[6]
  541. ) for product in products
  542. ]
  543. def search_by_name(self, name: str) -> List[Product]:
  544. """Search products by name"""
  545. conn = self._get_connection()
  546. cursor = conn.cursor()
  547. cursor.execute("SELECT * FROM products WHERE name LIKE %s", (f"%{name}%",))
  548. products = cursor.fetchall()
  549. conn.close()
  550. return [
  551. Product(
  552. id=product[0],
  553. name=product[1],
  554. type=product[2],
  555. description=product[3],
  556. price=product[4],
  557. image=product[5],
  558. status=product[6]
  559. ) for product in products
  560. ]
  561. def get_products(self, product_ids: List[int]) -> List[Product]:
  562. """Get multiple products by their IDs"""
  563. if not product_ids:
  564. return []
  565. placeholders = ', '.join('%s' for _ in product_ids)
  566. conn = self._get_connection()
  567. cursor = conn.cursor()
  568. cursor.execute(f"SELECT * FROM products WHERE id IN ({placeholders})", product_ids)
  569. products = cursor.fetchall()
  570. conn.close()
  571. return [
  572. Product(
  573. id=product[0],
  574. name=product[1],
  575. type=product[2],
  576. description=product[3],
  577. price=product[4],
  578. image=product[5],
  579. status=product[6]
  580. ) for product in products
  581. ]
  582. #endregion
  583. #region Update
  584. def _image_process(self, image: str, base64: str) -> str:
  585. """Process image for storage"""
  586. if not image or not base64:
  587. raise ValueError("Image and base64 data must be provided")
  588. image_path = os.path.join(IMAGE_PATH, image)
  589. if not os.path.exists(IMAGE_PATH):
  590. os.makedirs(IMAGE_PATH)
  591. with open(image_path, 'wb') as img_file:
  592. img_file.write(b64.b64decode(base64.split(',')[1]))
  593. return CURRENT_URL+"/images/" + image # Return url
  594. def update(self, product_id: int, name=None, type=None, description=None, price=None, image=None, status=None) -> bool:
  595. """Update product information"""
  596. conn = self._get_connection()
  597. cursor = conn.cursor()
  598. product = self.get_by_id(product_id)
  599. if not product:
  600. logger.error(f"Product with ID {product_id} not found.")
  601. return False
  602. updates = []
  603. params = []
  604. if name is not None:
  605. updates.append("name = %s")
  606. params.append(name)
  607. if type is not None:
  608. updates.append("type = %s")
  609. params.append(type)
  610. if description is not None:
  611. updates.append("description = %s")
  612. params.append(description)
  613. if price is not None:
  614. updates.append("price = %s")
  615. params.append(price)
  616. if image is not None:
  617. if product.image and product.image != image:
  618. if os.path.exists(os.path.join(IMAGE_PATH, product.image)):
  619. os.remove(os.path.join(IMAGE_PATH, product.image))
  620. try:
  621. extension = re.search(r'data:image/(.*%s);base64,', image)
  622. if not extension:
  623. raise ValueError("Invalid image format")
  624. extension = extension.group(1)
  625. image = self._image_process(f"{product.id}_img.{extension}", image)
  626. except ValueError as e:
  627. logger.error(f"Failed to process image: {e}")
  628. return False
  629. updates.append("image = %s")
  630. params.append(image)
  631. if status is not None:
  632. updates.append("status = %s")
  633. params.append(status)
  634. if not updates:
  635. conn.close()
  636. return False
  637. try:
  638. cursor.execute(f"UPDATE products SET {', '.join(updates)} WHERE id = %s", (*params, product_id))
  639. conn.commit()
  640. success = cursor.rowcount > 0
  641. if success:
  642. logger.info(f"Product with ID {product_id} updated.")
  643. return success
  644. except psycopg2.Error as e:
  645. logger.error(f"Failed to update product: {e}")
  646. return False
  647. finally:
  648. conn.close()
  649. def get_active_products(self) -> List[Product]:
  650. """Get only active products (status = 1)"""
  651. conn = self._get_connection()
  652. cursor = conn.cursor()
  653. cursor.execute("SELECT * FROM products WHERE status = 1")
  654. products = cursor.fetchall()
  655. conn.close()
  656. return [
  657. Product(
  658. id=product[0],
  659. name=product[1],
  660. type=product[2],
  661. description=product[3],
  662. price=product[4],
  663. image=product[5],
  664. status=product[6]
  665. ) for product in products
  666. ]
  667. def get_inactive_products(self) -> List[Product]:
  668. """Get only inactive products (status = 0)"""
  669. conn = self._get_connection()
  670. cursor = conn.cursor()
  671. cursor.execute("SELECT * FROM products WHERE status = 0")
  672. products = cursor.fetchall()
  673. conn.close()
  674. return [
  675. Product(
  676. id=product[0],
  677. name=product[1],
  678. type=product[2],
  679. description=product[3],
  680. price=product[4],
  681. image=product[5],
  682. status=product[6]
  683. ) for product in products
  684. ]
  685. def activate_product(self, product_id: int) -> bool:
  686. """Activate a product (set status to 1)"""
  687. return self.update(product_id, status=1)
  688. def deactivate_product(self, product_id: int) -> bool:
  689. """Deactivate a product (set status to 0)"""
  690. return self.update(product_id, status=0)
  691. def is_product_active(self, product_id: int) -> Optional[bool]:
  692. """Check if a product is active"""
  693. product = self.get_by_id(product_id)
  694. if product:
  695. return product.status == 1
  696. return None
  697. #endregion
  698. #region Delete
  699. def delete(self, product_id: int) -> bool:
  700. """Delete a product from the database"""
  701. conn = self._get_connection()
  702. cursor = conn.cursor()
  703. product = self.get_by_id(product_id)
  704. if product and product.image:
  705. os.remove(os.path.join(IMAGE_PATH, product.image.removeprefix(CURRENT_URL + "/images/")))
  706. cursor.execute("DELETE FROM products WHERE id = %s", (product_id,))
  707. conn.commit()
  708. success = cursor.rowcount > 0
  709. conn.close()
  710. if success:
  711. logger.info(f"Product with ID {product_id} deleted.")
  712. else:
  713. logger.error(f"Failed to delete product with ID {product_id}.")
  714. return success
  715. #endregion
  716. # Sales Data Service
  717. class SalesDataService(BaseDataService):
  718. """Service for managing sales"""
  719. #region C
  720. def create(self, user_id: int, fudo_id: str, total: float, table: int, product_ids: List[int], quantities: Optional[List[int]] = None) -> int:
  721. """Create a new sale with products and quantities"""
  722. conn = self._get_connection()
  723. cursor = conn.cursor()
  724. try:
  725. fecha = datetime.now().isoformat()
  726. # Insert sale
  727. cursor.execute(
  728. "INSERT INTO sales (user_id, total, fudo_id, date, table_number) VALUES (%s, %s, %s, %s, %s) RETURNING id",
  729. (user_id, total, fudo_id, fecha, table)
  730. )
  731. last_sale = cursor.fetchone()
  732. sale_id = last_sale[0] if last_sale else None
  733. if sale_id and product_ids:
  734. # Insert sale-product relationships with quantities
  735. if quantities is None:
  736. quantities = [1] * len(product_ids) # Default quantity 1
  737. for i, product_id in enumerate(product_ids):
  738. quantity = quantities[i] if i < len(quantities) else 1
  739. cursor.execute(
  740. "INSERT INTO sale_products (sale_id, product_id, quantity) VALUES (%s, %s, %s)",
  741. (sale_id, product_id, quantity)
  742. )
  743. conn.commit()
  744. if sale_id:
  745. logger.info(f"Sale created with ID: {sale_id}, fudo_id: {fudo_id}")
  746. return sale_id
  747. else:
  748. logger.error("Failed to create sale.")
  749. return -1
  750. except psycopg2.Error as e:
  751. logger.error(f"Failed to create sale: {e}")
  752. conn.rollback()
  753. return -1
  754. finally:
  755. conn.close()
  756. def add_product_to_sale(self, sale_id: int, product_id: int, quantity: int = 1) -> bool:
  757. """Add a product to an existing sale with quantity"""
  758. conn = self._get_connection()
  759. cursor = conn.cursor()
  760. try:
  761. cursor.execute(
  762. "INSERT INTO sale_products (sale_id, product_id, quantity) VALUES (%s, %s, %s)",
  763. (sale_id, product_id, quantity)
  764. )
  765. conn.commit()
  766. success = cursor.rowcount > 0
  767. if success:
  768. logger.info(f"Product {product_id} (qty: {quantity}) added to sale {sale_id}.")
  769. return success
  770. except psycopg2.IntegrityError as e:
  771. logger.error(f"Failed to add product to sale: {e}")
  772. return False
  773. finally:
  774. conn.close()
  775. def add_products_to_sale(self, sale_id: int, product_ids: List[int], quantities: Optional[List[int]] = None) -> bool:
  776. """Add multiple products to an existing sale with quantities"""
  777. conn = self._get_connection()
  778. cursor = conn.cursor()
  779. try:
  780. if quantities is None:
  781. quantities = [1] * len(product_ids) # Default quantity 1
  782. for i, product_id in enumerate(product_ids):
  783. quantity = quantities[i] if i < len(quantities) else 1
  784. cursor.execute(
  785. "INSERT INTO sale_products (sale_id, product_id, quantity) VALUES (%s, %s, %s)",
  786. (sale_id, product_id, quantity)
  787. )
  788. conn.commit()
  789. success = cursor.rowcount > 0
  790. if success:
  791. logger.info(f"Products added to sale {sale_id}.")
  792. return success
  793. except psycopg2.IntegrityError as e:
  794. logger.error(f"Failed to add products to sale: {e}")
  795. return False
  796. finally:
  797. conn.close()
  798. #endregion
  799. #region R
  800. def get_all(self) -> List[Sale]:
  801. """Get all sales from the database"""
  802. conn = self._get_connection()
  803. cursor = conn.cursor()
  804. cursor.execute("""
  805. SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s.table, u.name, u.email
  806. FROM sales s
  807. LEFT JOIN users u ON s.user_id = u.id
  808. ORDER BY s.date DESC
  809. """)
  810. sales = cursor.fetchall()
  811. conn.close()
  812. return [
  813. Sale(
  814. id=sale[0],
  815. user_id=sale[1],
  816. total=sale[2],
  817. fudo_id=sale[3],
  818. date=sale[4],
  819. table=sale[5],
  820. username=sale[6],
  821. user_email=sale[7],
  822. products=self.get_sale_products(sale[0])
  823. ) for sale in sales
  824. ]
  825. def get_by_id(self, sale_id: int) -> Optional[Sale]:
  826. """Get sale by ID"""
  827. conn = self._get_connection()
  828. cursor = conn.cursor()
  829. cursor.execute("""
  830. SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s.table, u.name, u.email
  831. FROM sales s
  832. LEFT JOIN users u ON s.user_id = u.id
  833. WHERE s.id = %s
  834. """, (sale_id,))
  835. sale = cursor.fetchone()
  836. conn.close()
  837. if sale:
  838. return Sale(
  839. id=sale[0],
  840. user_id=sale[1],
  841. total=sale[2],
  842. fudo_id=sale[3],
  843. date=sale[4],
  844. table=sale[5],
  845. username=sale[6],
  846. user_email=sale[7]
  847. )
  848. return None
  849. def get_by_fudo_id(self, fudo_id: str) -> Optional[Sale]:
  850. """Get sale by fudo_id"""
  851. conn = self._get_connection()
  852. cursor = conn.cursor()
  853. cursor.execute("""
  854. SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s.table, u.name, u.email
  855. FROM sales s
  856. LEFT JOIN users u ON s.user_id = u.id
  857. WHERE s.fudo_id = %s
  858. """, (fudo_id,))
  859. sale = cursor.fetchone()
  860. conn.close()
  861. if sale:
  862. return Sale(
  863. id=sale[0],
  864. user_id=sale[1],
  865. total=sale[2],
  866. fudo_id=sale[3],
  867. date=sale[4],
  868. table=sale[5],
  869. username=sale[6],
  870. user_email=sale[7]
  871. )
  872. return None
  873. def get_by_user(self, user_id: int) -> List[Sale]:
  874. """Get sales by user ID"""
  875. conn = self._get_connection()
  876. cursor = conn.cursor()
  877. cursor.execute(
  878. """
  879. SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s.table_number, u.name, u.email
  880. FROM sales s
  881. LEFT JOIN users u ON s.user_id = u.id
  882. WHERE s.user_id = %s
  883. ORDER BY s.date DESC
  884. """,
  885. (user_id,)
  886. )
  887. sales = cursor.fetchall()
  888. conn.close()
  889. return [
  890. Sale(
  891. id=sale[0],
  892. user_id=sale[1],
  893. total=sale[2],
  894. fudo_id=sale[3],
  895. date=sale[4],
  896. table=sale[5],
  897. username=sale[6],
  898. user_email=sale[7],
  899. products=self.get_sale_products(sale[0])
  900. ) for sale in sales
  901. ]
  902. def get_sale_products_ids(self, sale_id: int) -> List[int]:
  903. """Get product IDs for a specific sale"""
  904. conn = self._get_connection()
  905. cursor = conn.cursor()
  906. cursor.execute("""
  907. SELECT product_id FROM sale_products WHERE sale_id = %s
  908. """, (sale_id,))
  909. products = cursor.fetchall()
  910. conn.close()
  911. return [product[0] for product in products]
  912. def get_sale_products(self, sale_id: int) -> List[Product]:
  913. """Get products for a specific sale with quantities"""
  914. conn = self._get_connection()
  915. cursor = conn.cursor()
  916. cursor.execute("""
  917. SELECT p.id, p.name, p.type, p.description, p.price, p.image, p.status, sp.quantity
  918. FROM sale_products sp
  919. JOIN products p ON sp.product_id = p.id
  920. WHERE sp.sale_id = %s
  921. """, (sale_id,))
  922. products = cursor.fetchall()
  923. conn.close()
  924. return [
  925. Product(
  926. id=product[0],
  927. name=product[1],
  928. type=product[2],
  929. description=product[3],
  930. price=product[4],
  931. image=product[5],
  932. status=product[6],
  933. quantity=product[7]
  934. ) for product in products
  935. ]
  936. def get_by_table(self, table: int) -> List[Sale]:
  937. """Get sales by table number"""
  938. conn = self._get_connection()
  939. cursor = conn.cursor()
  940. cursor.execute(
  941. """
  942. SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s.table_number, u.name, u.email
  943. FROM sales s
  944. LEFT JOIN users u ON s.user_id = u.id
  945. WHERE s.table_number = %s
  946. ORDER BY s.date DESC
  947. """,
  948. (table,)
  949. )
  950. sales = cursor.fetchall()
  951. conn.close()
  952. return [
  953. Sale(
  954. id=sale[0],
  955. user_id=sale[1],
  956. total=sale[2],
  957. fudo_id=sale[3],
  958. date=sale[4],
  959. table=sale[5],
  960. username=sale[6],
  961. user_email=sale[7]
  962. ) for sale in sales
  963. ]
  964. def update_product_quantity(self, sale_id: int, product_id: int, new_quantity: int) -> bool:
  965. """Update quantity of a product in a sale"""
  966. conn = self._get_connection()
  967. cursor = conn.cursor()
  968. try:
  969. cursor.execute(
  970. "UPDATE sale_products SET quantity = %s WHERE sale_id = %s AND product_id = %s",
  971. (new_quantity, sale_id, product_id)
  972. )
  973. conn.commit()
  974. success = cursor.rowcount > 0
  975. if success:
  976. logger.info(f"Updated quantity to {new_quantity} for product {product_id} in sale {sale_id}.")
  977. return success
  978. except psycopg2.Error as e:
  979. logger.error(f"Failed to update product quantity: {e}")
  980. return False
  981. finally:
  982. conn.close()
  983. def get_product_quantity_in_sale(self, sale_id: int, product_id: int) -> Optional[int]:
  984. """Get quantity of a specific product in a sale"""
  985. conn = self._get_connection()
  986. cursor = conn.cursor()
  987. cursor.execute(
  988. "SELECT quantity FROM sale_products WHERE sale_id = %s AND product_id = %s",
  989. (sale_id, product_id)
  990. )
  991. result = cursor.fetchone()
  992. conn.close()
  993. return result[0] if result else None
  994. #endregion
  995. #region U
  996. def update(self, sale_id: int, user_id=None, total=None, table=None) -> bool:
  997. """Update sale information (products should be updated via specific methods)"""
  998. conn = self._get_connection()
  999. cursor = conn.cursor()
  1000. updates = []
  1001. params = []
  1002. if user_id is not None:
  1003. updates.append("user_id = %s")
  1004. params.append(user_id)
  1005. if total is not None:
  1006. updates.append("total = %s")
  1007. params.append(total)
  1008. if table is not None:
  1009. updates.append("table = %s")
  1010. params.append(table)
  1011. if not updates:
  1012. conn.close()
  1013. return False
  1014. try:
  1015. cursor.execute(f"UPDATE sales SET {', '.join(updates)} WHERE id = %s", (*params, sale_id))
  1016. conn.commit()
  1017. success = cursor.rowcount > 0
  1018. if success:
  1019. logger.info(f"Sale with ID {sale_id} updated.")
  1020. return success
  1021. except psycopg2.Error as e:
  1022. logger.error(f"Failed to update sale: {e}")
  1023. return False
  1024. finally:
  1025. conn.close()
  1026. #endregion
  1027. #region D
  1028. def delete(self, sale_id: int) -> bool:
  1029. """Delete a sale and its product relationships"""
  1030. conn = self._get_connection()
  1031. cursor = conn.cursor()
  1032. try:
  1033. # Delete sale-product relationships first
  1034. cursor.execute("DELETE FROM sale_products WHERE sale_id = %s", (sale_id,))
  1035. # Delete the sale
  1036. cursor.execute("DELETE FROM sales WHERE id = %s", (sale_id,))
  1037. conn.commit()
  1038. success = cursor.rowcount > 0
  1039. if success:
  1040. logger.info(f"Sale with ID {sale_id} deleted.")
  1041. else:
  1042. logger.error(f"Failed to delete sale with ID {sale_id}.")
  1043. return success
  1044. except psycopg2.Error as e:
  1045. logger.error(f"Failed to delete sale: {e}")
  1046. return False
  1047. finally:
  1048. conn.close()
  1049. def remove_product_from_sale(self, sale_id: int, product_id: int) -> bool:
  1050. """Remove a product from a sale (removes all quantity)"""
  1051. conn = self._get_connection()
  1052. cursor = conn.cursor()
  1053. cursor.execute(
  1054. "DELETE FROM sale_products WHERE sale_id = %s AND product_id = %s",
  1055. (sale_id, product_id)
  1056. )
  1057. conn.commit()
  1058. success = cursor.rowcount > 0
  1059. conn.close()
  1060. if success:
  1061. logger.info(f"Product {product_id} removed from sale {sale_id}.")
  1062. else:
  1063. logger.error(f"Failed to remove product {product_id} from sale {sale_id}.")
  1064. return success
  1065. def decrease_product_quantity(self, sale_id: int, product_id: int, decrease_by: int = 1) -> bool:
  1066. """Decrease quantity of a product in a sale, removes if quantity becomes 0 or less"""
  1067. conn = self._get_connection()
  1068. cursor = conn.cursor()
  1069. try:
  1070. # Get current quantity
  1071. cursor.execute(
  1072. "SELECT quantity FROM sale_products WHERE sale_id = %s AND product_id = %s",
  1073. (sale_id, product_id)
  1074. )
  1075. result = cursor.fetchone()
  1076. if not result:
  1077. return False
  1078. current_quantity = result[0]
  1079. new_quantity = current_quantity - decrease_by
  1080. if new_quantity <= 0:
  1081. # Remove the product completely
  1082. cursor.execute(
  1083. "DELETE FROM sale_products WHERE sale_id = %s AND product_id = %s",
  1084. (sale_id, product_id)
  1085. )
  1086. logger.info(f"Product {product_id} removed from sale {sale_id} (quantity reached 0).")
  1087. else:
  1088. # Update with new quantity
  1089. cursor.execute(
  1090. "UPDATE sale_products SET quantity = %s WHERE sale_id = %s AND product_id = %s",
  1091. (new_quantity, sale_id, product_id)
  1092. )
  1093. logger.info(f"Product {product_id} quantity decreased to {new_quantity} in sale {sale_id}.")
  1094. conn.commit()
  1095. return True
  1096. except psycopg2.Error as e:
  1097. logger.error(f"Failed to decrease product quantity: {e}")
  1098. return False
  1099. finally:
  1100. conn.close()
  1101. #endregion
  1102. # Factory class to get service instances
  1103. class DataServiceFactory:
  1104. """Factory class to create data service instances"""
  1105. @staticmethod
  1106. def get_user_service() -> UserDataService:
  1107. """Get user data service instance"""
  1108. return UserDataService()
  1109. @staticmethod
  1110. def get_blacklist_service() -> BlacklistDataService:
  1111. """Get blacklist data service instance"""
  1112. return BlacklistDataService()
  1113. @staticmethod
  1114. def get_product_service() -> ProductDataService:
  1115. """Get product data service instance"""
  1116. return ProductDataService()
  1117. @staticmethod
  1118. def get_sales_service() -> SalesDataService:
  1119. """Get sales data service instance"""
  1120. return SalesDataService()
  1121. # Helper functions for background data
  1122. def load_bg_data() -> List[Dict[str, str]]:
  1123. """Load background data for AI assistant"""
  1124. try:
  1125. with open(BG_DATA_PATH, 'r', encoding='utf-8') as f:
  1126. return json.load(f)
  1127. except FileNotFoundError:
  1128. logger.error(f"Data file not found at {BG_DATA_PATH}. Serving with empty data.")
  1129. return []
  1130. except json.JSONDecodeError:
  1131. logger.error(f"Could not decode JSON from {BG_DATA_PATH}. Serving with empty data.")
  1132. return []
  1133. def initialize_db():
  1134. # En PostgreSQL, no se crea el archivo de base de datos, así que verificamos si las tablas existen.
  1135. try:
  1136. conn = psycopg2.connect(
  1137. dbname=POSTGRESQL_DB_CONFIG['dbname'],
  1138. user=POSTGRESQL_DB_CONFIG['user'],
  1139. password=POSTGRESQL_DB_CONFIG['password'],
  1140. host=POSTGRESQL_DB_CONFIG['host'],
  1141. port=POSTGRESQL_DB_CONFIG['port']
  1142. )
  1143. cursor = conn.cursor()
  1144. # Verificar si la tabla 'users' ya existe
  1145. cursor.execute("""
  1146. SELECT EXISTS (
  1147. SELECT FROM information_schema.tables
  1148. WHERE table_name = 'users'
  1149. );
  1150. """)
  1151. exists = cursor.fetchone()
  1152. if exists:
  1153. logger.info("La base de datos ya está inicializada, no se necesita inicializar.")
  1154. conn.close()
  1155. return
  1156. conn.close()
  1157. except Exception as e:
  1158. logger.error(f"Error comprobando la existencia de tablas: {e}")
  1159. # Si hay error, continuamos con la inicialización
  1160. conn = psycopg2.connect(
  1161. dbname=POSTGRESQL_DB_CONFIG['dbname'],
  1162. user=POSTGRESQL_DB_CONFIG['user'],
  1163. password=POSTGRESQL_DB_CONFIG['password'],
  1164. host=POSTGRESQL_DB_CONFIG['host'],
  1165. port=POSTGRESQL_DB_CONFIG['port']
  1166. )
  1167. cursor = conn.cursor()
  1168. # Crear tabla de usuarios
  1169. logger.info("Inicializando base de datos...")
  1170. logger.info("Creando tabla de usuarios...")
  1171. cursor.execute("""
  1172. CREATE TABLE IF NOT EXISTS users (
  1173. id SERIAL PRIMARY KEY,
  1174. email VARCHAR(255) UNIQUE NOT NULL,
  1175. name VARCHAR(255) NOT NULL,
  1176. rut VARCHAR(20) UNIQUE NOT NULL,
  1177. pin_hash TEXT NOT NULL,
  1178. kleincoins TEXT NOT NULL,
  1179. created_at TEXT NOT NULL,
  1180. permissions INTEGER DEFAULT 0 NOT NULL CHECK (permissions IN (0, 1, 2)) -- 0: Usuario normal, 1: Administrador, 2: Superusuario
  1181. );
  1182. """)
  1183. # Crear tabla de productos
  1184. logger.info("Creando tabla de products...")
  1185. cursor.execute("""
  1186. CREATE TABLE IF NOT EXISTS products (
  1187. id INTEGER PRIMARY KEY,
  1188. name VARCHAR(255) NOT NULL,
  1189. type VARCHAR(100) NOT NULL,
  1190. description TEXT NOT NULL,
  1191. price INTEGER NOT NULL,
  1192. image TEXT,
  1193. status INTEGER DEFAULT 1 NOT NULL CHECK (status IN (0, 1)) -- 0: Inactivo, 1: Activo
  1194. );
  1195. """)
  1196. # Crear tabla de ventas
  1197. logger.info("Creando tabla de sales...")
  1198. cursor.execute("""
  1199. CREATE TABLE IF NOT EXISTS sales (
  1200. id SERIAL PRIMARY KEY,
  1201. user_id INTEGER NOT NULL,
  1202. total INTEGER NOT NULL,
  1203. fudo_id TEXT UNIQUE NOT NULL,
  1204. date TEXT NOT NULL,
  1205. table_number INTEGER NOT NULL,
  1206. FOREIGN KEY (user_id) REFERENCES users(id)
  1207. );
  1208. """)
  1209. # Crear tabla intermedia para ventas y productos
  1210. logger.info("Creando tabla intermedia de sale_products...")
  1211. cursor.execute("""
  1212. CREATE TABLE IF NOT EXISTS sale_products (
  1213. sale_id INTEGER NOT NULL,
  1214. product_id INTEGER NOT NULL,
  1215. quantity INTEGER NOT NULL DEFAULT 1,
  1216. FOREIGN KEY (sale_id) REFERENCES sales(id),
  1217. FOREIGN KEY (product_id) REFERENCES products(id)
  1218. );
  1219. """)
  1220. # Crear tabla de blacklist
  1221. logger.info("Creando tabla de blacklist...")
  1222. cursor.execute("""
  1223. CREATE TABLE IF NOT EXISTS blacklist (
  1224. id SERIAL PRIMARY KEY,
  1225. user_id INTEGER NOT NULL,
  1226. FOREIGN KEY (user_id) REFERENCES users(id)
  1227. );
  1228. """)
  1229. logger.info("Todas las tablas creadas correctamente.")
  1230. logger.info("Cargando datos de productos desde el archivo JSON...")
  1231. products_json = json.loads(open(PRODUCT_DATA_PATH, 'r', encoding='utf-8').read())
  1232. product_service = ProductDataService()
  1233. product_service.load_data(products_json)
  1234. conn.commit()
  1235. conn.close()
  1236. logger.info("Base de datos inicializada correctamente.")
  1237. data_bg_loaded = load_bg_data()