feat: add PPAC candidate tab and status
Deploy production / deploy (push) Successful in 56s

This commit is contained in:
yuxuanhui
2026-09-13 12:57:38 +08:00
parent e256d6fef1
commit 8444e3055e
10 changed files with 319 additions and 33 deletions
+108
View File
@@ -0,0 +1,108 @@
"""PPAC candidates retain their theme failure and wait for platform eligibility."""
import copy
import csv
import io
import pytest
from app.alphas import check_summary, snapshot_columns, upsert_alpha
from tests import test_submission
from tests.conftest import alpha
THEME = {"name": "PURE_POWER_POOL_THEME", "result": "FAIL"}
LIMIT = {"name": "REGULAR_SUBMISSION", "result": "FAIL"}
OTHER = {"name": "LOW_SHARPE", "result": "FAIL"}
@pytest.mark.parametrize("checked", [False, True])
@pytest.mark.parametrize("checks,expected", [
([THEME], "PPAC_CANDIDATE"),
([{**THEME, "result": "fail"}], "PPAC_CANDIDATE"),
([THEME, LIMIT, {"name": "MATCHES_THEMES", "result": "WARNING"},
{"name": "PROD_CORRELATION", "result": "PENDING"}], "PPAC_CANDIDATE"),
([THEME, OTHER], "FAIL_2"),
([THEME, THEME], "FAIL_2"),
([THEME, {"result": "FAIL"}], "FAIL_2"),
([OTHER], "FAIL_1"),
([{**THEME, "result": "PASS"}, OTHER], "FAIL_1"),
([{**THEME, "result": False}], "PENDING"),
([{**THEME, "value": False}], "PPAC_CANDIDATE"),
([], "PENDING"),
([{}], "PENDING"),
])
def test_only_a_single_explicit_theme_failure_is_a_candidate(checks, expected, checked):
original = copy.deepcopy(checks)
columns = snapshot_columns({}, {}, checks, checked=checked)
assert columns["check_type"] == expected
assert checks == original
if expected == "PPAC_CANDIDATE":
assert check_summary(checks, check_type=expected)["failed_checks"] == ["PURE_POWER_POOL_THEME"]
async def test_ppac_scope_filters_before_pagination_and_export(app, logged_in):
async with app.state.sessions.begin() as db:
for name, checks, status, region, hidden in [
("candidate1", [THEME], "UNSUBMITTED", "USA", False),
("candidate2", [THEME, LIMIT], "UNSUBMITTED", "USA", True),
("other_region", [THEME], "UNSUBMITTED", "CHN", False),
("submitted", [THEME], "ACTIVE", "USA", False),
("missing_status", [THEME], None, "USA", False),
("two_failures", [THEME, OTHER], "UNSUBMITTED", "USA", False),
("other_failure", [OTHER], "UNSUBMITTED", "USA", False),
("passed", [{**THEME, "result": "PASS"}], "UNSUBMITTED", "USA", False),
("missing", [], "UNSUBMITTED", "USA", False),
]:
await upsert_alpha(db, alpha(name, status=status, hidden=hidden, settings={"region": region},
**{"is": {"checks": checks}}))
query = "ppac_candidate=true&region=USA&sort=id&direction=asc&limit=1&offset=1"
response = await logged_in.get(f"/api/v1/alphas?{query}")
assert response.status_code == 200, response.text
result = response.json()
assert result["total"] == 2
assert [row["id"] for row in result["items"]] == ["candidate2"]
assert result["items"][0]["check_type"] == "PPAC_CANDIDATE"
assert result["items"][0]["failed_checks"] == ["PURE_POWER_POOL_THEME"]
export = await logged_in.get(f"/api/v1/alphas/export?{query}")
rows = list(csv.DictReader(io.StringIO(export.text.lstrip("\ufeff"))))
assert [row["id"] for row in rows] == ["candidate1", "candidate2"]
assert {row["check_type"] for row in rows} == {"PPAC_CANDIDATE"}
assert (await logged_in.get("/api/v1/alphas?ppac_candidate=true&submission=SUBMITTED")).json()["total"] == 0
assert (await logged_in.get("/api/v1/alphas?ppac_candidate=true&check_type=FAIL_1")).json()["total"] == 0
assert (await logged_in.get("/api/v1/alphas?ppac_candidate=true&submission_blocked=true")).json()["total"] == 1
assert (await logged_in.get("/api/v1/alphas?ppac_candidate=true&q=candidate1")).json()["total"] == 1
assert (await logged_in.get("/api/v1/alphas?check_type=PPAC_CANDIDATE&submission=UNSUBMITTED")).json()["total"] == 3
assert (await logged_in.get("/api/v1/alphas?ppac_candidate=false")).json()["total"] == 4
saved = await logged_in.post("/api/v1/research/assets", json={
"kind": "view", "content": {"name": "候选PPAC", "filters": {
"submission": "UNSUBMITTED", "ppac_candidate": True, "region": "USA",
}, "columns": ["name", "check_type"]},
})
assert saved.status_code in (200, 201), saved.text
assert saved.json()["content"]["filters"]["ppac_candidate"] is True
async def test_check_and_sync_refresh_candidate_status_without_losing_evidence(app, logged_in, monkeypatch):
await test_submission.setup(app, alpha(**{"is": {"checks": [OTHER]}}))
for checks, expected in [([THEME, LIMIT], "PPAC_CANDIDATE"), ([{**THEME, "result": "PASS"}], "PASS")]:
monkeypatch.setattr(test_submission, "CHECKS", checks)
response = await test_submission.enqueue(logged_in)
assert response.status_code == 202, response.text
await app.state.runner.execute(response.json()["id"])
state = (await logged_in.get("/api/v1/alphas/alpha1/submission")).json()
assert state["job"]["status"] == "completed"
assert state["check_summary"]["check_type"] == expected
detail = (await logged_in.get("/api/v1/alphas/alpha1")).json()
assert detail["checks"] == checks
assert detail["check_type"] == expected
assert detail["research"]["note"] == "preserve local research"
assert (await logged_in.get("/api/v1/alphas?ppac_candidate=true")).json()["total"] == (expected == "PPAC_CANDIDATE")
for checks, expected in [([THEME], "PPAC_CANDIDATE"), ([THEME, OTHER], "FAIL_2")]:
async with app.state.sessions.begin() as db:
await upsert_alpha(db, alpha(**{"is": {"checks": checks}}))
detail = (await logged_in.get("/api/v1/alphas/alpha1")).json()
assert detail["check_type"] == expected
assert detail["checks"] == checks
assert detail["research"]["note"] == "preserve local research"
assert (await logged_in.get("/api/v1/alphas?ppac_candidate=true")).json()["total"] == (expected == "PPAC_CANDIDATE")
@@ -0,0 +1,55 @@
"""Historical PPAC classification changes only matching cached check types."""
from datetime import datetime, timezone
from pathlib import Path
import sqlalchemy as sa
from alembic import command
from alembic.config import Config
from cryptography.fernet import Fernet
def test_ppac_backfill_preserves_evidence_across_batches_and_downgrade(tmp_path, monkeypatch):
database = tmp_path / "ppac.db"
monkeypatch.setenv("DATABASE_URL", f"sqlite+aiosqlite:///{database}")
monkeypatch.setenv("ADMIN_PASSWORD", "migration-test-only")
monkeypatch.setenv("ENCRYPTION_KEY", Fernet.generate_key().decode())
monkeypatch.setenv("WQ_EMAIL", "")
monkeypatch.setenv("WQ_PASSWORD", "")
root = Path(__file__).resolve().parents[1]
config = Config(str(root / "alembic.ini"))
config.set_main_option("script_location", str(root / "migrations"))
command.upgrade(config, "0021")
engine = sa.create_engine(f"sqlite:///{database}")
alphas = sa.Table("alphas", sa.MetaData(), autoload_with=engine)
theme = {"name": "PURE_POWER_POOL_THEME", "result": "FAIL"}
other = {"name": "LOW_SHARPE", "result": "FAIL"}
patterns = [
([theme], "FAIL_1", "PPAC_CANDIDATE"),
([{**theme, "result": "fail"}, {"name": "REGULAR_SUBMISSION", "result": "FAIL"}], "FAIL_1", "PPAC_CANDIDATE"),
([theme, other], "FAIL_2", "FAIL_2"),
([theme, theme], "FAIL_2", "FAIL_2"),
([other], "FAIL_1", "FAIL_1"),
([{**theme, "result": "PASS"}], "PASS", "PASS"),
([{**theme, "result": "WARNING"}], "PRE_CHECK", "PRE_CHECK"),
([{}], "PENDING", "PENDING"),
(None, "PENDING", "PENDING"),
]
with engine.begin() as db:
db.execute(alphas.insert(), [
{"id": f"ppac{i:04}", "status": "UNSUBMITTED", "hidden": False, "settings": {}, "os_metrics": {},
"is_metrics": {"checks": patterns[i % len(patterns)][0]}, "checks": patterns[i % len(patterns)][0],
"check_type": patterns[i % len(patterns)][1], "synced_at": datetime(2026, 9, 13, tzinfo=timezone.utc),
"raw": {"is": {"checks": patterns[i % len(patterns)][0]}}}
for i in range(503)
])
original = db.execute(sa.select(alphas).order_by(alphas.c.id)).mappings().all()
for target, position in [("0022", 2), ("0021", 1), ("0022", 2)]:
(command.upgrade if target == "0022" else command.downgrade)(config, target)
with engine.connect() as db:
rows = db.execute(sa.select(alphas).order_by(alphas.c.id)).mappings().all()
assert len(rows) == 503
for i, row in enumerate(rows):
assert dict(row) == {**original[i], "check_type": patterns[i % len(patterns)][position]}
command.check(config)
engine.dispose()