- name
- time-series-analytics-dev
- description
- Develop the Time Series Analytics microservice itself (FastAPI + Kapacitor) — build and deploy it locally via Docker Compose or Helm, run the mocked unit test suite (tests/run_tests.sh) and the slower Docker/Helm end-to-end functional suite (tests-functional/), navigate and modify src/main.py (routes), src/classifier_startup.py (Kapacitor/UDF lifecycle), and src/opcua_alerts.py, and follow this service's release conventions (CHANGELOG.md, image-tag bump locations, Dockerfile build args). Use when modifying, testing, debugging, or releasing this service's own code. Not for merely deploying the prebuilt image to build a new UDF-based use case on top of it — that is time-series-analytics-user.
# Time Series Analytics — Dev
Work on the service's source. **This skill assumes a repo clone** of
`edge-ai-libraries` with this microservice at
`microservices/time-series-analytics/`; if there is no clone, clone the
repo first.
Run all commands from the microservice root.
## When to Use
- Add, modify, or remove a REST route in `src/main.py`
- Change Kapacitor/UDF lifecycle behavior in `src/classifier_startup.py`
- Run or extend the unit test suite, or the Docker/Helm functional suite
- Build the image from source, debug a running container, or tune GPU/core-pinning behavior
- Cut a release: bump the version consistently across the files that track it
## Example Prompts
Sample problem-solving scenarios this skill handles end-to-end:
| Example | Problem it solves |
|---|---|
| [add-udf-list-endpoint.md](./example-prompts/add-udf-list-endpoint.md) | Add a new REST route with test coverage |
| [debug-udf-not-starting.md](./example-prompts/debug-udf-not-starting.md) | Diagnose a deployed UDF that silently isn't processing data |
## Reference Lookup
| File | Load when… |
|---|---|
| [`references/source-map.md`](./references/source-map.md) | locating where a route, config field, or lifecycle step lives before editing |
| [`references/testing.md`](./references/testing.md) | writing new tests, running a subset, or avoiding the import-time Kapacitor-startup trap |
| [`references/build-and-deploy.md`](./references/build-and-deploy.md) | building the image, GPU/core-pinning setup, Helm deployment |
| [`references/release-conventions.md`](./references/release-conventions.md) | bumping the version, updating CHANGELOG.md, touching Dockerfile build args |
## The one gotcha to know first
`src/main.py` imports `classifier_startup` at module load, and importing
*that* for real starts an actual Kapacitor daemon subprocess. Any test that
imports `main` must mock `classifier_startup` in `sys.modules` **before**
the import — `tests/test_main.py` already does this; reuse its pattern
rather than re-importing `main` fresh in a new test module. Details:
[`references/testing.md`](./references/testing.md).
## Environment setup
```bash
python3 -m venv env && source env/bin/activate
pip install -r requirements.txt -r tests/requirements.txt
```
## Test / verify loop
```bash
./tests/run_tests.sh # full unit suite + coverage (see references/testing.md)
PYTHONPATH=./src pytest tests -k <name> -v # fast iteration on one test
```
Functional (slow — builds the image / stands up Helm):
```bash
cd tests-functional && pip install -r requirements.txt
pytest -q -vv --self-contained-html --html=./test_report/report.html .
```
## Source map (summary)
- `src/main.py` (~900 lines) — every route: ingestion, config, UDF package
upload/validation, OPC UA alerts. The module-level `config` dict is the
single source of truth; `POST /config` is the only writer at runtime.
- `src/classifier_startup.py` (~550 lines) — Kapacitor daemon lifecycle:
rewrites `kapacitor.conf`'s `[udf.functions.*]`/`[[mqtt]]` sections from
`config`, validates the extracted UDF package's files exist, starts
`kapacitord` as a subprocess, enables the Kapacitor task via its CLI.
- `src/opcua_alerts.py` (~210 lines) — `asyncua`-based OPC UA client used by
the `/opcua_alerts` route.
- Full annotated map: [`references/source-map.md`](./references/source-map.md).
## Build & deploy from source
```bash
cd docker && docker compose build && docker compose up -d
```
GPU driver setup, CPU core-pinning (`CORE_PINNING` env var), Helm chart
values, and the `/dev/dri`-mount gotcha on GPU-less hosts:
[`references/build-and-deploy.md`](./references/build-and-deploy.md).
## Debug a running instance
1. `docker logs -f ia-time-series-analytics-microservice` — startup,
Kapacitor task enable/retry, request logs.
2. `curl -sf http://localhost:5000/health` (503 = Kapacitor daemon not
running, not just "process not ready").
3. Kapacitor-internal errors aren't in the container's top-level log:
`docker exec -it ia-time-series-analytics-microservice bash` then
`cat /tmp/log/kapacitor/kapacitor.log | grep -i error`.
## Contribution gotchas
| Gotcha | Consequence |
|---|---|
| `classifier_startup` starts a real Kapacitor daemon on import | tests must mock it in `sys.modules` before importing `main` (see above) |
| The three UDF names (`config.json`'s `udfs.name`, `.py` filename, `.tick` filename, tick script's `@name()` node) must be identical | a mismatch fails silently at the pipeline level, not loudly — worth checking first when a "deployment succeeded but nothing happens" bug report comes in |
| `kapacitord` runs as a subprocess *inside this same container*, not a sidecar | don't assume container-to-container networking semantics when tracing a startup failure |
| Compose unconditionally mounts `/dev/dri` | fails container startup on hosts with no Intel iGPU — see `references/build-and-deploy.md` |
| A version bump touches `docker/.env`, `helm/values.yaml`, and `README-dockerhub.md` together | see `references/release-conventions.md` — don't bump only one |
| Every new source/config/doc file needs the SPDX header | matches the existing files' `Apache v2 license` / `Copyright (C) 2026 Intel Corporation` / `SPDX-License-Identifier: Apache-2.0` block |
View on GitHub