From 3078b18cfe427f5e26064c6c6691c1da0f0f9e34 Mon Sep 17 00:00:00 2001 From: zaiun xu Date: Wed, 8 Apr 2026 20:02:05 +0800 Subject: [PATCH] feat(scripts): add biomass_poller with httpx and loguru Poll GET /api/v1/biomass/real/camera/ and /health/result/; add loguru to fish_api dev deps. Made-with: Cursor --- fish_api/pyproject.toml | 2 +- scripts/biomass_poller.py | 92 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100755 scripts/biomass_poller.py diff --git a/fish_api/pyproject.toml b/fish_api/pyproject.toml index f029ec3..dad87b2 100644 --- a/fish_api/pyproject.toml +++ b/fish_api/pyproject.toml @@ -11,7 +11,7 @@ dependencies = [ ] [dependency-groups] -dev = ["httpx>=0.28.1"] +dev = ["httpx>=0.28.1", "loguru>=0.7.0"] [project.scripts] fish-action-watch = "app.action_watch_cli:main" diff --git a/scripts/biomass_poller.py b/scripts/biomass_poller.py new file mode 100755 index 0000000..87678cc --- /dev/null +++ b/scripts/biomass_poller.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +""" +独立进程:轮询 Fish API 的两个结果接口,用 loguru 输出响应。 + + BIOMASS_API_BASE=http://127.0.0.1:8000 POLL_INTERVAL=5 \\ + python scripts/biomass_poller.py + +依赖(与 fish_api dev 组一致):httpx、loguru + cd fish_api && pip install -e ".[dev]" +""" + +from __future__ import annotations + +import argparse +import asyncio +import json +import os +import sys +from typing import Any + +import httpx +from loguru import logger + + +def _fmt_body(resp: httpx.Response) -> Any: + try: + return resp.json() + except Exception: + return resp.text + + +async def poll_once(client: httpx.AsyncClient, base: str) -> None: + base = base.rstrip("/") + camera_url = f"{base}/api/v1/biomass/real/camera/" + health_url = f"{base}/api/v1/biomass/health/result/" + r1 = await client.get(camera_url) + r2 = await client.get(health_url) + logger.info( + "[real/camera/] HTTP {} | {}", + r1.status_code, + json.dumps(_fmt_body(r1), ensure_ascii=False), + ) + logger.info( + "[health/result/] HTTP {} | {}", + r2.status_code, + json.dumps(_fmt_body(r2), ensure_ascii=False), + ) + + +async def poll_loop(base: str, interval: float) -> None: + async with httpx.AsyncClient(timeout=30.0) as client: + while True: + try: + await poll_once(client, base) + except Exception: + logger.exception("poll round failed") + await asyncio.sleep(max(interval, 0.5)) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Poll Fish API biomass GET endpoints.") + parser.add_argument( + "--base-url", + default=os.environ.get("BIOMASS_API_BASE", "http://127.0.0.1:8000"), + help="Fish API 根 URL(默认 BIOMASS_API_BASE 或 http://127.0.0.1:8000)", + ) + parser.add_argument( + "--interval", + type=float, + default=float(os.environ.get("POLL_INTERVAL", "5")), + help="轮询间隔秒数(默认 POLL_INTERVAL 或 5)", + ) + args = parser.parse_args() + + logger.remove() + logger.add( + sys.stderr, + level=os.environ.get("LOG_LEVEL", "INFO"), + format="{time:YYYY-MM-DD HH:mm:ss.SSS} | " + "{level: <8} | " + "{message}", + ) + logger.info( + "biomass_poller 启动 | base={} | interval={}s | GET /api/v1/biomass/real/camera/ 与 /health/result/", + args.base_url.rstrip("/"), + args.interval, + ) + asyncio.run(poll_loop(args.base_url, args.interval)) + + +if __name__ == "__main__": + main()