mirror of
https://github.com/ZhuLinsen/daily_stock_analysis
synced 2026-09-20 10:53:33 +08:00
fix: preserve zero sentiment score alignment (#1900)
* fix: preserve zero sentiment score alignment * fix(review-feedback-1900): 修正 docs/CHANGELOG.md 中与本 PR 无关的条目,避免把未包含在当前 diff 的功能/修复写入本 PR and
This commit is contained in:
@@ -885,6 +885,16 @@ def _prepare_report_for_task_enrichment(
|
||||
return enriched_report
|
||||
|
||||
|
||||
def _first_non_empty_report_value(*values: Any) -> Any:
|
||||
for value in values:
|
||||
if value is None:
|
||||
continue
|
||||
if isinstance(value, str) and not value.strip():
|
||||
continue
|
||||
return value
|
||||
return None
|
||||
|
||||
|
||||
def _ensure_report_action_fields(report_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
enriched_report = dict(report_data)
|
||||
meta = dict(enriched_report.get("meta") or {})
|
||||
@@ -899,7 +909,10 @@ def _ensure_report_action_fields(report_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
explicit_action=raw_result.get("action") or summary.get("action"),
|
||||
report_type=meta.get("report_type"),
|
||||
report_language=report_language,
|
||||
sentiment_score=summary.get("sentiment_score") or raw_result.get("sentiment_score"),
|
||||
sentiment_score=_first_non_empty_report_value(
|
||||
summary.get("sentiment_score"),
|
||||
raw_result.get("sentiment_score"),
|
||||
),
|
||||
guardrail_reason=_extract_guardrail_reason(raw_result),
|
||||
align_with_score=True,
|
||||
)
|
||||
@@ -1339,10 +1352,10 @@ def _build_analysis_report(
|
||||
explicit_action=raw_result_data.get("action") or details_data.get("action") or summary_data.get("action"),
|
||||
report_type=meta.report_type,
|
||||
report_language=report_language,
|
||||
sentiment_score=(
|
||||
summary_data.get("sentiment_score")
|
||||
or raw_result_data.get("sentiment_score")
|
||||
or details_data.get("sentiment_score")
|
||||
sentiment_score=_first_non_empty_report_value(
|
||||
summary_data.get("sentiment_score"),
|
||||
raw_result_data.get("sentiment_score"),
|
||||
details_data.get("sentiment_score"),
|
||||
),
|
||||
guardrail_reason=_extract_guardrail_reason(raw_result_data),
|
||||
align_with_score=True,
|
||||
|
||||
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
||||
> For user-friendly release highlights, see the [GitHub Releases](https://github.com/ZhuLinsen/daily_stock_analysis/releases) page.
|
||||
|
||||
## [Unreleased]
|
||||
- [修复] 修复任务状态接口重建报告动作字段时把合法情绪分 `0` 当成空值的问题,确保低分报告能按评分口径纠正为卖出建议。
|
||||
|
||||
<!-- 新条目格式:- [类型] 描述(类型取值:新功能/改进/修复/文档/测试/chore)-->
|
||||
<!-- 每条独立一行追加到本段末尾,无需分类标题,合并时冲突最小 -->
|
||||
|
||||
|
||||
@@ -651,6 +651,114 @@ class AnalysisApiContractTestCase(unittest.TestCase):
|
||||
self.assertEqual(status.result.report["summary"]["action"], "watch")
|
||||
self.assertEqual(status.result.report["summary"]["action_label"], "观望")
|
||||
|
||||
def test_get_analysis_status_preserves_zero_sentiment_score_when_aligning_action(self) -> None:
|
||||
if get_analysis_status is None or analysis_endpoint_module is None:
|
||||
self.skipTest("analysis endpoint helpers unavailable in this environment")
|
||||
|
||||
created_at = datetime(2026, 5, 21, 17, 40, 0)
|
||||
queue = MagicMock()
|
||||
queue.get_task.return_value = SimpleNamespace(
|
||||
task_id="task-queue-zero-score",
|
||||
stock_code="600519",
|
||||
stock_name="贵州茅台",
|
||||
status=analysis_endpoint_module.TaskStatusEnum.COMPLETED,
|
||||
progress=100,
|
||||
result={
|
||||
"stock_code": "600519",
|
||||
"stock_name": "贵州茅台",
|
||||
"report": {
|
||||
"meta": {
|
||||
"query_id": "task-queue-zero-score",
|
||||
"stock_code": "600519",
|
||||
"report_type": "detailed",
|
||||
"report_language": "zh",
|
||||
},
|
||||
"summary": {
|
||||
"analysis_summary": "趋势显著恶化",
|
||||
"operation_advice": "持有",
|
||||
"sentiment_score": 0,
|
||||
},
|
||||
"details": {
|
||||
"raw_result": {
|
||||
"operation_advice": "持有",
|
||||
"report_language": "zh",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
error=None,
|
||||
original_query=None,
|
||||
selection_source=None,
|
||||
analysis_phase="auto",
|
||||
created_at=created_at,
|
||||
completed_at=datetime(2026, 5, 21, 17, 45, 0),
|
||||
)
|
||||
|
||||
with patch("api.v1.endpoints.analysis.get_task_queue", return_value=queue):
|
||||
status = get_analysis_status("task-queue-zero-score")
|
||||
|
||||
self.assertEqual(status.status, "completed")
|
||||
self.assertIsNotNone(status.result)
|
||||
self.assertEqual(status.result.report["summary"]["sentiment_score"], 0)
|
||||
self.assertEqual(status.result.report["summary"]["action"], "sell")
|
||||
self.assertEqual(status.result.report["summary"]["action_label"], "卖出")
|
||||
|
||||
def test_get_analysis_status_preserves_zero_sentiment_score_when_enriching_report(self) -> None:
|
||||
if get_analysis_status is None or analysis_endpoint_module is None:
|
||||
self.skipTest("analysis endpoint helpers unavailable in this environment")
|
||||
|
||||
created_at = datetime(2026, 5, 21, 17, 40, 0)
|
||||
queue = MagicMock()
|
||||
queue.get_task.return_value = SimpleNamespace(
|
||||
task_id="task-queue-zero-score-enriched",
|
||||
stock_code="600519",
|
||||
stock_name="贵州茅台",
|
||||
status=analysis_endpoint_module.TaskStatusEnum.COMPLETED,
|
||||
progress=100,
|
||||
result={
|
||||
"stock_code": "600519",
|
||||
"stock_name": "贵州茅台",
|
||||
"report": {
|
||||
"meta": {
|
||||
"query_id": "task-queue-zero-score-enriched",
|
||||
"stock_code": "600519",
|
||||
"report_type": "detailed",
|
||||
"report_language": "zh",
|
||||
},
|
||||
"summary": {
|
||||
"analysis_summary": "趋势显著恶化",
|
||||
"operation_advice": "持有",
|
||||
"sentiment_score": 0,
|
||||
},
|
||||
"details": {
|
||||
"raw_result": {
|
||||
"operation_advice": "持有",
|
||||
"report_language": "zh",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
error=None,
|
||||
original_query=None,
|
||||
selection_source=None,
|
||||
analysis_phase="auto",
|
||||
created_at=created_at,
|
||||
completed_at=datetime(2026, 5, 21, 17, 45, 0),
|
||||
)
|
||||
|
||||
with patch("api.v1.endpoints.analysis.get_task_queue", return_value=queue), \
|
||||
patch(
|
||||
"api.v1.endpoints.analysis._load_sync_fundamental_sources",
|
||||
return_value=({}, None),
|
||||
):
|
||||
status = get_analysis_status("task-queue-zero-score-enriched")
|
||||
|
||||
self.assertEqual(status.status, "completed")
|
||||
self.assertIsNotNone(status.result)
|
||||
self.assertEqual(status.result.report["summary"]["sentiment_score"], 0)
|
||||
self.assertEqual(status.result.report["summary"]["action"], "sell")
|
||||
self.assertEqual(status.result.report["summary"]["action_label"], "卖出")
|
||||
|
||||
def test_get_analysis_status_preserves_queue_report_created_at_when_enriching(self) -> None:
|
||||
if get_analysis_status is None or analysis_endpoint_module is None:
|
||||
self.skipTest("analysis endpoint helpers unavailable in this environment")
|
||||
|
||||
Reference in New Issue
Block a user