fudo.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import math
  2. from time import time
  3. import requests
  4. from config.settings import DEVELOPMENT
  5. import os
  6. import redis
  7. from logging import getLogger
  8. logger = getLogger(__name__)
  9. api_token = os.getenv('FUDO_API_KEY')
  10. api_secret = os.getenv('FUDO_API_SECRET')
  11. token = None
  12. token_exp = None
  13. # Configuración de Redis
  14. redis_client = redis.Redis(
  15. host=os.getenv('REDIS_HOST', 'localhost'),
  16. port=int(os.getenv('REDIS_PORT', 6379)),
  17. db=1 if DEVELOPMENT else 0,
  18. decode_responses=True
  19. )
  20. REDIS_TOKEN_KEY = 'fudo_api_token'
  21. def get_token():
  22. """
  23. Obtiene el token de autenticación de Fudo API.
  24. Primero verifica si existe un token válido en Redis.
  25. Si no existe o ha expirado, solicita uno nuevo y lo guarda en Redis con expiración automática.
  26. """
  27. global token, token_exp
  28. # Intento de obtener el token desde la RAM
  29. if token and token_exp and time() < token_exp:
  30. logger.info("Token obtenido desde variable global")
  31. return token
  32. try:
  33. # Intentar obtener el token desde Redis
  34. cached_token = redis_client.get(REDIS_TOKEN_KEY)
  35. if cached_token:
  36. logger.info("Token obtenido desde Redis cache")
  37. token = cached_token
  38. ttl = redis_client.ttl(REDIS_TOKEN_KEY)
  39. if ttl is None or int(str(ttl)) < 0:
  40. token_exp = None
  41. else:
  42. token_exp = int(str(ttl)) + int(time())
  43. return str(cached_token)
  44. except Exception as e:
  45. logger.error(f"Error al conectar con Redis: {e}")
  46. logger.info("Fallback: obteniendo token sin cache")
  47. # Si no hay token en cache, solicitar uno nuevo
  48. url = 'https://auth.fu.do/api'
  49. data = {
  50. "apiKey": api_token,
  51. "apiSecret": api_secret
  52. }
  53. r = requests.post(url, data=data)
  54. response_data = r.json()
  55. token = response_data['token']
  56. expiration_timestamp = response_data['exp']
  57. # Calcular TTL en segundos para Redis
  58. current_time = int(time())
  59. ttl_seconds = expiration_timestamp - current_time
  60. try:
  61. # Guardar el token en Redis con expiración automática
  62. if ttl_seconds > 0:
  63. redis_client.setex(REDIS_TOKEN_KEY, ttl_seconds, token)
  64. logger.info(f"Token nuevo guardado en Redis (expira en {ttl_seconds} segundos)")
  65. else:
  66. logger.warning("Warning: El token ya está expirado")
  67. except Exception as e:
  68. logger.error(f"Error al guardar en Redis: {e}")
  69. return token
  70. def get_categories():
  71. token = get_token()
  72. url = 'https://api.fu.do/v1alpha1/product-categories'
  73. headers = {
  74. 'Authorization': 'Bearer ' + token
  75. }
  76. r = requests.get(url, headers=headers)
  77. return r.json()
  78. def get_product(id_category:int):
  79. url = 'https://api.fu.do/v1alpha1/products/{}'.format(id_category)
  80. token = get_token()
  81. headers = {
  82. 'Authorization': 'Bearer ' + token
  83. }
  84. r = requests.get(url, headers=headers)
  85. if r.status_code != 200:
  86. logger.error(f"Error al obtener producto: {r.json()['errors']}")
  87. return r.json()
  88. def get_products():
  89. url = 'https://api.fu.do/v1alpha1/products'
  90. token = get_token()
  91. headers = {
  92. 'Authorization': 'Bearer ' + token
  93. }
  94. r = requests.get(url, headers=headers)
  95. return list(filter(lambda x: x['relationships']['productCategory']['data']['id'] == '1', r.json()['data']))
  96. def get_table(number:int):
  97. n_per_page = 10
  98. page = math.ceil(number / n_per_page)
  99. url = 'https://api.fu.do/v1alpha1/tables?page[number]={}&page[size]={}&include=activeSales&sort=number'.format(page, n_per_page)
  100. token = get_token()
  101. headers = {
  102. 'Authorization': 'Bearer ' + token
  103. }
  104. r = requests.get(url, headers=headers)
  105. if r.status_code != 200:
  106. logger.error('Error al obtener tablas:' + str(r.json()['errors']))
  107. return None
  108. try:
  109. return list(filter(lambda x: x['attributes']['number'] == number, r.json()['data']))[0]
  110. except:
  111. logger.error('Error al obtener tabla')
  112. logger.error(r.json())
  113. return None
  114. def get_sale(sale_id:int):
  115. url = 'https://api.fu.do/v1alpha1/sales/{}'.format(sale_id)
  116. token = get_token()
  117. headers = {
  118. 'Authorization': 'Bearer ' + token
  119. }
  120. r = requests.get(url, headers=headers)
  121. if r.status_code != 200:
  122. logger.error('Error al obtener tablas:' + str(r.json()['errors']))
  123. return None
  124. return r.json()
  125. def create_sale(table_id:int):
  126. url = 'https://api.fu.do/v1alpha1/sales'
  127. token = get_token()
  128. headers = {
  129. 'Authorization': 'Bearer ' + token
  130. }
  131. data = {
  132. "data": {
  133. "type": "Sale",
  134. "attributes": {
  135. "people": 1,
  136. "saleType": "EAT-IN",
  137. "comment": "Pedido desde la app pedidos express"
  138. },
  139. "relationships": {
  140. "table": {
  141. "data": {
  142. "id": str(table_id),
  143. "type": "Table"
  144. }
  145. },
  146. "waiter": {
  147. "data": {
  148. "type": "User",
  149. "id": "76"
  150. }
  151. }
  152. }
  153. }
  154. }
  155. r = requests.post(url, headers=headers, json=data)
  156. if r.status_code != 201:
  157. logger.error('Error al crear la venta:', r.json())
  158. return None
  159. return r.json()["data"]
  160. def create_item(product_id:int, quantity:int, sale_id:int, comment = None):
  161. url = 'https://api.fu.do/v1alpha1/items'
  162. token = get_token()
  163. headers = {
  164. 'Authorization': 'Bearer ' + token
  165. }
  166. data = {
  167. "quantity": quantity,
  168. "origin": "MOBILE",
  169. "comment": "Pedido desde pedidos express" + (f" - {comment}" if comment else ""),
  170. }
  171. data = {
  172. "data":{
  173. "type": "Item",
  174. "attributes": data,
  175. "relationships": {
  176. "product": {
  177. "data": {
  178. "type": "Product",
  179. "id": str(product_id)
  180. }
  181. },
  182. "sale": {
  183. "data": {
  184. "type": "Sale",
  185. "id": str(sale_id)
  186. }
  187. }
  188. },
  189. }
  190. }
  191. r = requests.post(url, headers=headers, json=data)
  192. if r.status_code != 201:
  193. logger.error(r.json())
  194. return None
  195. return r.json()["data"]
  196. def get_active_sale(table):
  197. data = table['relationships']['activeSales']['data']
  198. if len(data) == 0:
  199. return None
  200. return data[0]
  201. def clear_token():
  202. """
  203. Elimina el token cached de Redis.
  204. Útil cuando el token es inválido o se necesita forzar una renovación.
  205. """
  206. try:
  207. redis_client.delete(REDIS_TOKEN_KEY)
  208. logger.info("Token eliminado del cache")
  209. except Exception as e:
  210. logger.error(f"Error al eliminar token de Redis: {e}")
  211. if __name__ == "__main__":
  212. table = get_table(107)
  213. if table is None:
  214. logger.error('No se pudo obtener la mesa')
  215. exit()
  216. activeSale = get_active_sale(table)
  217. if not activeSale:
  218. logger.error('No hay una venta activa para la mesa')
  219. activeSale = create_sale(table['id'])
  220. if activeSale is None:
  221. logger.error('No se pudo crear la venta')
  222. exit()
  223. else:
  224. activeSale = activeSale[0]
  225. logger.info('Venta activa: %s', activeSale['id'])
  226. """
  227. Instrucciones para hacer un pedido:
  228. 1. Obtener el token de autenticación con `get_token()` (ahora usa Redis cache).
  229. 2. Obtener la mesa con `get_table(numero_de_mesa)`.
  230. 3. Ver si tiene una activeSale, en caso contrario crear una con `create_sale(id_mesa)`.
  231. 4. Agregar los items a la venta con `create_item(id_producto, cantidad, id_venta, comentario)`.
  232. Configuración de Redis:
  233. - Host: REDIS_HOST (default: localhost)
  234. - Puerto: REDIS_PORT (default: 6379)
  235. - Base de datos: REDIS_DB (default: 0)
  236. """