# KPI Calculation

Each machine that is part of the MAT project records various data related to production, its health status, and its operation. Besides retrieving this data, it can be useful to aggregate and perform operations on it to extract valuable insights. A KPI (Key Performance Indicator) is a quantifiable measure used to evaluate a certain aspect. Some examples of KPIs include performance, quality, availability, and OEE.

## /apis/kpis/\<assetId>

## GET Method&#x20;

When a machine is configured within a project, formulas are defined to calculate the various reference KPIs, such as availability, quality, quantity produced, downtime, and more. For each of these KPIs, it is also specified which fields can be used for data aggregation.

To discover the KPIs available for a specific machine, the aggregators that can be used to calculate a KPI, and its unit of measurement, you can use this endpoint:

<details>

<summary>Request</summary>

```
HTTP
GET https://{BASE_URL}/apis/kpis/<assetId>
```

where:&#x20;

* `assetId` : ID of the sub-machine to consider.

#### Query

```
JSON
{
    "subAssetId": "subAssetId"
}
```

where:&#x20;

* `subAssetId` : ID of the sub-machine to consider. This field can be omitted if the asset being considered is not a multi-machine system.

</details>

By making a GET request, you can retrieve the list of all KPIs available for the specified machine. The endpoint allows you to specify in the query parameters the ID of the sub-machine to which these KPIs should refer. If the project is not multi-machine, then the "subAssetId" parameter can be ignored.

The information retrieved through this endpoint can be used for POST requests to the same endpoint, as it is necessary to specify one of the KPIs returned by the GET request.

### Example

<details>

<summary>Request</summary>

```
HTTP
GET https://matdev.40mat.com/apis/kpis/40-line?subAssetId=M1
```

#### Query

```
JSON
{
    "subAssetId": "M1"
}
```

</details>

In this example, the endpoint is called for the machine with ID "40-line" and the sub-machine with ID "M1". The response will include a list of all the KPIs available for the "40-line" machine and its sub-machine "M1".

An example of the response is shown below. As you can see, for each available KPI, the information on the available aggregators and the unit of measurement for the KPI, if present, is provided. The keys within the response and the values associated with the "id" key in each individual object represent the ID of each KPI and can be used in the POST method invocation of the endpoint, with the "kpiName" key in the body, to identify the value you want to calculate. In the example considered, valid values for "kpiName" include: "availability", "avgProdAct", "avgProdSet", "cons0", "cons1", "downTime"...

<details>

<summary>Response</summary>

```json
JSON
{
    "availability": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "availability",
        "measurement_unit": "%"
    },
    "avgProdAct": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "avgProdAct",
        "measurement_unit": null
    },
    "avgProdSet": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "avgProdSet",
        "measurement_unit": null
    },
    "cons0": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "cons0",
        "measurement_unit": "m³"
    },
    "cons1": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "cons1",
        "measurement_unit": "kWh"
    },
    "downTime": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "downTime",
        "measurement_unit": "sec"
    },
    "idealCount": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "idealCount",
        "measurement_unit": null
    },
    "idleTime": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "idleTime",
        "measurement_unit": "sec"
    },
    "mtbf": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "mtbf",
        "measurement_unit": "sec"
    },
    "mttr": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "mttr",
        "measurement_unit": "sec"
    },
    "oee": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "oee",
        "measurement_unit": "%"
    },
    "oeeGlobal": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "oeeGlobal",
        "measurement_unit": "%"
    },
    "performance": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "performance",
        "measurement_unit": "%"
    },
    "prodTime": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "prodTime",
        "measurement_unit": "sec"
    },
    "stopCount": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "stopCount",
        "measurement_unit": "-"
    },
    "totCount": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "totCount",
        "measurement_unit": null
    },
    "totGood": {
        "available_aggregators": [
            "aggr0",
            "aggr1",
            "aggr2"
        ],
        "id": "totGood",
        "measurement_unit": null
    }
}
```

</details>

### Code

{% tabs %}
{% tab title="Python" %}

