feat: unify scope settings and catalog table interactions
Deploy production / deploy (push) Successful in 54s

This commit is contained in:
yuxuanhui
2026-09-12 12:11:53 +08:00
parent 9dee2de039
commit 8981c24fda
29 changed files with 2521 additions and 796 deletions
+29 -3
View File
@@ -30,7 +30,7 @@ def setting_rows(data):
try:
children = data["actions"]["POST"]["settings"]["children"]
def choices(key, instrument=None, region=None):
def choices(key, instrument=None, region=None, delay=None, universe=None):
value = children[key]["choices"]
if isinstance(value, dict) and "instrumentType" in value:
value = value["instrumentType"][instrument]
@@ -38,6 +38,14 @@ def setting_rows(data):
value = value[instrument]
if isinstance(value, dict) and "region" in value:
value = value["region"][region]
for dimension, selected in (("delay", delay), ("universe", universe)):
if isinstance(value, dict) and dimension in value:
branches = value[dimension]
if not isinstance(branches, dict):
raise ValueError()
value = branches.get(str(selected), branches.get(selected))
if not isinstance(value, list):
raise ValueError()
return [item["value"] for item in value]
rows = []
@@ -48,12 +56,29 @@ def setting_rows(data):
for delay in choices("delay", instrument, region):
if type(delay) is not int or delay not in (0, 1):
continue
for universe in choices("universe", instrument, region):
for universe in choices("universe", instrument, region, delay):
neutralizations = (
choices("neutralization", instrument, region)
choices("neutralization", instrument, region, delay, universe)
if "neutralization" in children
else []
)
fields = {}
for key in (
"decay", "truncation", "pasteurization", "unitHandling",
"nanHandling", "language", "visualization", "maxTrade", "maxPosition",
):
definition = children.get(key)
if not isinstance(definition, dict):
continue
field = {}
if "choices" in definition:
field["choices"] = choices(key, instrument, region, delay, universe)
if any(type(v) not in (str, int, float, bool) for v in field["choices"]):
raise ValueError()
for source, target in (("min_value", "minimum"), ("max_value", "maximum")):
if type(definition.get(source)) in (int, float):
field[target] = definition[source]
fields[key] = field
rows.append(
{
"instrument_type": instrument,
@@ -61,6 +86,7 @@ def setting_rows(data):
"universe": universe,
"delay": delay,
"neutralizations": neutralizations,
"fields": fields,
}
)
if not rows:
+4 -2
View File
@@ -1,6 +1,6 @@
"""Authenticated preparation and field-directory endpoints."""
from typing import Annotated
from typing import Annotated, Literal
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from sqlalchemy import delete, select
@@ -55,9 +55,11 @@ async def preparations(
scope_key: str | None = None,
limit: int = Query(25, ge=1, le=100),
offset: int = Query(0, ge=0),
sort: Literal["name", "created_at", "updated_at"] = "updated_at",
direction: Literal["asc", "desc"] = "desc",
):
async with request.app.state.sessions() as db:
return await Preparations(db).list(q, scope_key, limit, offset)
return await Preparations(db).list(q, scope_key, limit, offset, sort, direction)
@router.post("/data-preparations", status_code=201)
+14 -7
View File
@@ -138,9 +138,12 @@ class Preparations:
async def online_fields(self, filters):
text_filters = (filters.q, filters.dataset_id, filters.field_type)
numeric_filters = (
filters.coverage_min, filters.coverage_max,
filters.user_count_min, filters.user_count_max,
filters.alpha_count_min, filters.alpha_count_max,
filters.coverage_min,
filters.coverage_max,
filters.user_count_min,
filters.user_count_max,
filters.alpha_count_min,
filters.alpha_count_max,
)
if not any(value and value.strip() for value in text_filters) and not any(
value is not None for value in numeric_filters
@@ -304,7 +307,7 @@ class Preparations:
)
)
async def list(self, q="", scope_key=None, limit=25, offset=0):
async def list(self, q="", scope_key=None, limit=25, offset=0, sort="updated_at", direction="desc"):
query = select(DataPreparation)
if scope_key:
query = query.where(DataPreparation.scope_key == scope_key)
@@ -316,9 +319,13 @@ class Preparations:
)
)
total = await self.db.scalar(select(func.count()).select_from(query.subquery()))
rows = await self.db.scalars(
query.order_by(DataPreparation.updated_at.desc(), DataPreparation.id).limit(limit).offset(offset)
)
column = {
"name": DataPreparation.name,
"created_at": DataPreparation.created_at,
"updated_at": DataPreparation.updated_at,
}[sort]
order = column.asc() if direction == "asc" else column.desc()
rows = await self.db.scalars(query.order_by(order, DataPreparation.id).limit(limit).offset(offset))
return page([await self.output(row) for row in rows], total, limit, offset)
async def members(self, preparation_id, q="", dataset_id=None, limit=25, offset=0):