This commit is contained in:
@@ -35,6 +35,7 @@ def snapshot_columns(settings, metrics, checks, *, checked=False):
|
||||
"""
|
||||
settings = settings if isinstance(settings, dict) else {}
|
||||
metrics = metrics if isinstance(metrics, dict) else {}
|
||||
blocked = submission_limits(checks)["status"] == "blocked"
|
||||
checks, _ = split_checks(checks)
|
||||
valid = [check for check in checks if isinstance(check, dict)]
|
||||
failures = len(failed_checks(checks))
|
||||
@@ -52,6 +53,7 @@ def snapshot_columns(settings, metrics, checks, *, checked=False):
|
||||
neutralization = settings.get("neutralization")
|
||||
return {
|
||||
"check_type": check_type,
|
||||
"submission_blocked": blocked,
|
||||
"neutralization": neutralization if isinstance(neutralization, str) else None,
|
||||
"pnl": number(metrics.get("pnl")),
|
||||
"prod_correlation": prod_correlation,
|
||||
@@ -189,6 +191,10 @@ def list_statement(filters):
|
||||
query = select(Alpha, Research).join(Research, Research.alpha_id == Alpha.id)
|
||||
if filters.submission:
|
||||
query = query.where(submission_condition(filters.submission))
|
||||
if filters.submission_blocked is not None:
|
||||
query = query.where(Alpha.submission_blocked == filters.submission_blocked)
|
||||
if filters.submission_blocked:
|
||||
query = query.where(submission_condition("UNSUBMITTED"))
|
||||
source_filters = {k: getattr(filters, k) for k in ("source", "source_reference", "research_id", "backtest_run_id")}
|
||||
if any(source_filters.values()):
|
||||
query = query.where(Alpha.id.in_(source_alpha_ids(**source_filters)))
|
||||
|
||||
@@ -87,6 +87,7 @@ class Alpha(Base):
|
||||
pnl: Mapped[float | None] = mapped_column(Float)
|
||||
neutralization: Mapped[str | None] = mapped_column(Text)
|
||||
check_type: Mapped[str] = mapped_column(String(20), default="PENDING", server_default="PENDING", index=True)
|
||||
submission_blocked: Mapped[bool] = mapped_column(Boolean, default=False, server_default=false(), index=True)
|
||||
date_created: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True)
|
||||
date_submitted: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
synced_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
|
||||
|
||||
@@ -69,6 +69,7 @@ class PreferencesInput(Contract):
|
||||
|
||||
|
||||
class AlphaFilters(Contract):
|
||||
submission_blocked: bool | None = None
|
||||
submission: Submission | None = None
|
||||
source: str | None = Field(default=None, max_length=100)
|
||||
source_reference: str | None = Field(default=None, max_length=200)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
"""Index the cached submission limit independently of Alpha quality checks."""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision = "0020"
|
||||
down_revision = "0019"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column("alphas", sa.Column("submission_blocked", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
table = sa.table("alphas", sa.column("id", sa.String()), sa.column("checks", sa.JSON()),
|
||||
sa.column("submission_blocked", sa.Boolean()))
|
||||
connection = op.get_bind()
|
||||
last_id = None
|
||||
while True:
|
||||
query = sa.select(table.c.id, table.c.checks).order_by(table.c.id).limit(500)
|
||||
if last_id is not None:
|
||||
query = query.where(table.c.id > last_id)
|
||||
rows = connection.execute(query).mappings().all()
|
||||
if not rows:
|
||||
break
|
||||
# Frozen snapshot interpretation; never depend on future application code.
|
||||
blocked = [row["id"] for row in rows if isinstance(row["checks"], list) and any(
|
||||
isinstance(check, dict) and check.get("name") == "REGULAR_SUBMISSION"
|
||||
and isinstance(check.get("result"), str) and check["result"].upper() == "FAIL"
|
||||
for check in row["checks"])]
|
||||
if blocked:
|
||||
connection.execute(table.update().where(table.c.id.in_(blocked)).values(submission_blocked=True))
|
||||
last_id = rows[-1]["id"]
|
||||
op.create_index("ix_alphas_submission_blocked", "alphas", ["submission_blocked"])
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_index("ix_alphas_submission_blocked", table_name="alphas")
|
||||
op.drop_column("alphas", "submission_blocked")
|
||||
@@ -219,6 +219,7 @@ def test_migration_backfills_multiple_batches_and_preserves_research(tmp_path, m
|
||||
assert len(rows) == 503
|
||||
for i, row in enumerate(rows):
|
||||
expected = snapshot_columns(row["settings"], row["is_metrics"], checks(i % 4), checked=True)
|
||||
expected.pop("submission_blocked") # Added after this historical migration.
|
||||
assert {key: row[key] for key in expected} == expected
|
||||
record = db.execute(sa.select(research)).mappings().one()
|
||||
assert record["tags"] == ["PPAC"] and record["note"] == "keep" and record["version"] == 7
|
||||
|
||||
@@ -60,5 +60,15 @@ def test_stage_backfill_preserves_evidence_and_uses_checkpoints(tmp_path, monkey
|
||||
for i, row in enumerate(rows):
|
||||
assert row["check_type"] == patterns[i % 7][expected_index]
|
||||
assert row["checks"] == row["raw"]["is"]["checks"] == row["is_metrics"]["checks"] == patterns[i % 7][0]
|
||||
command.upgrade(config, "head")
|
||||
current = sa.Table("alphas", sa.MetaData(), autoload_with=engine)
|
||||
with engine.connect() as db:
|
||||
rows = db.execute(sa.select(current).order_by(current.c.id)).mappings().all()
|
||||
for i, row in enumerate(rows):
|
||||
assert row["submission_blocked"] is (i % 7 < 4)
|
||||
assert row["checks"] == patterns[i % 7][0]
|
||||
command.downgrade(config, "0019")
|
||||
assert "submission_blocked" not in {c["name"] for c in sa.inspect(engine).get_columns("alphas")}
|
||||
command.upgrade(config, "head")
|
||||
command.check(config)
|
||||
engine.dispose()
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
"""Submission limits filter cached unsubmitted Alpha snapshots before pagination."""
|
||||
|
||||
import csv
|
||||
import io
|
||||
|
||||
from app.alphas import snapshot_columns, upsert_alpha
|
||||
from tests.conftest import alpha
|
||||
|
||||
|
||||
async def test_blocked_filter_intersects_filters_and_export_and_refresh(app, logged_in):
|
||||
async with app.state.sessions.begin() as db:
|
||||
for name, result, status, region in [
|
||||
("blocked1", "FAIL", "UNSUBMITTED", "USA"),
|
||||
("blocked2", "fail", "UNSUBMITTED", "USA"),
|
||||
("other_region", "FAIL", "UNSUBMITTED", "CHN"),
|
||||
("submitted", "FAIL", "ACTIVE", "USA"),
|
||||
("passed", "PASS", "UNSUBMITTED", "USA"),
|
||||
("pending", "PENDING", "UNSUBMITTED", "USA"),
|
||||
("missing", None, "UNSUBMITTED", "USA"),
|
||||
]:
|
||||
checks = [{"name": "LOW_SHARPE", "result": "PASS"}]
|
||||
if result:
|
||||
checks.append({"name": "REGULAR_SUBMISSION", "result": result})
|
||||
await upsert_alpha(db, alpha(name, status=status, settings={"region": region}, **{"is": {"checks": checks}}))
|
||||
query = "submission_blocked=true®ion=USA&check_type=PRE_CHECK&sort=id&direction=asc&limit=1&offset=1"
|
||||
response = await logged_in.get(f"/api/v1/alphas?{query}")
|
||||
assert response.status_code == 200
|
||||
assert response.json()["total"] == 2
|
||||
assert [row["id"] for row in response.json()["items"]] == ["blocked2"]
|
||||
export = await logged_in.get(f"/api/v1/alphas/export?{query}")
|
||||
assert [row["id"] for row in csv.DictReader(io.StringIO(export.text.lstrip("\ufeff")))] == ["blocked1", "blocked2"]
|
||||
assert (await logged_in.get("/api/v1/alphas?submission_blocked=true&submission=SUBMITTED")).json()["total"] == 0
|
||||
assert (await logged_in.get("/api/v1/alphas?submission_blocked=true&q=blocked1")).json()["total"] == 1
|
||||
async with app.state.sessions.begin() as db:
|
||||
await upsert_alpha(db, alpha("blocked1", **{"is": {"checks": [{"name": "REGULAR_SUBMISSION", "result": "PASS"}]}}))
|
||||
assert (await logged_in.get("/api/v1/alphas?submission_blocked=true&q=blocked1")).json()["total"] == 0
|
||||
|
||||
|
||||
def test_explicit_check_refreshes_limit_independently_of_quality():
|
||||
for checked in (False, True):
|
||||
for result, blocked in [("FAIL", True), ("PASS", False), ("PENDING", False)]:
|
||||
columns = snapshot_columns({}, {}, [{"name": "LOW_SHARPE", "result": "PASS"},
|
||||
{"name": "REGULAR_SUBMISSION", "result": result}], checked=checked)
|
||||
assert columns["submission_blocked"] is blocked
|
||||
assert columns["check_type"] == ("PASS" if checked else "PRE_CHECK")
|
||||
Reference in New Issue
Block a user