''' $Author: murco $ $Id: jroproc_base.py 1 2012-11-12 18:56:07Z murco $ ''' import inspect from fuzzywuzzy import process def checkKwargs(method, kwargs): currentKwargs = kwargs choices = inspect.getargspec(method).args try: choices.remove('self') except Exception as e: pass try: choices.remove('dataOut') except Exception as e: pass for kwarg in kwargs: fuzz = process.extractOne(kwarg, choices) if fuzz is None: continue if fuzz[1] < 100: raise Exception('\x1b[0;32;40mDid you mean {} instead of {} in {}? \x1b[0m'. format(fuzz[0], kwarg, method.__self__.__class__.__name__)) class ProcessingUnit(object): """ Esta es la clase base para el procesamiento de datos. Contiene el metodo "call" para llamar operaciones. Las operaciones pueden ser: - Metodos internos (callMethod) - Objetos del tipo Operation (callObject). Antes de ser llamados, estos objetos tienen que ser agreagados con el metodo "add". """ # objeto de datos de entrada (Voltage, Spectra o Correlation) dataIn = None dataInList = [] # objeto de datos de entrada (Voltage, Spectra o Correlation) dataOut = None operations2RunDict = None isConfig = False def __init__(self, *args, **kwargs): self.dataIn = None self.dataInList = [] self.dataOut = None self.operations2RunDict = {} self.operationKwargs = {} self.isConfig = False self.args = args self.kwargs = kwargs if not hasattr(self, 'name'): self.name = self.__class__.__name__ checkKwargs(self.run, kwargs) def getAllowedArgs(self): if hasattr(self, '__attrs__'): return self.__attrs__ else: return inspect.getargspec(self.run).args def addOperationKwargs(self, objId, **kwargs): ''' ''' self.operationKwargs[objId] = kwargs def addOperation(self, opObj, objId): """ Agrega un objeto del tipo "Operation" (opObj) a la lista de objetos "self.objectList" y retorna el identificador asociado a este objeto. Input: object : objeto de la clase "Operation" Return: objId : identificador del objeto, necesario para ejecutar la operacion """ self.operations2RunDict[objId] = opObj return objId def getOperationObj(self, objId): if objId not in list(self.operations2RunDict.keys()): return None return self.operations2RunDict[objId] def operation(self, **kwargs): """ Operacion directa sobre la data (dataOut.data). Es necesario actualizar los valores de los atributos del objeto dataOut Input: **kwargs : Diccionario de argumentos de la funcion a ejecutar """ raise NotImplementedError def callMethod(self, name, opId): """ Ejecuta el metodo con el nombre "name" y con argumentos **kwargs de la propia clase. Input: name : nombre del metodo a ejecutar **kwargs : diccionario con los nombres y valores de la funcion a ejecutar. """ #Checking the inputs if name == 'run': if not self.checkInputs(): self.dataOut.flagNoData = True return False else: #Si no es un metodo RUN la entrada es la misma dataOut (interna) if self.dataOut is not None and self.dataOut.isEmpty(): return False #Getting the pointer to method methodToCall = getattr(self, name) #Executing the self method if hasattr(self, 'mp'): if name=='run': if self.mp is False: self.mp = True self.start() else: self.operationKwargs[opId]['parent'] = self.kwargs methodToCall(**self.operationKwargs[opId]) else: if name=='run': methodToCall(**self.kwargs) else: methodToCall(**self.operationKwargs[opId]) if self.dataOut is None: return False if self.dataOut.isEmpty(): return False return True def callObject(self, objId): """ Ejecuta la operacion asociada al identificador del objeto "objId" Input: objId : identificador del objeto a ejecutar **kwargs : diccionario con los nombres y valores de la funcion a ejecutar. Return: None """ if self.dataOut is not None and self.dataOut.isEmpty(): return False externalProcObj = self.operations2RunDict[objId] if hasattr(externalProcObj, 'mp'): if externalProcObj.mp is False: externalProcObj.kwargs['parent'] = self.kwargs self.operationKwargs[objId] = externalProcObj.kwargs externalProcObj.mp = True externalProcObj.start() else: externalProcObj.run(self.dataOut, **externalProcObj.kwargs) self.operationKwargs[objId] = externalProcObj.kwargs return True def call(self, opType, opName=None, opId=None): """ Return True si ejecuta la operacion interna nombrada "opName" o la operacion externa identificada con el id "opId"; con los argumentos "**kwargs". False si la operacion no se ha ejecutado. Input: opType : Puede ser "self" o "external" Depende del tipo de operacion para llamar a:callMethod or callObject: 1. If opType = "self": Llama a un metodo propio de esta clase: name_method = getattr(self, name) name_method(**kwargs) 2. If opType = "other" o"external": Llama al metodo "run()" de una instancia de la clase "Operation" o de un derivado de ella: instanceName = self.operationList[opId] instanceName.run(**kwargs) opName : Si la operacion es interna (opType = 'self'), entonces el "opName" sera usada para llamar a un metodo interno de la clase Processing opId : Si la operacion es externa (opType = 'other' o 'external), entonces el "opId" sera usada para llamar al metodo "run" de la clase Operation registrada anteriormente con ese Id Exception: Este objeto de tipo Operation debe de haber sido agregado antes con el metodo: "addOperation" e identificado con el valor "opId" = el id de la operacion. De lo contrario retornara un error del tipo ValueError """ if opType == 'self': if not opName: raise ValueError("opName parameter should be defined") sts = self.callMethod(opName, opId) elif opType == 'other' or opType == 'external' or opType == 'plotter': if not opId: raise ValueError("opId parameter should be defined") if opId not in list(self.operations2RunDict.keys()): raise ValueError("Any operation with id=%s has been added" %str(opId)) sts = self.callObject(opId) else: raise ValueError("opType should be 'self', 'external' or 'plotter'; and not '%s'" %opType) return sts def setInput(self, dataIn): self.dataIn = dataIn self.dataInList.append(dataIn) def getOutputObj(self): return self.dataOut def checkInputs(self): for thisDataIn in self.dataInList: if thisDataIn.isEmpty(): return False return True def setup(self): raise NotImplementedError def run(self): raise NotImplementedError def close(self): #Close every thread, queue or any other object here is it is neccesary. return class Operation(object): """ Clase base para definir las operaciones adicionales que se pueden agregar a la clase ProcessingUnit y necesiten acumular informacion previa de los datos a procesar. De preferencia usar un buffer de acumulacion dentro de esta clase Ejemplo: Integraciones coherentes, necesita la informacion previa de los n perfiles anteriores (bufffer) """ __buffer = None isConfig = False def __init__(self, **kwargs): self.__buffer = None self.isConfig = False self.kwargs = kwargs if not hasattr(self, 'name'): self.name = self.__class__.__name__ checkKwargs(self.run, kwargs) def getAllowedArgs(self): if hasattr(self, '__attrs__'): return self.__attrs__ else: return inspect.getargspec(self.run).args def setup(self): self.isConfig = True raise NotImplementedError def run(self, dataIn, **kwargs): """ Realiza las operaciones necesarias sobre la dataIn.data y actualiza los atributos del objeto dataIn. Input: dataIn : objeto del tipo JROData Return: None Affected: __buffer : buffer de recepcion de datos. """ if not self.isConfig: self.setup(**kwargs) raise NotImplementedError def close(self): pass