feat: enhance UI, refactor services, improve templates and tests
- Replace image_extractor with pdf_image_extractor service - Enhance pi_client with expanded API capabilities - Improve summarizer service with additional features - Update admin routes with more endpoints - Add login page template - Enhance detail page with comprehensive layout - Improve search and trends pages - Update base template with additional elements - Refactor tests for better coverage - Add validate_summary script - Update project configuration and dependencies
This commit is contained in:
+18
-26
@@ -182,7 +182,7 @@ class TestSummarizeOneFlow:
|
||||
patch(
|
||||
"app.services.summarizer.call_pi",
|
||||
new_callable=AsyncMock,
|
||||
return_value=mock_pi_output,
|
||||
return_value=(mock_pi_output, "test-session-id"),
|
||||
),
|
||||
):
|
||||
result = await summarize_one(db_session, sample_paper)
|
||||
@@ -246,27 +246,28 @@ class TestSummarizeOneFlow:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_json_not_found(self, db_session, sample_paper, _patch_paths):
|
||||
"""pi 输出无 JSON → json_not_found。"""
|
||||
"""pi 输出无 JSON → 验证循环重试 4 次后 ValueError (unknown)。"""
|
||||
with (
|
||||
patch("app.services.summarizer.download_pdf", new_callable=AsyncMock),
|
||||
patch(
|
||||
"app.services.summarizer.call_pi",
|
||||
new_callable=AsyncMock,
|
||||
return_value="No JSON in this output at all.",
|
||||
return_value=("No JSON in this output at all.", "test-session-id"),
|
||||
),
|
||||
):
|
||||
result = await summarize_one(db_session, sample_paper)
|
||||
|
||||
assert result["status"] == "failed"
|
||||
assert result["error_type"] == "json_not_found"
|
||||
assert result["error_type"] == "unknown"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_field_missing_and_retry(
|
||||
async def test_validation_fails_and_retries(
|
||||
self, db_session, sample_paper, _patch_paths
|
||||
):
|
||||
"""必填字段缺失 → field_missing → retry → permanent_failure。"""
|
||||
"""验证失败(字段不符合要求)→ 重试多次后失败。"""
|
||||
bad_json = json.dumps(
|
||||
{
|
||||
"arxiv_id": sample_paper.arxiv_id,
|
||||
"title_zh": "", # 空的必填字段
|
||||
"one_line": "valid line",
|
||||
"tags": ["tag1"],
|
||||
@@ -282,23 +283,14 @@ class TestSummarizeOneFlow:
|
||||
patch(
|
||||
"app.services.summarizer.call_pi",
|
||||
new_callable=AsyncMock,
|
||||
return_value=bad_output,
|
||||
return_value=(bad_output, "test-session-id"),
|
||||
),
|
||||
):
|
||||
# 第一次失败 → pending (retry)
|
||||
result1 = await summarize_one(db_session, sample_paper)
|
||||
assert result1["status"] == "failed"
|
||||
assert result1["error_type"] == "field_missing"
|
||||
assert result1["retry_count"] == 1
|
||||
|
||||
# 第二次失败 → permanent_failure (SUMMARY_MAX_RETRIES=1, 所以 2 次 > 1+1)
|
||||
db_session.refresh(sample_paper)
|
||||
result2 = await summarize_one(db_session, sample_paper)
|
||||
assert result2["status"] == "failed"
|
||||
assert result2["retry_count"] == 2
|
||||
|
||||
db_session.refresh(sample_paper)
|
||||
assert sample_paper.summary_status.status == "permanent_failure"
|
||||
# _validate_summary 先拦截,4 轮都失败后 ValueError → unknown
|
||||
result = await summarize_one(db_session, sample_paper)
|
||||
assert result["status"] == "failed"
|
||||
assert result["error_type"] == "unknown"
|
||||
assert result["retry_count"] == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_raw_output_saved_on_failure(
|
||||
@@ -310,7 +302,7 @@ class TestSummarizeOneFlow:
|
||||
patch(
|
||||
"app.services.summarizer.call_pi",
|
||||
new_callable=AsyncMock,
|
||||
return_value="Some output without JSON",
|
||||
return_value=("Some output without JSON", "test-session-id"),
|
||||
),
|
||||
):
|
||||
await summarize_one(db_session, sample_paper)
|
||||
@@ -329,7 +321,7 @@ class TestSummarizeOneFlow:
|
||||
patch(
|
||||
"app.services.summarizer.call_pi",
|
||||
new_callable=AsyncMock,
|
||||
return_value=mock_pi_output,
|
||||
return_value=(mock_pi_output, "test-session-id"),
|
||||
),
|
||||
):
|
||||
await summarize_one(db_session, sample_paper)
|
||||
@@ -417,7 +409,7 @@ class TestBatchSummarize:
|
||||
patch(
|
||||
"app.services.summarizer.call_pi",
|
||||
new_callable=AsyncMock,
|
||||
return_value=mock_pi_output,
|
||||
return_value=(mock_pi_output, "test-session-id"),
|
||||
),
|
||||
):
|
||||
result = await summarize_batch(db_session, _session_factory=_TestSession)
|
||||
@@ -464,7 +456,7 @@ class TestBatchSummarize:
|
||||
call_count += 1
|
||||
if call_count == 1:
|
||||
raise PiTimeoutError("timeout")
|
||||
return mock_pi_output
|
||||
return mock_pi_output, "test-session-id"
|
||||
|
||||
with (
|
||||
patch("app.services.summarizer.download_pdf", new_callable=AsyncMock),
|
||||
@@ -506,7 +498,7 @@ class TestBatchSummarize:
|
||||
patch(
|
||||
"app.services.summarizer.call_pi",
|
||||
new_callable=AsyncMock,
|
||||
return_value=mock_pi_output,
|
||||
return_value=(mock_pi_output, "test-session-id"),
|
||||
),
|
||||
):
|
||||
await summarize_batch(db_session, _session_factory=_TestSession)
|
||||
|
||||
Reference in New Issue
Block a user