Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/data/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
Layer0RawData,
Layer0TableListItem,
Layer0TableMeta,
TableCrossmatchSummary,
TableRecord,
TableStatistics,
)
Expand All @@ -41,6 +42,7 @@
"Layer0TableListItem",
"ColumnDescription",
"TableStatistics",
"TableCrossmatchSummary",
"get_object",
"DesignationRecord",
"ICRSRecord",
Expand Down
5 changes: 5 additions & 0 deletions app/data/model/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,8 @@ class TableStatistics:
last_modified_dt: datetime.datetime
total_rows: int
total_original_rows: int


@dataclass
class TableCrossmatchSummary:
counts: dict[str, int]
21 changes: 21 additions & 0 deletions app/data/repositories/layer0/records.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,27 @@ def get_table_statistics(self, table_name: str) -> model.TableStatistics:
total_original_rows,
)

def get_table_crossmatch_summary(self, table_name: str) -> model.TableCrossmatchSummary:
table_id_row = self._storage.query_one(template.FETCH_RAWDATA_REGISTRY, params=[table_name])
table_id = table_id_row["id"]

rows = self._storage.query(
"""
SELECT CASE
WHEN c.record_id IS NULL THEN 'unprocessed'
ELSE c.triage_status::text
END AS triage,
COUNT(1) AS cnt
FROM layer0.records AS o
LEFT JOIN layer0.crossmatch AS c ON c.record_id = o.id
WHERE o.table_id = %s
GROUP BY triage
""",
params=[table_id],
)

return model.TableCrossmatchSummary(counts={row["triage"]: row["cnt"] for row in rows})

def set_crossmatch_results(self, rows: list[tuple[str, enums.RecordTriageStatus, list[int]]]) -> None:
if not rows:
return
Expand Down
3 changes: 3 additions & 0 deletions app/data/repositories/layer0/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ def register_records(self, table_name: str, record_ids: list[str]) -> None:
def get_table_statistics(self, table_name: str) -> model.TableStatistics:
return self.records_repo.get_table_statistics(table_name)

def get_table_crossmatch_summary(self, table_name: str) -> model.TableCrossmatchSummary:
return self.records_repo.get_table_crossmatch_summary(table_name)

