2

I'm designing a program in Python to handle the calculation of key performance indicators (KPIs) for an industrial application. The results are calculated from raw process data, which in the program is stored in a NumPy matrix.

I wanted to keep the actual calculation procedures separate from the main program, which handles the other heavy lifting, like fetching the relevant raw data. There are multiple real-life "units" for which the KPIs can be calculated, so each of them has their own Python file containing the specific implementations of the KPIs. A for-loop in the main program module calculates all the KPIs for all the units in succession.

So what I do is as follows:

1) Import the calculation file for the unit under calculation this round:

print("Importing calculation module '{}.py'...".format(file_name))
try:
    module = __import__(file_name)
except ImportError:
    status = "Error: no plant file defined."
else:
    print("Done.")

2) Pass the imported module to a function, which determines, which KPI function to call in the module:

def CalculateKPI(KPIToCalculate, raw_data, kpi_module):

    status = ""
    kpi_result = []

    not_defined = "Error: Calculation method not defined for this KPI."

    print("Calculating KPI values... ")

    if(KPIToCalculate == "SupportFuelUsage"):
        try:
            kpi_result, status = kpi_module.SupportFuelUsage(raw_data)
        except AttributeError:
            status = not_defined
    elif(KPIToCalculate == "Lambda"):
        try:
            kpi_result, status = kpi_module.Lambda(raw_data)
        except AttributeError:
            status = not_defined
    elif(KPIToCalculate == "PrimaryAirRatio"):
        try:
            kpi_result, status = kpi_module.PrimaryAirRatio(raw_data)
        except AttributeError:
            status = not_defined

    ... As many ELIF forks as there are possible KPIs, close to 200

    else:
        status = "Error: Function CalculateKPI does not contain if-statement for this KPI."
        return kpi_result, status

    print("Done.")

    return kpi_result, status

It does work. However, this seems like a very bulky and crude way of doing this, and in the long term will likely be a nightmare to manage.

So basically, what I'm asking is: is there any way of determining the function to call in the module based on the variable "KPIToCalculate" alone, and not have to list each possible function separately?

Julien
  • 13,986
  • 5
  • 29
  • 53
Veriticus
  • 31
  • 3
  • You could define a function that determines the best match between method names inside `kpi_module` and the value of `KPIToCalculate` and execute that method. – sobek Sep 12 '18 at 06:53
  • 1
    Even the question has been closed, you may be better assigning an `id` to each `kpi` and defining the methods as `def kpi_id(raw_data, *args, **kwargs)`. Then in the main program you can access these functions as you need. – Jorge Lavín Sep 12 '18 at 06:53
  • I think OP is asking something different then the above 'duplicate answer'; seems to want to know if there's a cleaner way to build an interface to a module that provides a variety of different functionality that may or may not be present. – Joshua R. Sep 12 '18 at 06:55
  • I might move `CalculateKPI` into your module and return the appropriate function (which takes raw_data as an argument), if available, if not- return `None`. – Joshua R. Sep 12 '18 at 06:57
  • 1
    Welcome to Stackoverflow. Deceze's duplicate can probably help you since your functions seem to be named same as possible values of `KPIToCalculate`. Another option would be to create a dictionary that has the possible String values as keys and the function to call as values. Then you could call the function for all Strings by doing `funcDict[KPIToCalculate](raw_data)` – Bernhard Sep 12 '18 at 06:58
  • You can use [getattr](https://docs.python.org/3/library/functions.html#getattr). For your case it could look like this: import kpi_module kpi_func = getattr(kpi_module, KPIToCalculate) try: kpi_result, status = kpi_func(raw_data) except AttributeError: status = not_defined – halloleo Sep 12 '18 at 07:00
  • Like in the duplicate answer, the getattr() method seems to work. Maybe I should have tried wording my problem differently to find that answer immediately. Thanks all. – Veriticus Sep 12 '18 at 07:21

0 Answers0