Coverage for python/felis/validation.py: 56%
45 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-20 03:38 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-20 03:38 -0700
1# This file is part of felis.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22from __future__ import annotations
24import logging
25from collections.abc import Sequence
26from typing import Any
28from pydantic import Field, model_validator
30from .datamodel import Column, DescriptionStr, Schema, Table
32logger = logging.getLogger(__name__)
34__all__ = ["RspColumn", "RspSchema", "RspTable", "get_schema"]
37class RspColumn(Column):
38 """Column for RSP data validation."""
40 description: DescriptionStr
41 """Redefine description to make it required."""
44class RspTable(Table):
45 """Table for RSP data validation.
47 The list of columns is overridden to use RspColumn instead of Column.
49 Tables for the RSP must have a TAP table index and a valid description.
50 """
52 description: DescriptionStr
53 """Redefine description to make it required."""
55 tap_table_index: int = Field(..., alias="tap:table_index")
56 """Redefine the TAP_SCHEMA table index so that it is required."""
58 columns: Sequence[RspColumn]
59 """Redefine columns to include RSP validation."""
61 @model_validator(mode="after") # type: ignore[arg-type]
62 @classmethod
63 def check_tap_principal(cls: Any, tbl: "RspTable") -> "RspTable":
64 """Check that at least one column is flagged as 'principal' for
65 TAP purposes.
66 """
67 for col in tbl.columns:
68 if col.tap_principal == 1:
69 return tbl
70 raise ValueError(f"Table '{tbl.name}' is missing at least one column designated as 'tap:principal'")
73class RspSchema(Schema):
74 """Schema for RSP data validation.
76 TAP table indexes must be unique across all tables.
77 """
79 tables: Sequence[RspTable]
80 """Redefine tables to include RSP validation."""
82 @model_validator(mode="after") # type: ignore[arg-type]
83 @classmethod
84 def check_tap_table_indexes(cls: Any, sch: RspSchema) -> RspSchema:
85 """Check that the TAP table indexes are unique."""
86 table_indicies = set()
87 for table in sch.tables:
88 table_index = table.tap_table_index
89 if table_index is not None:
90 if table_index in table_indicies:
91 raise ValueError(f"Duplicate 'tap:table_index' value {table_index} found in schema")
92 table_indicies.add(table_index)
93 return sch
96def get_schema(schema_name: str) -> type[Schema]:
97 """Get the schema class for the given name."""
98 if schema_name == "default":
99 return Schema
100 elif schema_name == "RSP":
101 return RspSchema
102 else:
103 raise ValueError(f"Unknown schema name '{schema_name}'")