def get_processed_records(
self,
limit: int,
Expand Down
26 changes: 21 additions & 5 deletions app/domain/adminapi/table_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,25 @@ def get_table(self, r: adminapi.GetTableRequest) -> adminapi.GetTableResponse:
raise RuntimeError(f"Table {r.table_name} has no ID")

table_stats = self.layer0_repo.get_table_statistics(r.table_name)
crossmatch_summary = self.layer0_repo.get_table_crossmatch_summary(r.table_name)
rows_num = table_stats.total_original_rows
metadata = {"datatype": meta.datatype, "modification_dt": meta.modification_dt}

statistics = None
if table_stats.statuses:
statistics = table_stats.statuses
pending_count = crossmatch_summary.counts.get(adminapi.CrossmatchTriageStatus.PENDING.value, 0)
resolved_count = crossmatch_summary.counts.get(adminapi.CrossmatchTriageStatus.RESOLVED.value, 0)
unprocessed_count = crossmatch_summary.counts.get(adminapi.CrossmatchTriageStatus.UNPROCESSED.value, 0)

if resolved_count == 0 and pending_count == 0:
crossmatch_result = adminapi.TableCrossmatchResultStatus.NOT_STARTED
elif pending_count > 0 or unprocessed_count > 0:
crossmatch_result = adminapi.TableCrossmatchResultStatus.IN_PROGRESS
else:
crossmatch_result = adminapi.TableCrossmatchResultStatus.DONE

crossmatch_statuses = {
adminapi.CrossmatchTriageStatus.UNPROCESSED: unprocessed_count,
adminapi.CrossmatchTriageStatus.PENDING: pending_count,
adminapi.CrossmatchTriageStatus.RESOLVED: resolved_count,
}

return adminapi.GetTableResponse(
id=meta.table_id,
Expand All @@ -210,7 +223,10 @@ def get_table(self, r: adminapi.GetTableRequest) -> adminapi.GetTableResponse:
rows_num=rows_num,
meta=metadata,
bibliography=_bibliography_to_presentation(bibliography),
statistics=statistics,
crossmatch=adminapi.TableCrossmatchResults(
result=crossmatch_result,
statuses=crossmatch_statuses,
),
)

def get_records(self, r: adminapi.GetRecordsRequest) -> adminapi.GetRecordsResponse:
Expand Down
15 changes: 13 additions & 2 deletions app/presentation/adminapi/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from astropy import units as u

from app.lib.storage import enums, mapping
from app.presentation.adminapi.records import GetRecordsRequest, GetRecordsResponse
from app.presentation.adminapi.records import CrossmatchTriageStatus, GetRecordsRequest, GetRecordsResponse

DatatypeEnum = enum.StrEnum(
"DatatypeEnum",
Expand Down Expand Up @@ -71,14 +71,25 @@ class GetTableListResponse(pydantic.BaseModel):
tables: list[TableListItem]


class TableCrossmatchResultStatus(enum.Enum):
DONE = "done"
IN_PROGRESS = "in_progress"
NOT_STARTED = "not_started"


class TableCrossmatchResults(pydantic.BaseModel):
result: TableCrossmatchResultStatus
statuses: dict[CrossmatchTriageStatus, int]


class GetTableResponse(pydantic.BaseModel):
id: int
description: str
column_info: list[ColumnDescription]
rows_num: int
meta: dict[str, Any]
bibliography: Bibliography
statistics: dict[enums.RecordCrossmatchStatus, int] | None = None
crossmatch: TableCrossmatchResults


class CreateTableRequest(pydantic.BaseModel):
Expand Down
49 changes: 47 additions & 2 deletions tests/regression/upload_simple_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,14 @@ def check_table_list(session: requests.Session, table_name: str):


@lib.test_logging_decorator
def check_get_table(session: requests.Session, table_name: str, expected_columns: int, expected_rows: int):
def check_get_table(
session: requests.Session,
table_name: str,
expected_columns: int,
expected_rows: int,
expected_result: adminapi.TableCrossmatchResultStatus,
expected_statuses: dict[adminapi.CrossmatchTriageStatus, int],
):
request_data = adminapi.GetTableRequest(table_name=table_name)
response = session.get("/v1/table", params=request_data.model_dump(mode="json"))
response.raise_for_status()
Expand All @@ -375,6 +382,9 @@ def check_get_table(session: requests.Session, table_name: str, expected_columns
assert table_info["rows_num"] == expected_rows
assert "bibliography" in table_info
assert "meta" in table_info
assert "crossmatch" in table_info
assert table_info["crossmatch"]["result"] == expected_result.value
assert table_info["crossmatch"]["statuses"] == {status.value: count for status, count in expected_statuses.items()}


@lib.test_logging_decorator
Expand Down Expand Up @@ -497,7 +507,18 @@ def run():
)

check_table_list(adminapi_session, table_name)
check_get_table(adminapi_session, table_name, expected_columns=6, expected_rows=OBJECTS_NUM)
check_get_table(
adminapi_session,
table_name,
expected_columns=6,
expected_rows=OBJECTS_NUM,
expected_result=adminapi.TableCrossmatchResultStatus.NOT_STARTED,
expected_statuses={
adminapi.CrossmatchTriageStatus.UNPROCESSED: OBJECTS_NUM,
adminapi.CrossmatchTriageStatus.PENDING: 0,
adminapi.CrossmatchTriageStatus.RESOLVED: 0,
},
)

records = get_records(adminapi_session, table_name, OBJECTS_NUM * 2)
upload_structured_data(adminapi_session, records)
Expand All @@ -509,6 +530,18 @@ def run():
record_ids,
triage_statuses=[enums.RecordTriageStatus.RESOLVED] * len(record_ids),
)
check_get_table(
adminapi_session,
table_name,
expected_columns=6,
expected_rows=OBJECTS_NUM,
expected_result=adminapi.TableCrossmatchResultStatus.DONE,
expected_statuses={
adminapi.CrossmatchTriageStatus.UNPROCESSED: 0,
adminapi.CrossmatchTriageStatus.PENDING: 0,
adminapi.CrossmatchTriageStatus.RESOLVED: OBJECTS_NUM,
},
)

submit_crossmatch(table_name)
layer2_import()
Expand Down Expand Up @@ -543,6 +576,18 @@ def run():
triage_statuses=[enums.RecordTriageStatus.PENDING] * n_pending
+ [enums.RecordTriageStatus.RESOLVED] * n_resolved,
)
check_get_table(
adminapi_session,
table_name_2,
expected_columns=6,
expected_rows=TABLE2_OBJECTS_NUM,
expected_result=adminapi.TableCrossmatchResultStatus.IN_PROGRESS,
expected_statuses={
adminapi.CrossmatchTriageStatus.UNPROCESSED: 0,
adminapi.CrossmatchTriageStatus.PENDING: n_pending,
adminapi.CrossmatchTriageStatus.RESOLVED: n_resolved,
},
)

check_triage_via_records(adminapi_session, table_name_2, expected_pending=n_pending, expected_resolved=n_resolved)

Expand Down
Loading