```python
import requests
import json

# Example data for the header
headers = {
    'App-Name': 'matdev-be',
    'Authorization': 'Bearer ey....xy1w'
}

# URL of the third-party API
myDomain = 'matdev.40mat.com'
myAssetId = '40-line'
endpoint = f'/apis/kpis/{myAssetId}'
url = f'https://{myDomain}{endpoint}'


# Make the POST request
response = requests.get(url, headers=headers, )

# Check the response status and return the data
if response.status_code == 200:
    print('Success:')
    print(json.dumps(response.json(), indent=4))
else:
    print('Request failed:')
    print(f'Status code: {response.status_code}')
    print(response.text)

```

{% endtab %}
{% endtabs %}

### OpenAPI

{% openapi src="broken-reference" path="/apis/kpis/{assetId}" method="get" %}
[Broken file](https://40factory.gitbook.io/mat-api-en/endpoints-description/broken-reference)
{% endopenapi %}

## POST Method

This method allows requesting the calculation of various KPIs that are typically presented through the graphical interface in MAT.

<details>

<summary>Request</summary>

```
HTTP
POST https://{BASE_URL}/apis/kpis/<assetId>
```

where:&#x20;

* `assetId` : id of the sub-machine to be considered.

#### Query

```
JSON
{
    "subAssetId": "subAssetId"
}
```

where:&#x20;

* `subAssetId` : id of the sub-machine to be considered. This field can be omitted if the asset in question is not a multi-machine system.

#### Body

```
JSON
{
    "kpiName": "oee",
    "from": "2024-03-31T22:00:00.000Z",
    "to": "2024-04-05T21:59:59.999Z",
    "resampleBy": "1d",
    "filterBy": {"colName": "colValue"},
    "groupBy":"colName",
    "limit": 3,
    "calendar": 0
}
```

The request body allows specifying several parameters that influence the calculation of the requested KPI:

* `kpiName` : this parameter contains the name of the KPI to be calculated. The list of available KPIs can be retrieved by calling the corresponding endpoint. [/apis/kpis/\<assetId> con metodo GET](#metodo-get). Note that this parameter can also be a list of strings, where each string represents the name of a KPI to be calculated;
* `from` : initial timestamp from which the data calculation should start, expressed in UTC and following the format `"YYYY-MM-DDThh:mm:ss.sssZ"` (ISO 8601 Standard);
* `to` : final timestamp of the interval within which the data should be calculated, also expressed in UTC in the `"YYYY-MM-DDThh:mm:ss.sssZ"` format (ISO 8601 Standard). If this field is omitted or set to `null`, it will automatically default to the current time of the request;
* `resampleBy` : this parameter allows you to define the frequency at which the specified KPI should be calculated. Since data in the MAT ecosystem is time-based, it can either be returned over a time range or as a time series. This field specifies how the data should be returned. The accepted values are:
  * unique: indicates that a single numeric value should be returned for the entire interval;
  * {n}m: calculates the KPI every `{n}` minutes over the selected interval;
  * {n}h: calculates the KPI every `{n}` hours over the selected interval;
  * {n}d: calculates the KPI every `{n}` days over the selected interval;
  * {n}w: calculates the KPI every `{n}` weeks over the selected interval;&#x20;
* `filterBy` : this field allows you to apply filters to the data. The value must be a dictionary where the keys represent the fields to filter by, and the values specify the criteria for those fields;
* `groupBy` : this field allows you to group the data by one of the specified values. It should be a string indicating the field to group by. For more advanced usage, a list of strings can be provided;
* `limit` : this numeric field allows you to specify the maximum number of data rows to return. If you think of the data as written in tables, the "limit" operation retrieves the last `{n}` rows from the table. In particular, it refers to the most recent rows recorded. If the `resampleBy` field is present, `limit` will be ignored. This field can be used in several ways:
  * "limit" field without "from" and "to": retrieves the last `{n}` rows recorded or, more generally, the last `{n}` data points available;
  * "limit" field with "from"and without "to": behaves the same as above—returns the most recent `{n}` data points available;
  * "limit" field with"from" and "to": returns the last `{n}` rows of data, considering the `to` field as the end of the interval. The data returned will be the most recent values written before the `to` timestamp;
  * "limit" field with "to" and without "from": this configuration is not allowed and will result in an error;
* `calendar` : boolean value indicating whether the calendar should be considered in data analysis. By default, this value is set to false.

</details>

Below are some usage examples.

## Example- resampleBy = 1h

<details>

<summary>Request</summary>

```
HTTP
POST https://matdev.40mat.com/apis/kpis/40-line
```

#### Query

```
JSON
{
    "subAssetId": "M1"
}
```

#### Body

```
JSON
{
    "kpiName": "totCount",
    "resampleBy" : "1h",
    "from": "2024-05-01T22:00:00.000Z",
    "to": "2024-05-02T21:59:00.000Z"
}
```

</details>

In this example, the goal is to retrieve the machine's production data (`totCount`) for the day of May 2nd, 2024. Specifically, the objective is to obtain the number of units produced during each hour of that day.\
To make this request, it is necessary to specify the identifier of the KPI to be calculated, along with the timestamps that define the desired interval.\
Additionally, the `resampleBy` value must be set to the temporal aggregation of interest—in this case, 1 hour (`"1h"`).

Below is the response. You can observe that, for each hour of the day, a value for the `totCount` KPI has been calculated, which refers to the production data of the corresponding hour.

<details>

<summary>Response</summary>

```json
JSON
{
    "measurementUnit": "units",
    "data": [
        {
            "_time": "2024-05-02T00:00:00.000",
            "totCount": 640.0
        },
        {
            "_time": "2024-05-02T01:00:00.000",
            "totCount": 552.0
        },
        {
            "_time": "2024-05-02T02:00:00.000",
            "totCount": 650.0
        },
        {
            "_time": "2024-05-02T03:00:00.000",
            "totCount": 198.0
        },
        {
            "_time": "2024-05-02T04:00:00.000",
            "totCount": 651.0
        },
        {
            "_time": "2024-05-02T05:00:00.000",
            "totCount": 497.0
        },
        {
            "_time": "2024-05-02T06:00:00.000",
            "totCount": 653.0
        },
        {
            "_time": "2024-05-02T07:00:00.000",
            "totCount": 649.0
        },
        {
            "_time": "2024-05-02T08:00:00.000",
            "totCount": 197.0
        },
        {
            "_time": "2024-05-02T09:00:00.000",
            "totCount": 622.0
        },
        {
            "_time": "2024-05-02T10:00:00.000",
            "totCount": 644.0
        },
        {
            "_time": "2024-05-02T11:00:00.000",
            "totCount": 556.0
        },
        {
            "_time": "2024-05-02T12:00:00.000",
            "totCount": 650.0
        },
        {
            "_time": "2024-05-02T13:00:00.000",
            "totCount": 651.0
        },
        {
            "_time": "2024-05-02T14:00:00.000",
            "totCount": 652.0
        },
        {
            "_time": "2024-05-02T15:00:00.000",
            "totCount": 649.0
        },
        {
            "_time": "2024-05-02T16:00:00.000",
            "totCount": 519.0
        },
        {
            "_time": "2024-05-02T17:00:00.000",
            "totCount": 379.0
        },
        {
            "_time": "2024-05-02T18:00:00.000",
            "totCount": 638.0
        },
        {
            "_time": "2024-05-02T19:00:00.000",
            "totCount": 329.0
        },
        {
            "_time": "2024-05-02T20:00:00.000",
            "totCount": 377.0
        },
        {
            "_time": "2024-05-02T21:00:00.000",
            "totCount": 241.0
        },
        {
            "_time": "2024-05-02T22:00:00.000",
            "totCount": 598.0
        },
        {
            "_time": "2024-05-02T23:00:00.000",
            "totCount": 334.0
        }
    ]
}
```

</details>

## Example- resampleBy = "unique"

<details>

<summary>Request</summary>

```
HTTP
POST https://matdev.40mat.com/apis/kpis/40-line
```

#### Query

```
JSON
{
    "subAssetId": "M1"
}
```

#### Body

```
JSON
{
    "kpiName": "totCount",
    "resampleBy" : "unique",
    "from": "2024-05-01T22:00:00.000Z",
    "to": "2024-05-02T21:59:00.000Z"
}
```

</details>

In this example, the goal is to retrieve the machine’s production data (`totCount`) for the day of May 2nd, 2024. Specifically, the objective is to obtain a single value representing the total number of units produced throughout the entire day.\
To achieve this, the temporal resampling must be set to a single value, so the `"resampleBy"` key should be set to `"unique"`.

Below is the response.

<details>

<summary>Response</summary>

```json
JSON
{
    "measurementUnit": "units",
    "data": {
        "totCount": 12526.0
    }
}
```

</details>

## Example - groupBy

<details>

<summary>Request</summary>

```
HTTP
POST https://matdev.40mat.com/apis/kpis/40-line
```

#### Query

```
JSON
{
    "subAssetId": "M1"
}
```

#### Body

```
JSON
{
    "kpiName": "totCount",
    "resampleBy" : "unique",
    "from": "2024-05-01T22:00:00.000Z",
    "to": "2024-05-03T21:59:00.000Z",
    "groupBy": "aggr1"
}
```

</details>

In this example, the goal is to calculate how much each individual operator (`"aggr1"`) produced on May 2nd, 2024.\
To differentiate the production data based on the active operator, it is necessary to use the **group by** operation.\
Within the `"groupBy"` key, you can specify the field by which you want to group the data—in this case, the operator field (`"aggr1"`).

The response shows the KPI value calculated for each operator.

<details>

<summary>Response</summary>

```json
JSON
{
    "measurementUnit": "units",
    "data": {
        "Luigi": [
            {
                "_time": null,
                "totCount": 2041.0
            }
        ],
        "Mario": [
            {
                "_time": null,
                "totCount": 1568.0
            }
        ],
        "Peach": [
            {
                "_time": null,
                "totCount": 2515.0
            }
        ],
        "Waluigi": [
            {
                "_time": null,
                "totCount": 1754.0
            }
        ],
        "Wario": [
            {
                "_time": null,
                "totCount": 2173.0
            }
        ],
        "Yoshi": [
            {
                "_time": null,
                "totCount": 2475.0
            }
        ]
    }
}
```

</details>

## Example - filterBy

<details>

<summary>Request</summary>

```
HTTP
POST https://matdev.40mat.com/apis/kpis/40-line
```

#### Query

```
JSON
{
    "subAssetId": "M1"
}
```

#### Body

```
JSON
{
    "kpiName": "totCount",
    "resampleBy" : "unique",
    "from": "2024-05-01T22:00:00.000Z",
    "to": "2024-05-03T21:59:00.000Z",
    "filterBy": {"aggr0": "prod1"}
}
```

</details>

The objective of this example is to calculate how many units of product "prod1" ("aggr0") were produced by the machine on the specified day. To do so, it is necessary to filter the production data by summing all production that occurred when the aggregator "aggr0" was set to "prod1". By using "resampleBy" set to "unique", we obtain a single value for the KPI.

Below is the result of the call:

<details>

<summary>Response</summary>

```json
JSON
{
    "measurementUnit": "units",
    "data": {
        "totCount": 6156.0
    }
}
```

</details>

## Example - groupBy, filterBy

<details>

<summary>Request</summary>

```
HTTP
POST https://matdev.40mat.com/apis/kpis/40-line
```

#### Query

```
JSON
{
    "subAssetId": "M1"
}
```

#### Body

```
JSON
{
    "kpiName": "totCount",
    "resampleBy" : "unique",
    "from": "2024-05-01T22:00:00.000Z",
    "to": "2024-05-03T21:59:00.000Z",
    "filterBy": {"aggr0": "prod1"},
    "groupBy": "aggr1"
}
```

</details>

The objective of this example is to calculate how many units of product "prod1" were produced by the various operators during the specified day. To do so, it is requested to apply a filter on the field "aggr0," which should only be considered if equal to "prod1," and to perform a grouping by operator ("aggr1") so that the data related to each individual operator is displayed.

Below is the response. As requested, for each operator, the amount they produced is indicated. The filter ensured that only the production of "prod1" was considered.

<details>

<summary>Response</summary>

```json
JSON
{
    "measurementUnit": "units",
    "data": {
        "Luigi": [
            {
                "_time": null,
                "totCount": 685.0
            }
        ],
        "Mario": [
            {
                "_time": null,
                "totCount": 1179.0
            }
        ],
        "Peach": [
            {
                "_time": null,
                "totCount": 398.0
            }
        ],
        "Waluigi": [
            {
                "_time": null,
                "totCount": 1431.0
            }
        ],
        "Wario": [
            {
                "_time": null,
                "totCount": 1925.0
            }
        ],
        "Yoshi": [
            {
                "_time": null,
                "totCount": 538.0
            }
        ]
    }
}
```

</details>

## Example - limit

<details>

<summary>Request</summary>

```
HTTP
POST https://matdev.40mat.com/apis/kpis/40-line
```

#### Query

```
JSON
{
    "subAssetId": "M1"
}
```

#### Body

```
JSON
{
    "kpiName": "stopCount",
    "from": "2024-05-01T22:00:00.000Z",
    "to": "2024-05-03T21:59:00.000Z",
    "limit":1
}
```

</details>

In this example, the goal is to retrieve the last value taken by the KPI "stopCount" within the specified interval. In the response, we will obtain the last value recorded before the specified moment within the "to" field.

<details>

<summary>Response</summary>

```json
JSON
{
    "measurementUnit": null,
    "data": [
        {
            "_time": "2024-05-03T23:55:52.882",
            "stopCount": 0.0
        }
    ]
}
```

</details>

Attention: The timestamps specified in the request are in UTC format, while those in the response are in the machine's timezone. This means that in this example, the requested time interval is from 2024-05-02 00:00:00.000 to 2024-05-03 23:59:59.999, and the last recorded data was at 2024-05-03 at 23:55:52.882, which is approximately four minutes before the end of the chosen interval.

<details>

<summary>Request</summary>

```
HTTP
POST https://matdev.40mat.com/apis/kpis/40-line
```

#### Query

```
JSON
{
    "subAssetId": "M1"
}
```

#### Body

```
JSON
{
    "kpiName": "stopCount",
    "from": "2024-05-01T22:00:00.000Z",
    "limit":1
}
```

</details>

This second example aims to show the difference when a "to" field is specified versus when it is omitted. If no end timestamp is specified, the system assumes the current moment as the end of the interval, returning the last recorded data. As of the time this documentation is being written, it is 09:37:00.000 on 2024-05-13, and the last recorded data was at 09:34:49.044, as can be seen from the response.

<details>

<summary>Response</summary>

```json
JSON
{
    "measurementUnit": null,
    "data": [
        {
            "_time": "2024-05-13T09:34:49.044",
            "stopCount": 1.0
        }
    ]
}
```

</details>

The exact same result would have been obtained with a call where neither the "from" field nor the "to" field is specified.

<details>

<summary>Request</summary>

```
HTTP
POST https://matdev.40mat.com/apis/kpis/40-line
```

#### Query

```
JSON
{
    "subAssetId": "M1"
}
```

#### Body

```
JSON
{
    "kpiName": "stopCount",
    "limit":1
}
```

</details>

The "limit" field allows returning more than a single result.

<details>

<summary>Request</summary>

```
HTTP
POST https://matdev.40mat.com/apis/kpis/40-line
```

#### Query

```
JSON
{
    "subAssetId": "M1"
}
```

#### Body

```
JSON
{
    "kpiName": "stopCount",
    "limit": 4
}
```

</details>

The response will return the last four recorded values for the specified KPI.

<details>

<summary>Response</summary>

```json
JSON
{
    "measurementUnit": null,
    "data": [
        {
            "_time": "2024-05-13T09:39:49.215",
            "stopCount": 0.0
        },
        {
            "_time": "2024-05-13T09:34:49.044",
            "stopCount": 0.0
        },
        {
            "_time": "2024-05-13T09:29:48.877",
            "stopCount": 0.0
        },
        {
            "_time": "2024-05-13T09:24:48.708",
            "stopCount": 0.0
        }
    ]
}
```

</details>

## Example - kpiName as List

<details>

<summary>Request</summary>

```
HTTP
POST https://matdev.40mat.com/apis/kpis/40-line
```

#### Query

```
JSON
{
    "subAssetId": "M1"
}
```

#### Body

```
JSON
{
    "kpiName": ["totCount", "prodTime"],
    "resampleBy" : "1h",
    "from": "2024-05-01T22:00:00.000Z",
    "to": "2024-05-02T21:59:00.000Z"
}
```

</details>

The goal of this request is to obtain the values for both the "totCount" KPI and the "prodTime" KPI within the same call.

The response will be in the following format:

<details>

<summary>Response</summary>

```json
{
    "data": [
        {
            "_time": "2025-03-01T00:00:00.000",
            "prodTime": 3600.26171875,
            "totCount": 651.0
        },
        {
            "_time": "2025-03-02T00:00:00.000",
            "prodTime": 76823.71875,
            "totCount": 13879.0
        },
        {
            "_time": "2025-03-03T00:00:00.000",
            "prodTime": 79187.7890625,
            "totCount": 14310.0
        },
        {
            "_time": "2025-03-04T00:00:00.000",
            "prodTime": 71278.21875,
            "totCount": 12884.0
        },
        {
            "_time": "2025-03-05T00:00:00.000",
            "prodTime": 72628.234375,
            "totCount": 13122.0
        },
        {
            "_time": "2025-03-06T00:00:00.000",
            "prodTime": 76829.703125,
            "totCount": 13869.0
        },
        {
            "_time": "2025-03-07T00:00:00.000",
            "prodTime": 75673.25,
            "totCount": 13681.0
        }
    ]
}
```

</details>

### Code

{% tabs %}
{% tab title="Python" %}
The following script can be used to invoke a call to the /apis/kpis endpoint with the POST method. Make sure to correctly configure the parameters in the body of the request.

```python
import requests
import json

# Example data for the header
headers = {
    'App-Name': 'matdev-be',
    'Authorization': 'Bearer ey....xy1w'
}

# Example data for the body
body = {
    "kpiName": "totCount",
    "resampleBy" : "unique",
    "from": "2024-05-01T22:00:00.000Z",
    "to": "2024-05-30T21:59:00.000Z",
    "filterBy": {"aggr0": "prod1"},
    "groupBy": "aggr1",
    "limit" : None
}

# Example data for the query string
query_params = {
    "subAssetId": "M1"
}

# URL of the third-party API
myDomain = 'matdev.40mat.com'
myAssetId = '40-line'
endpoint = f'/apis/kpis/{myAssetId}'
url = f'https://{myDomain}{endpoint}'

# Make the POST request
response = requests.post(url, headers=headers, json=body, params=query_params )

# Check the response status and return the data
if response.status_code == 200:
    print('Success:')
    print(json.dumps(response.json(), indent=4))
else:
    print('Request failed:')
    print(f'Status code: {response.status_code}')
    print(response.text)
```

{% endtab %}
{% endtabs %}

### OpenAPI

{% openapi src="broken-reference" path="/apis/kpis/{assetId}" method="post" %}
[Broken file](https://40factory.gitbook.io/mat-api-en/endpoints-description/broken-reference)
{% endopenapi %}
