class EventoRepository(EventRepositoryInterface): def __init__(self): self.logger = LoggerRepository('comando_repo') def pub_event(self, evento): try: eventos.put(evento) return True except Exception: return False def subscribe_event(self): while True: try: yield eventos.get(block=True, timeout=QUEUE_TIMEOUT) except Exception as ex: self.logger.error(ex) continue
class TareaRepository(TareaRepositoryInterface): def __init__(self): self.logger = LoggerRepository('tarea_repo') def next_tarea(self): while True: try: self.logger.debug("esperando por tarea") yield tareas.get(block=True, timeout=QUEUE_TIMEOUT) except Empty: pass def append(self, tarea: Tarea): try: tareas.put(tarea) except: self.logger.crit("Cola de tareas llena!!!!")
async def brain(thread_executor, id_repo, tarea_repo, comando_repo, humor_repo, accion_repo, evento_repo): logger = LoggerRepository('brain') try: loop = asyncio.get_event_loop() blocking_tasks = [ loop.run_in_executor(thread_executor, Sensor(humor_repo).listen_to_task_result, evento_repo), loop.run_in_executor(thread_executor, Ejecutor().listen_for_next_tarea, id_repo, tarea_repo, evento_repo, accion_repo), loop.run_in_executor(thread_executor, Comander().listen_to_command, comando_repo, tarea_repo, accion_repo, evento_repo) ] logger.debug("Preparados los threads") return blocking_tasks except asyncio.CancelledError: pass except Exception as mgr_ex: logger.error(f"Excecion en brain: {mgr_ex}")
class ComandoRepository(ComandoRepositoryInterface): def __init__(self): self.logger = LoggerRepository('comando_repo') def next_comando(self): while True: try: self.logger.debug("Esperamos por nuevo comando") c = comandos.get() self.logger.debug("Llego un comando") yield c except Empty: pass async def send_comando(self, comando): await comandos.aput(comando) self.logger.debug("Comando enviado")
def __init__(self): self.logger = LoggerRepository('comando_repo') self.comandos = get_queue(COMANDOS_QUEUE_NAME)
class ComandoRepository(ComandoRepositoryInterface): def __init__(self): self.logger = LoggerRepository('comando_repo') self.comandos = get_queue(COMANDOS_QUEUE_NAME) @staticmethod def serialize(comando: Comando) -> str: if isinstance(comando, ComandoNuevaTarea): return json.dumps({ "clase": "ComandoNuevaTarea", "idd": str(comando.idd), "tarea": TareaRepository.to_json(comando.tarea) }) if isinstance(comando, ComandoNuevaAccion): return json.dumps({ "clase": "ComandoNuevaAccion", "idd": str(comando.idd), "accion": AccionRepository.to_json(accion=comando.accion) }) @staticmethod def deserialize_comando_nueva_tarea(comando: dict) -> ComandoNuevaTarea: return ComandoNuevaTarea(idd=Idd(Idefier(), idd_str=comando['idd']), tarea=TareaRepository.deserialize( comando['tarea'])) @staticmethod def deserialize_comando_nueva_accion(comando: dict) -> ComandoNuevaAccion: return ComandoNuevaAccion(idd=Idd(Idefier(), idd_str=comando['idd']), accion=AccionRepository.deserialize( comando['accion'])) def next_comando(self): while True: self.logger.debug("Esperamos por nuevo comando") cmd = self.comandos.receive_messages( MaxNumberOfMessages=MAX_NUMBER_OF_MESSAGES, WaitTimeSeconds=POLL_TIME, AttributeNames=['MessageDeduplicationId', 'MessageGroupId']) for cc in cmd: c = json.loads(cc.body) cc.delete() if c['clase'] == "ComandoNuevaTarea": yield ComandoRepository.deserialize_comando_nueva_tarea(c) if c['clase'] == "ComandoNuevaAccion": yield ComandoRepository.deserialize_comando_nueva_accion(c) def send_comando(self, comando): try: self.logger.debug(f"Intentamos enviar el comando::: {comando}") response = self.comandos.send_message( MessageBody=ComandoRepository.serialize(comando), MessageGroupId=MESSAGE_GROUP_ID, MessageDeduplicationId=str(comando.idd)) self.logger.debug( f"Comando enviado: [{comando}], messageid: [{response['MessageId']}]" ) except Exception as ex: self.logger.error(f"Error intentando enviar un comando: {ex}")
from flask import Flask, request, jsonify from trasto.infrastructure.awsmultiprocess.accion_repository import \ AccionRepository from trasto.infrastructure.awsmultiprocess.comando_repository import \ ComandoRepository from trasto.infrastructure.memory.repositories import (EstadoDeHumorRepository, Idefier, LoggerRepository) from trasto.model.commands import ComandoNuevaAccion, ComandoNuevaTarea from trasto.model.entities import Accion, Tarea, TipoAccion from trasto.model.value_entities import Idd logger = LoggerRepository('web') accion_repo = AccionRepository() accion_repo.purge_table() app = Flask(__name__) @app.route('/', methods=[ 'GET', ]) def get_service(): logger.debug("Solicitada get_service") return { "service": "trastobrain", }
class AccionRepository(AccionRepositoryInterface): def __init__(self): self.logger = LoggerRepository('accion_repo') self.acciones = get_dynamodb_acciones_table() def purge_table(self): try: self.logger.debug(f"Eliminamos todos los elementos de la tabla") response = self.acciones.scan() except ClientError as ex: self.logger.error(f"Error in get_by_type: {ex}") return None else: for each in response['Items']: self.acciones.delete_item(Key={'idd': each['idd']}) @staticmethod def to_json(accion: Accion) -> dict: print(accion) return { "idd": str(accion.idd), "nombre": accion.nombre, "script_url": accion.script_url, "tipo": str(accion.tipo) } @staticmethod def serialize(accion: Accion) -> str: return json.dumps(AccionRepository.to_json(accion)) @staticmethod def deserialize(accion: dict) -> Accion: return Accion(**accion) def get_acciones_by_type(self, tipo: TipoAccion): try: self.logger.debug(f"Buscamos accion con el tipo: {tipo}") response = self.acciones.scan( FilterExpression=Key('tipo').eq(str(tipo))) except ClientError as ex: self.logger.error(f"Error in get_by_type: {ex}") return None else: for i in response['Items']: yield AccionRepository.deserialize(i) def get_all(self): return tuple( AccionRepository.deserialize(a) for a in self.acciones.scan()['Items']) def get_accion_by_id(self, idd: Idd): try: self.logger.debug(f"Buscamos accion con la idd: {idd}") response = self.acciones.get_item(Key={'idd': str(idd)}) except ClientError as ex: self.logger.error(f"Error in get_by_id: {ex}") return None else: if not 'Item' in response: raise AccionNotFoundError(idd) return AccionRepository.deserialize(response['Item']) def get_acciones_buen_humor(self): return (a for a in self.get_acciones_by_type( TipoAccion(TipoAccion.BUEN_HUMOR))) def get_acciones_mal_humor(self): return (a for a in self.get_acciones_by_type( TipoAccion(TipoAccion.MAL_HUMOR))) def del_accion(self, accion: Accion): self.acciones.delete_item(Key={"idd": str(accion.idd)}) def rollback_append_accion(self, accion: Accion): self.logger.debug("Rolling back append accion") self.del_accion(accion) def append_accion(self, accion: Accion, evento_repo: EventRepositoryInterface): try: self.logger.debug( f"Apending nueva accion: tabla: {self.acciones} item: {AccionRepository.to_json(accion)}" ) self.acciones.put_item(Item=AccionRepository.to_json(accion)) emitido = evento_repo.pub_event( NuevaAccionCreada(idd=Idd(idefier=Idefier()), accion_idd=accion.idd, accion_nombre=accion.nombre)) if not emitido: self.rollback_append_accion(accion=accion) except Exception as ex: self.logger.error(ex) self.rollback_append_accion(accion=accion) def get_all_json(self): return tuple( AccionRepository.to_json(accion) for accion in self.get_all())
class AccionRepository(AccionRepositoryInterface): def __init__(self): self.logger = LoggerRepository('accion_repo') self.acciones = list() def get_actiones_by_type(self, tipo: TipoAccion): for accion in self.acciones: if accion.tipo == tipo: yield accion def get_all(self): return tuple(a for a in self.acciones) def get_accion_by_id(self, idd: Idd): self.logger.debug(f"Buscamos accion con la idd: {idd}") for accion in self.acciones: self.logger.debug(f"Miramos si esta accion {accion} corresponde con id: {idd}") if accion.idd == idd: return accion raise AccionNotFoundError(f"idd={idd}") def get_acciones_buen_humor(self): return (a for a in self.get_acciones_by_type(TipoAccion(TipoAccion.BUEN_HUMOR))) def get_acciones_mal_humor(self): return (a for a in self.get_acciones_by_type(TipoAccion(TipoAccion.MAL_HUMOR))) def del_accion(self, accion: Accion): self.acciones.remove(accion) def rollback_append_accion(self, accion: Accion): self.logger.debug("Rolling back append accion") self.del_accion(accion) def append_accion(self, accion: Accion, evento_repo: EventRepositoryInterface): try: self.logger.debug("Apending nueva accion") self.acciones.append(accion) emitido = evento_repo.pub_event(accion) if not emitido: self.rollback_append_accion(accion=accion) except Exception as ex: self.logger.error(ex) self.rollback_append_accion(accion=accion) def to_json(self, accion: Accion): return json.dumps({ "idd": f"{accion.idd}", "nombre": accion.nombre, "script_url": accion.script_url, "tipo": f"{accion.tipo}" }) def from_json(self, json_str_accion: str): json_accion = json.loads(json_str_accion) return Accion( idd=Idd(idefier=Idefier(), idd_str=json_accion['idd']), nombre=json_accion['nombre'], script_url=json_accion['script_url'], tipo=json_accion['tipo'] ) def get_all_json(self): return tuple(json.loads(self.to_json(accion)) for accion in self.get_all())
def __init__(self, humor_repo: EstadoHumorRepositoryInterface): self.logger = LoggerRepository('sensor') self.humor_repo = humor_repo
class Sensor(SensorInterface): def __init__(self, humor_repo: EstadoHumorRepositoryInterface): self.logger = LoggerRepository('sensor') self.humor_repo = humor_repo def listen_to_task_result(self, evento_repo: EventRepositoryInterface): try: self.logger.debug("Escuchando a resultado de tarea") for evento in evento_repo.subscribe_event(): if isinstance(evento, AccionTerminada): self.logger.debug( f"Se ha terminado la tarea: {evento.tarea_idd}, resultado: {evento.resultado}" ) self.update_humor_from_task_result(evento.resultado, self.humor_repo, evento_repo) self.logger.debug("Escuchando por un resultado de tarea") except Exception as ex: self.logger.error(ex) traceback.print_exc() def update_humor_from_task_result( self, resultado: ResultadoAccion, humor_repo: EstadoHumorRepositoryInterface, evento_repo: EventRepositoryInterface): try: print(resultado) humor_repo.mejora() if resultado.is_good() else humor_repo.empeora( ) self.logger.debug("El humor ha cambiado a : {}".format( humor_repo.que_tal())) evento_repo.pub_event( EstadoHumorCambiado(idd=Idd(Idefier()), nuevo_estado_humor=humor_repo.que_tal())) except Exception as ex: self.logger.error(ex) traceback.print_exc()
class Comander(ComanderInterface): def __init__(self): self.logger = LoggerRepository('comander') def enqueue_task(self, tarea: Tarea, tarea_repo: TareaRepositoryInterface): self.logger.debug(f"Encolando tarea {tarea}") tarea_repo.append(tarea) def listen_to_command(self, repo_command: ComandoRepositoryInterface, tarea_repo: TareaRepositoryInterface, accion_repo: AccionRepositoryInterface, evento_repo: EventRepositoryInterface): self.logger.debug("Escuchando por nuevo comando") #while True: for cmd in repo_command.next_comando(): try: #cmd = repo_command.next_comando() if isinstance(cmd, ComandoNuevaTarea): self.logger.debug( "Recibido comando de tipo ComandoNuevaTarea") self.enqueue_task(cmd.tarea, tarea_repo) self.logger.debug("Escuchando por nuevo comando") continue if isinstance(cmd, ComandoNuevaAccion): self.logger.debug( "Recibido comando de tipo ComandoNuevaAccion") accion_repo.append_accion(accion=cmd.accion, evento_repo=evento_repo) self.logger.debug("Escuchando por nuevo comando") continue raise CommandNotImplemented(cmd) except Exception as ex: self.logger.error(ex) traceback.print_exc()
def __init__(self): self.logger = LoggerRepository('evento_repo') self.eventos = get_queue(EVENTOS_QUEUE_NAME)
class EventoRepository(EventRepositoryInterface): def __init__(self): self.logger = LoggerRepository('evento_repo') self.eventos = get_queue(EVENTOS_QUEUE_NAME) @staticmethod def to_json(evento: Evento): if isinstance(evento, AccionTerminada): return { "clase": "AccionTerminada", "idd": str(evento.idd), "tarea_idd": str(evento.tarea_idd), "resultado": { "codigo": str(evento.resultado.codigo), "msg": evento.resultado.msg } } if isinstance(evento, NuevaAccionCreada): return { "clase": "NuevaAccionCreada", "idd": str(evento.idd), "accion_idd": str(evento.accion_idd), "accion_nombre": str(evento.accion_nombre) } if isinstance(evento, EstadoHumorCambiado): return { "clase": "EstadoHumorCambiado", "idd": str(evento.idd), "nuevo_estado_humor": evento.nuevo_estado_humor } raise EventoNotImplemented(evento) @staticmethod def from_json(evento: dict): if evento['clase'] == 'AccionTerminada': evento.pop('clase') return AccionTerminada(**evento) if evento['clase'] == 'EstadoHumorCambiado': evento.pop('clase') return EstadoHumorCambiado(**evento) if evento['clase'] == 'NuevaAccionCreada': evento.pop('clase') return NuevaAccionCreada(**evento) @staticmethod def serialize(evento: dict): return json.dumps(EventoRepository.to_json(evento)) @staticmethod def deserialize(evento: str): return EventoRepository.from_json(json.loads(evento)) def pub_event(self, evento: Evento): try: evs = EventoRepository.serialize(evento) self.logger.debug(f"Intentado enviar el evento: {evs}") self.eventos.send_message(MessageBody=evs, MessageGroupId=MESSAGE_GROUP_ID, MessageDeduplicationId=str(evento.idd)) self.logger.debug("Evento enviado") return True except EventoNotImplemented as exx: raise exx except Exception as ex: self.logger.error(f"Sending event: {ex}") return False def subscribe_event(self): while True: try: self.logger.debug("Esperando por eventos") msg = self.eventos.receive_messages( MaxNumberOfMessages=MAX_NUMBER_OF_MESSAGES, WaitTimeSeconds=POLL_TIME, AttributeNames=[ 'MessageDeduplicationId', 'MessageGroupId' ]) #self.logger.debug(f"Han llegado eventos: {msg}") for cc in msg: if cc is None: break c = json.loads(cc.body) cc.delete() yield EventoRepository.from_json(c) except Exception as ex: self.logger.error(ex) continue
def __init__(self): self.logger = LoggerRepository('accion_repo') self.acciones = get_dynamodb_acciones_table()
from trasto.infrastructure.awsmultiprocess.evento_repository import \ EventoRepository from trasto.infrastructure.memory.repositories import Idefier, LoggerRepository from trasto.model.events import (AccionTerminada, EstadoHumorCambiado, NuevaAccionCreada) from trasto.model.value_entities import Idd, ResultadoAccion, CodigoResultado logger = LoggerRepository('test_evento_respository') def test_send_evento_accion_terminada(): evento_repo = EventoRepository() evento = AccionTerminada(idd=Idd(Idefier()), tarea_idd=Idd(Idefier()), resultado=ResultadoAccion( codigo=CodigoResultado(codigo="BIEN"), msg="Mensaje de prueba")) assert evento_repo.pub_event(evento=evento) def test_evento_accion_terminada(): evento_repo = EventoRepository() evento = AccionTerminada( idd=Idd(Idefier()), tarea_idd=Idd(Idefier()), resultado=ResultadoAccion( codigo=CodigoResultado(codigo=CodigoResultado.BUEN_RESULTADO), msg="Mensaje de prueba"))
class Ejecutor(EjecutorInterface): def __init__(self): self.logger = LoggerRepository('ejecutor') def listen_for_next_tarea(self, id_repo: IdefierInterface, tarea_repo: TareaRepositoryInterface, evento_repo: EventRepositoryInterface, accion_repo: AccionRepositoryInterface): try: self.logger.debug("Escuchando por nueva tarea") for tarea in tarea_repo.next_tarea(): self.ejecuta_tarea(tarea=tarea, id_repo=id_repo, evento_repo=evento_repo, accion_repo=accion_repo) self.logger.debug("Escuchando por nueva tarea") except Exception as ex: self.logger.error(ex) traceback.print_exc() def ejecuta_tarea(self, tarea: Tarea, id_repo: IdefierInterface, evento_repo: EventRepositoryInterface, accion_repo: AccionRepositoryInterface): evento = None resultado = None try: accionid = tarea.accionid print(f"ha llegado el accion id: {accionid}") idd = Idd(idefier=id_repo, idd_str=accionid) self.logger.debug( f"Intentamos ejecutar ---- accionid: {idd}, repo: {accion_repo}" ) accion = accion_repo.get_accion_by_id(idd) self.logger.debug(f"Ejecutamos: {accion} (dormimos 10s)") #TODO implementar realmente la ejecucion, ahora solo hay un ejemplo time.sleep(10) self.logger.debug("despertamos, tarea ejecutada") if int(tarea.nombre) > 0: resultado = ResultadoAccion(codigo=CodigoResultado( codigo=CodigoResultado.BUEN_RESULTADO), msg="la tarea ha ido bien") else: resultado = ResultadoAccion(codigo=CodigoResultado( codigo=CodigoResultado.MAL_RESULTADO), msg="la tarea ha ido mal") evento = AccionTerminada(idd=Idd(idefier=Idefier()), tarea_idd=tarea.idd, resultado=resultado) evento_repo.pub_event(evento=evento) except AccionNotFoundError as exx: self.logger.error(f"No se ha encontrado la Accion {exx}") resultado = ResultadoAccion( codigo=CodigoResultado(codigo=CodigoResultado.MAL_RESULTADO), msg=f"No existe la tarea {exx}") evento = AccionTerminada(idd=Idd(idefier=Idefier()), tarea_idd=tarea.idd, resultado=resultado) evento_repo.pub_event(evento=evento) except Exception as ekk: self.logger.error(f"Error no identificado: {ekk}")
def __init__(self): self.logger = LoggerRepository('ejecutor')
def __init__(self): self.logger = LoggerRepository('tarea_repo')
class TareaRepository(TareaRepositoryInterface): def __init__(self): self.logger = LoggerRepository('tarea_repo') self.tareas_prio = get_queue(TAREAS_PRIORITARIAS_QUEUE_NAME) self.tareas_norm = get_queue(TAREAS_NORMALES_QUEUE_NAME) def purge_queue(self): get_sqs_client().purge_queue(QueueUrl=self.tareas_norm.url) get_sqs_client().purge_queue(QueueUrl=self.tareas_prio.url) @staticmethod def to_json(tarea: Tarea) -> dict: return { "idd": str(tarea.idd), "accionid": str(tarea.accionid), "parametros": tarea.parametros, "nombre": tarea.nombre, "prioridad": tarea.prioridad } @staticmethod def serialize(tarea: Tarea) -> str: return json.dumps(TareaRepository.to_json(tarea)) @staticmethod def deserialize(tarea: dict) -> Tarea: return Tarea(**tarea) def _next_tarea_alta(self): return self.tareas_prio.receive_messages( MaxNumberOfMessages=MAX_NUMBER_OF_MESSAGES, WaitTimeSeconds=POLL_TIME, AttributeNames=['MessageDeduplicationId', 'MessageGroupId']) def _next_tarea_baja(self): return self.tareas_norm.receive_messages( MaxNumberOfMessages=MAX_NUMBER_OF_MESSAGES, WaitTimeSeconds=POLL_TIME, AttributeNames=['MessageDeduplicationId', 'MessageGroupId']) def next_tarea(self): while True: self.logger.debug("Esperamos por nueva tarea") msg_alta = self._next_tarea_alta() if msg_alta.count: #self.logger.debug("hay tareas alta 1") for cc_alta in msg_alta: cc_alta.delete() #self.logger.debug("Devolvemos tarea alta 1") yield TareaRepository.deserialize(json.loads(cc_alta.body)) msg_baja = self._next_tarea_baja() if msg_baja.count: #self.logger.debug("Hay tareas baja") for cc_baja in msg_baja: msg_alta = self._next_tarea_alta() if msg_alta.count: #self.logger.debug("Hay rtarea alta 2") for cc_alta in msg_alta: cc_alta.delete() #self.logger.debug("Devolvemos tarea alta 2") yield TareaRepository.deserialize( json.loads(cc_alta.body)) cc_baja.delete() #self.logger.debug("Devolvemos tarea baja") yield TareaRepository.deserialize(json.loads(cc_baja.body)) def append(self, tarea: Tarea): cola = self.tareas_prio if tarea.prioridad == Prioridad.ALTA else self.tareas_norm cola.send_message(MessageBody=TareaRepository.serialize(tarea), MessageGroupId=MESSAGE_GROUP_ID, MessageDeduplicationId=str(tarea.idd)) self.logger.debug("Tarea enviada")
def __init__(self): self.logger = LoggerRepository('comando_repo')
def __init__(self): self.logger = LoggerRepository('tarea_repo') self.tareas_prio = get_queue(TAREAS_PRIORITARIAS_QUEUE_NAME) self.tareas_norm = get_queue(TAREAS_NORMALES_QUEUE_NAME)
def __init__(self): self.logger = LoggerRepository('accion_repo') self.acciones = list()
class Comander(ComanderInterface): def __init__(self): self.logger = LoggerRepository('comander') def enqueue_task(self, tarea: Tarea, tarea_repo: TareaRepositoryInterface): self.logger.debug(f"Encolando tarea {tarea}") tarea_repo.append(tarea) def listen_to_command(self, repo_command: ComandoRepositoryInterface, tarea_repo: TareaRepositoryInterface, accion_repo: AccionRepositoryInterface, evento_repo: EventRepositoryInterface): self.logger.debug("Escuchando por nuevo comando") #while True: for cmd in repo_command.next_comando(): try: #cmd = repo_command.next_comando() if isinstance(cmd, ComandoNuevaTarea): self.logger.debug( "Recibido comando de tipo ComandoNuevaTarea") self.enqueue_task(cmd.tarea, tarea_repo) self.logger.debug("Escuchando por nuevo comando") continue if isinstance(cmd, ComandoNuevaAccion): #TODO la persistencia de la Acción debe ser llevada a cabo por el Aggregate Accion, que se ejecuta en el # service Ejecutor. En el modo monoproceso no es tan importante, en el modo multiproceso (para garantizar # la separación de bases de datos entre microservicios) si es relevante cambiar la responsabilidad self.logger.debug( "Recibido comando de tipo ComandoNuevaAccion") accion_repo.append_accion(accion=cmd.accion, evento_repo=evento_repo) self.logger.debug("Escuchando por nuevo comando") continue raise CommandNotImplemented(cmd) except Exception as ex: self.logger.error(ex) traceback.print_exc()