# Python Microsoft Fabric - Reading recent events

Query the last few minutes of events in an eventhouse, on demand from a service or every minute on a schedule.

This page queries the events of the last 15 minutes in an eventhouse, from a service on demand and from the scheduler every minute. An eventhouse stores the events an eventstream receives so they can be queried, and the eventhouse on this page is `Operations Events`.

This is polling. To run a service as soon as an event arrives, use a Kafka channel as described in [receiving events](https://zato.io/docs/dev/examples/cloud/fabric/receiving-events.html).

> **What you need from your Fabric admin**
>
> The eventhouse's Query URI, from the eventhouse's overview page, of the form `https://<cluster>.kusto.fabric.microsoft.com`. The name of the KQL database in it, here `Operations Events`, and the name of the table the eventstream writes to, here `Events`. The app registration of your Fabric connection must be allowed to read the database.

## The KQL query {#the-kql-query}

KQL is the query language of eventhouses. The query below counts the events of one type from the last 15 minutes, per location, and this is what the Fabric KQL editor shows for it:

```text
Events
| where event_type == 'appointment_cancelled'
| where occurred_at > ago(15m)
| summarize cancelled = count() by location
```

## The connection {#the-connection}

The eventhouse accepts queries over HTTPS at `/v1/rest/query` on its Query URI, and the token it takes comes from a Bearer token definition. Under `Security → Bearer tokens`:

| Field | Value |
| --- | --- |
| Name | `Fabric Eventhouse Token` |
| Username | The application (client) ID of your app registration |
| Password | Its client secret |
| Auth endpoint | `https://login.microsoftonline.com/<tenant ID>/oauth2/v2.0/token` |
| Scopes | `https://kusto.kusto.windows.net/.default` |

Then, under `Connections → REST → Outgoing connections`:

| Field | Value |
| --- | --- |
| Name | `Operations Events` |
| Host | The Query URI, e.g. `https://<cluster>.kusto.fabric.microsoft.com` |
| URL path | `/v1/rest/query` |
| Data format | JSON |
| Security | `Fabric Eventhouse Token` |

In enmasse YAML:

```yaml
security:
  - name: Fabric Eventhouse Token
    type: bearer_token
    username: <application (client) ID>
    password: Zato_Enmasse_Env.FabricEventhouseSecret
    auth_endpoint: https://login.microsoftonline.com/<tenant ID>/oauth2/v2.0/token
    scopes: https://kusto.kusto.windows.net/.default

outgoing_rest:
  - name: Operations Events
    host: https://<cluster>.kusto.fabric.microsoft.com
    url_path: /v1/rest/query
    data_format: json
    security: Fabric Eventhouse Token
```

## The service {#the-service}

The request body names the database and carries the query. The reply is a list of tables, the first of which holds the result as a list of column descriptions and a list of rows, and the service zips the two into dicts.

```python
# -*- coding: utf-8 -*-

# Zato
from zato.common.typing_ import anydict, dictlist
from zato.server.service import Service

def to_rows(reply:'anydict') -> 'dictlist':
    """ Turns the eventhouse's reply into a list of dicts keyed by column name.
    """

    # The result is the first table of the reply ..
    tables = reply['Tables']
    result = tables[0]

    # .. its column names are in one list ..
    column_names = []
    for column in result['Columns']:
        column_names.append(column['ColumnName'])

    # .. and the rows in another.
    out = []
    for values in result['Rows']:
        pairs = zip(column_names, values)
        row = dict(pairs)
        out.append(row)

    return out

class RecentCancellations(Service):

    name = 'events.recent-cancellations'

    def handle(self):

        # The query and the database it runs against ..
        query = """
        Events
        | where event_type == 'appointment_cancelled'
        | where occurred_at > ago(15m)
        | summarize cancelled = count() by location
        """

        request = {
            'db': 'Operations Events',
            'csl': query,
        }

        # .. posted to the eventhouse ..
        conn = self.rest['Operations Events']
        response = conn.post(self.cid, request)

        # .. and its reply turned into rows.
        rows = to_rows(response.data)

        self.response.payload = {'rows': rows}
```

Invoked on demand, the response has the same rows the KQL editor showed:

```json
{
  "rows": [
    {"location": "Maple Grove", "cancelled": 1}
  ]
}
```

## Every minute {#every-minute}

To run the same query every minute, open the `Operations Events` connection in the Dashboard and fill in the Scheduler, Request and Callback tabs:

| Tab | Field | Value |
| --- | --- | --- |
| Scheduler | Run every | `1` minutes |
| Scheduler | Start time | Now |
| Request | Method | `POST` |
| Request | Body | `{"db": "Operations Events", "csl": "Events \\| where event_type == 'appointment_cancelled' \\| where occurred_at > ago(15m) \\| summarize cancelled = count() by location"}` |
| Callback | Deliver to | Service |
| Callback | Service | `events.on-recent-cancellations` |

Every minute, the body is posted and the reply arrives in the callback service as its request:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class OnRecentCancellations(Service):

    name = 'events.on-recent-cancellations'

    def handle(self):

        # The eventhouse's reply, delivered by the connection.
        rows = to_rows(self.request.payload)

        for row in rows:
            cancelled = row['cancelled']
            location = row['location']
            self.logger.info(f'Cancelled in the last 15 minutes -> {cancelled} at {location}')
```

`to_rows` is the same function as above.

## See also {#see-also}

- [Receiving events as they happen](https://zato.io/docs/dev/examples/cloud/fabric/receiving-events.html) - Running a service as soon as an event arrives
- [Sending events as they happen](https://zato.io/docs/dev/examples/cloud/fabric/sending-events.html) - Sending the events that the eventhouse stores
- [Events](https://zato.io/docs/dev/examples/cloud/fabric/api/events.html) - The KQL request and response shape in detail

## Learn more {#learn-more}

- [Development documentation](https://zato.io/docs/dev/) - Everything about writing services, in one place
- [Requests and responses](https://zato.io/docs/dev/request-response/) - What a service receives, what it returns and how to shape both
- [Integration examples](https://zato.io/docs/dev/examples/) - Ready-made code for the systems you are likely to connect to
- [IDE and debugging](https://zato.io/docs/dev/ide/) - Write services in the Dashboard or in your own editor
- [Data models](https://zato.io/docs/dev/model/) - Declare inputs and outputs and have them validated for you
- [In-depth API tutorial](https://zato.io/tutorials/main/01.html) - The full platform tutorial, from installation to production patterns
