refactor: centralize compact date formatting

This commit is contained in:
leefer
2026-08-02 00:18:38 +08:00
parent 9710ac7e65
commit 053c5092bc
8 changed files with 39 additions and 14 deletions
+2
View File
@@ -28,6 +28,8 @@ background scheduler
- `server.py` is the stable command/import facade. Runtime composition lives in - `server.py` is the stable command/import facade. Runtime composition lives in
`backend/application.py` and `backend/bootstrap/`. `backend/application.py` and `backend/bootstrap/`.
- `backend/bootstrap/` owns process configuration, dependency construction, startup, and
shared input/display-format contracts. It does not own feature behavior.
- `backend/http/` owns common authentication, request IDs, responses, static delivery, and - `backend/http/` owns common authentication, request IDs, responses, static delivery, and
error normalization. Feature-specific transport handlers live beside their feature. error normalization. Feature-specific transport handlers live beside their feature.
Exact POST endpoints that only delegate to one of those handlers use the explicit maps in Exact POST endpoints that only delegate to one of those handlers use the explicit maps in
+4
View File
@@ -71,6 +71,10 @@ def normalize_date(value: str) -> str:
return parsed.strftime("%Y%m%d") return parsed.strftime("%Y%m%d")
def display_compact_date(value: str) -> str:
return f"{value[:4]}-{value[4:6]}-{value[6:8]}" if len(value) == 8 else value
def validate_stock_code(value: str) -> str: def validate_stock_code(value: str) -> str:
code = value.strip() code = value.strip()
if not re.fullmatch(r"\d{6}", code): if not re.fullmatch(r"\d{6}", code):
+1 -4
View File
@@ -11,6 +11,7 @@ from datetime import datetime, time as dt_time, timedelta
from threading import Lock from threading import Lock
from typing import Any, ClassVar from typing import Any, ClassVar
from backend.bootstrap.config import display_compact_date as _display_date
from backend.data.numbers import finite_number as _number from backend.data.numbers import finite_number as _number
from backend.features.sentiment.engine import apply_sentiment_to_dashboard from backend.features.sentiment.engine import apply_sentiment_to_dashboard
@@ -2007,10 +2008,6 @@ def _display_time(value: Any) -> str:
return f"{raw[:2]}:{raw[2:4]}:{raw[4:6]}" return f"{raw[:2]}:{raw[2:4]}:{raw[4:6]}"
def _display_date(value: str) -> str:
return f"{value[:4]}-{value[4:6]}-{value[6:8]}" if len(value) == 8 else value
def _realtime_market_status(current_time: dt_time) -> str: def _realtime_market_status(current_time: dt_time) -> str:
if current_time < dt_time(9, 25): if current_time < dt_time(9, 25):
return "pre_open" return "pre_open"
+1 -4
View File
@@ -8,6 +8,7 @@ from collections import defaultdict
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Any from typing import Any
from backend.bootstrap.config import display_compact_date as _display_date
from backend.data.numbers import finite_number as _number from backend.data.numbers import finite_number as _number
from backend.data.providers.tushare_client import TushareClient, TushareError from backend.data.providers.tushare_client import TushareClient, TushareError
from backend.features.screener.strategies import ADVANCED_CURATED_STRATEGIES from backend.features.screener.strategies import ADVANCED_CURATED_STRATEGIES
@@ -2200,7 +2201,3 @@ def _regime_reason(regime: str) -> str:
"divergence": "指数或核心仍强,但广度、封板质量开始分化。", "divergence": "指数或核心仍强,但广度、封板质量开始分化。",
"retreat": "情绪指标继续走弱,应提高筛选门槛并接受无候选结果。", "retreat": "情绪指标继续走弱,应提高筛选门槛并接受无候选结果。",
}.get(regime, "市场阶段待确认。") }.get(regime, "市场阶段待确认。")
def _display_date(value: str) -> str:
return f"{value[:4]}-{value[4:6]}-{value[6:8]}" if len(value) == 8 else value
+10 -4
View File
@@ -230,6 +230,12 @@
"path": "backend/data/numbers.py" "path": "backend/data/numbers.py"
} }
], ],
"date_formatting": [
{
"function": "display_compact_date",
"path": "backend/bootstrap/config.py"
}
],
"llm_entrypoints": [ "llm_entrypoints": [
{ {
"function": "stream_with_mentor", "function": "stream_with_mentor",
@@ -289,13 +295,13 @@
}, },
{ {
"path": "backend/features/screener/engine.py", "path": "backend/features/screener/engine.py",
"bytes": 108434, "bytes": 108387,
"lines": 2206 "lines": 2203
}, },
{ {
"path": "backend/data/providers/tushare_client.py", "path": "backend/data/providers/tushare_client.py",
"bytes": 94171, "bytes": 94124,
"lines": 2168 "lines": 2165
}, },
{ {
"path": "frontend/app.js", "path": "frontend/app.js",
+8
View File
@@ -146,6 +146,7 @@ class MarketSliceSourceEquivalenceTests(unittest.TestCase):
self.assertEqual(sha256(ORIGINAL_ROOT / original), sha256(APP_ROOT / migrated)) self.assertEqual(sha256(ORIGINAL_ROOT / original), sha256(APP_ROOT / migrated))
original_tushare = top_level_definitions(ORIGINAL_ROOT / "tushare_client.py") original_tushare = top_level_definitions(ORIGINAL_ROOT / "tushare_client.py")
original_tushare.pop("_number") original_tushare.pop("_number")
original_tushare.pop("_display_date")
self.assertEqual( self.assertEqual(
original_tushare, original_tushare,
top_level_definitions(APP_ROOT / "backend/data/providers/tushare_client.py"), top_level_definitions(APP_ROOT / "backend/data/providers/tushare_client.py"),
@@ -155,6 +156,13 @@ class MarketSliceSourceEquivalenceTests(unittest.TestCase):
function_contract(APP_ROOT / "backend/data/numbers.py", "finite_number"), function_contract(APP_ROOT / "backend/data/numbers.py", "finite_number"),
) )
self.assertIs(canonical_tushare._number, finite_number) self.assertIs(canonical_tushare._number, finite_number)
self.assertEqual(
function_contract(ORIGINAL_ROOT / "tushare_client.py", "_display_date"),
function_contract(
APP_ROOT / "backend/bootstrap/config.py", "display_compact_date"
),
)
self.assertIs(canonical_tushare._display_date, bootstrap_config.display_compact_date)
original_charts = top_level_definitions(ORIGINAL_ROOT / "chart_data_provider.py") original_charts = top_level_definitions(ORIGINAL_ROOT / "chart_data_provider.py")
original_charts.pop("_stock_market_code") original_charts.pop("_stock_market_code")
self.assertEqual( self.assertEqual(
+10 -2
View File
@@ -9,6 +9,7 @@ import advanced_strategies
import llm_strategy import llm_strategy
import screener import screener
import strategy_tracking import strategy_tracking
from backend.bootstrap import config as bootstrap_config
from backend.data.numbers import finite_number from backend.data.numbers import finite_number
from backend.features.screener import compiler, engine, strategies, tracking from backend.features.screener import compiler, engine, strategies, tracking
from backend.features.screener import service as screener_service from backend.features.screener import service as screener_service
@@ -151,12 +152,12 @@ class ScreenerSliceSourceEquivalenceTests(unittest.TestCase):
self.assertEqual( self.assertEqual(
module_contract( module_contract(
ORIGINAL_ROOT / "screener.py", ORIGINAL_ROOT / "screener.py",
excluded_definitions={"_number"}, excluded_definitions={"_display_date", "_number"},
exclude_imports=True, exclude_imports=True,
), ),
module_contract( module_contract(
APP_ROOT / "backend" / "features" / "screener" / "engine.py", APP_ROOT / "backend" / "features" / "screener" / "engine.py",
excluded_definitions={"_number"}, excluded_definitions={"_display_date", "_number"},
exclude_imports=True, exclude_imports=True,
), ),
) )
@@ -165,6 +166,13 @@ class ScreenerSliceSourceEquivalenceTests(unittest.TestCase):
function_contract(APP_ROOT / "backend/data/numbers.py", "finite_number"), function_contract(APP_ROOT / "backend/data/numbers.py", "finite_number"),
) )
self.assertIs(engine._number, finite_number) self.assertIs(engine._number, finite_number)
self.assertEqual(
function_contract(ORIGINAL_ROOT / "screener.py", "_display_date"),
function_contract(
APP_ROOT / "backend/bootstrap/config.py", "display_compact_date"
),
)
self.assertIs(engine._display_date, bootstrap_config.display_compact_date)
self.assertEqual( self.assertEqual(
class_methods( class_methods(
ORIGINAL_ROOT / "backend" / "features" / "screener" / "tracking.py", ORIGINAL_ROOT / "backend" / "features" / "screener" / "tracking.py",
+3
View File
@@ -159,6 +159,9 @@ def build() -> dict[str, Any]:
{"function": "finite_number", "path": "backend/data/numbers.py"}, {"function": "finite_number", "path": "backend/data/numbers.py"},
{"function": "non_nan_number", "path": "backend/data/numbers.py"}, {"function": "non_nan_number", "path": "backend/data/numbers.py"},
], ],
"date_formatting": [
{"function": "display_compact_date", "path": "backend/bootstrap/config.py"},
],
"llm_entrypoints": [ "llm_entrypoints": [
{"function": "stream_with_mentor", "path": "backend/features/mentor/agent.py"}, {"function": "stream_with_mentor", "path": "backend/features/mentor/agent.py"},
{"function": "interpret_heaven", "path": "backend/features/heaven/agent.py"}, {"function": "interpret_heaven", "path": "backend/features/heaven/agent.py"},