api
¶
JSON API for the dashboard frontend. Currently just the tag tree (the actual sidebar is
server-rendered from the same data, see routes.pages; this endpoint exists for any future
client-side refresh/polling use), a liveness/db-change ping, and the table/plot data endpoints.
Every handler does its database and plot-building work on a worker thread (never on the event
loop, so a slow request can't stall other endpoints when this app is embedded in a larger one),
each thread with its own read-only RecordStore, and caches its response in
request.app.state.response_cache. The .db file can be regenerated out from under a running
viewer process (e.g. someone re-runs trendify generate/run); /ping detects that via the
file's mtime and clears the cache, so this is a cache invalidation concern rather than something
that makes the cache unsafe to use.
Requests tagged X-Trendify-Hydrate (the frontend's background-prefetch scheduler, not a real
click -- see _is_hydration_request) run on request.app.state.hydration_runner's thread
instead of request.app.state.runner's, so a slow prefetch can never queue ahead of a real click
(see viewer.app.create_app's docstring and viewer.hydration.HydrationRunner).
TableResponse
¶
PlotResponse
¶
ping
async
¶
Cheap liveness check the frontend polls to show a connected/disconnected indicator.
Also reports the .db file's mtime, so the client can detect that someone regenerated it
(e.g. re-ran trendify generate/run while this server is still up) and prompt a refresh.
The underlying RecordStore connection itself stays valid across a regeneration (the
generate pipeline writes into the same file/inode via WAL, it doesn't replace it); only
this process's response cache goes stale, so an mtime change clears it here.
Source code in src/trendify/viewer/routes/api.py
get_table
async
¶
get_table(
tag: str, view: TableView, request: Request
) -> TableResponse
Table data for the table viewer's Melted/Pivot/Statistics tabs, as DataTables-ready
{available, columns, rows} JSON.
tag is the tag's encode_tag (JSON) form, the same string the sidebar's tojson-
rendered tag-selected payload already produces client-side, since a tuple tag has no
single unambiguous plain-string/path encoding to put directly in a URL path segment.
pivot/stats report available: false (not a 500) rather than raising when the pivot
fails (a repeated (row, col) pair) or there are no numeric pivoted columns: both are
normal, expected shapes for some tags' data, not error conditions.
Source code in src/trendify/viewer/routes/api.py
get_plot
async
¶
get_plot(
tag: str,
request: Request,
line_mode: LineMode = LINES_AND_MARKERS,
interp: InterpMode = LINEAR,
hover: HoverMode = CLOSEST,
show_spike: bool = False,
max_points: int | None = Query(
None, gt=0, le=_MAX_POINTS_LIMIT
),
) -> PlotResponse
Plotly figure JSON ({available, data, layout}) for the plot viewer, built the same way as
the static Plotly/matplotlib renderers (generator.render._render_tag_assets): every
PlottableData2D record sharing tag is added to one shared PlotlyFigure, then that
tag's Format2D (if any) is applied.
line_mode/interp/hover/show_spike/max_points are the dashboard's view-only
PlotConfig settings (viewer.plot_config), applied as a post-process pass over the
already-built figure JSON rather than baked into PlotlyFigure/the record classes
themselves -- they're display overrides the viewer controls, independent of whatever
style a record's own Pen/Marker authored. AxLine renders as layout shapes
(add_hline/add_vline), not data traces, so it's naturally unaffected by the
trace-level overrides below.
Source code in src/trendify/viewer/routes/api.py
| Python | |
|---|---|
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | |