Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-46975: Read band information from obscore config and fix self-description doc #23

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions changelog.d/20241216_192656_steliosvoutsinas_DM_46975.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!-- Delete the sections that don't apply -->

### New features

- Now reads bands information from obscore config
- Fix some of the self-descirption documentation (calibs and remove specify statements)
43 changes: 43 additions & 0 deletions src/sia/models/sia_query_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ..models.common import CaseInsensitiveEnum

__all__ = [
"BandInfo",
"BaseQueryParams",
"CalibLevel",
"DPType",
Expand Down Expand Up @@ -66,6 +67,48 @@ class Polarization(CaseInsensitiveEnum):
YX = "YX"


@dataclass
class BandInfo:
"""A class to represent a band's wavelength range.

Attributes
----------
label
The band's label.
low
The low end of the band's wavelength range.
high
The high end of the band's wavelength range.
"""

label: str
low: float
high: float

@property
def midpoint(self) -> float:
"""Calculate the midpoint of the band's wavelength range.

Returns
-------
float
The midpoint of the band's wavelength range
"""
return (self.low + self.high) / 2

@property
def formatted_midpoint(self) -> str:
"""Return the midpoint formatted in scientific notation.

Returns
-------
str
The midpoint formatted in scientific notation
"""
nm_value = self.midpoint * 1e9
return f"{nm_value:.1f}e-9"


class BaseQueryParams(ABC):
"""Base class for query parameters."""

Expand Down
34 changes: 34 additions & 0 deletions src/sia/services/response_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from ..constants import RESULT_NAME as RESULT
from ..factory import Factory
from ..models.data_collections import ButlerDataCollection
from ..models.sia_query_params import BandInfo
from ..services.votable import VotableConverterService

logger = structlog.get_logger(__name__)
Expand All @@ -32,6 +33,34 @@
class ResponseHandlerService:
"""Service for handling the SIAv2 query response."""

@staticmethod
def _generate_band_info(
spectral_ranges: dict[str, tuple[float | None, float | None]],
) -> list[BandInfo]:
"""Generate band information from spectral ranges dictionary.

Parameters
----------
spectral_ranges
The spectral ranges dictionary.

Returns
-------
list[BandInfo]
The list of BandInfo objects.
"""
bands = []
for band_name, (low, high) in spectral_ranges.items():
if low is not None and high is not None:
# The Rubin label is hardcoded here, but it could be
# parameterized if needed in the future.
bands.append(
BandInfo(
label=f"Rubin band {band_name}", low=low, high=high
)
)
return bands

@staticmethod
def self_description_response(
request: Request,
Expand Down Expand Up @@ -59,6 +88,10 @@ def self_description_response(
Response
The response containing the self-description.
"""
bands = ResponseHandlerService._generate_band_info(
obscore_config.spectral_ranges
)

return _TEMPLATES.TemplateResponse(
request,
"self_description.xml",
Expand All @@ -77,6 +110,7 @@ def self_description_response(
"query", collection_name=butler_collection.name
),
"facility_name": obscore_config.facility_name.strip(),
"bands": bands,
},
headers={
"content-disposition": f"attachment; filename={RESULT}.xml",
Expand Down
22 changes: 12 additions & 10 deletions src/sia/templates/self_description.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@
<RESOURCE type="meta" utype="adhoc:this">
<DESCRIPTION>Self description and list of supported parameters</DESCRIPTION>
<GROUP name="inputParams">
<PARAM name="BAND" datatype="double" arraysize="2" unit="m" xtype="interval">
<DESCRIPTION>Energy bounds</DESCRIPTION>
<PARAM name="BAND" datatype="float" arraysize="2*" xtype="interval">
<DESCRIPTION>Wavelength/filter band selection</DESCRIPTION>
{%- for band in bands %}
<OPTION name="{{ band.label }}" value="{{ band.formatted_midpoint }}"/>
{%- endfor %}
</PARAM>
<PARAM name="CALIB" datatype="int" arraysize="*">
<DESCRIPTION>Calibration level</DESCRIPTION>
<VALUES type="actual">
<OPTION value="0"/>
<OPTION value="1"/>
<OPTION value="2"/>
<OPTION value="3"/>
<OPTION value="1" name="Raw Data"/>
<OPTION value="2" name="PVIs"/>
<OPTION value="3" name="Coadds and Difference Images"/>
</VALUES>
</PARAM>
<PARAM name="COLLECTION" datatype="char" arraysize="*">
Expand Down Expand Up @@ -81,10 +83,10 @@
</VALUES>
</PARAM>
<PARAM name="SPATRES" datatype="double" arraysize="2" unit="arcsec" xtype="interval">
<DESCRIPTION>Specify position resolution</DESCRIPTION>
<DESCRIPTION>Position resolution</DESCRIPTION>
</PARAM>
<PARAM name="SPECRP" datatype="double" arraysize="2" xtype="interval">
<DESCRIPTION>Specify energy resolving power</DESCRIPTION>
<DESCRIPTION>Energy resolving power</DESCRIPTION>
</PARAM>
<PARAM name="TARGET" datatype="char" arraysize="*">
<DESCRIPTION>Target name</DESCRIPTION>
Expand Down Expand Up @@ -121,7 +123,7 @@
<DESCRIPTION>Product type (e.g. science, calibration, auxiliary, preview, info)</DESCRIPTION>
</FIELD>
<FIELD name="calib_level" datatype="short" ucd="meta.code;obs.calib" utype="obscore:obsdataset.caliblevel">
<DESCRIPTION>Calibration level of the observation: in {0, 1, 2, 3, 4}</DESCRIPTION>
<DESCRIPTION>Calibration level of the observation: in {1, 2, 3}</DESCRIPTION>
</FIELD>
<FIELD name="dataproduct_type" datatype="char" arraysize="*" ucd="meta.id" utype="obscore:obsdataset.dataproducttype">
<DESCRIPTION>Data product (file content) primary type</DESCRIPTION>
Expand Down Expand Up @@ -198,4 +200,4 @@
</DATA>
</TABLE>
</RESOURCE>
</VOTABLE>
</VOTABLE>
18 changes: 18 additions & 0 deletions tests/handlers/external/external_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from sia.config import config
from sia.constants import RESULT_NAME
from sia.models.sia_query_params import BandInfo
from tests.support.butler import MockButler, MockButlerQueryService
from tests.support.constants import EXCEPTION_MESSAGES
from tests.support.validators import validate_votable_error
Expand Down Expand Up @@ -240,12 +241,29 @@ async def test_query_maxrec_zero(
)
templates_dir = Jinja2Templates(template_dir)

bands = [
BandInfo(label="Rubin band HSC-G", low=406.0e-9, high=545.0e-9),
BandInfo(label="Rubin band HSC-R", low=543.0e-9, high=693.0e-9),
BandInfo(label="Rubin band HSC-R2", low=542.0e-9, high=693.0e-9),
BandInfo(label="Rubin band HSC-I", low=690.0e-9, high=842.0e-9),
BandInfo(label="Rubin band HSC-I2", low=692.0e-9, high=850.0e-9),
BandInfo(label="Rubin band HSC-Z", low=852.0e-9, high=928.0e-9),
BandInfo(label="Rubin band HSC-Y", low=937.0e-9, high=1015.0e-9),
BandInfo(label="Rubin band N921", low=914.7e-9, high=928.1e-9),
BandInfo(label="Rubin band g", low=406.0e-9, high=545.0e-9),
BandInfo(label="Rubin band r", low=542.0e-9, high=693.0e-9),
BandInfo(label="Rubin band i", low=692.0e-9, high=850.0e-9),
BandInfo(label="Rubin band z", low=852.0e-9, high=928.0e-9),
BandInfo(label="Rubin band y", low=937.0e-9, high=1015.0e-9),
]

context = {
"instruments": ["HSC"],
"collections": ["LSST.CI"],
"resource_identifier": "ivo://rubin//ci_hsc_gen3",
"access_url": "https://example.com/api/sia/hsc/query",
"facility_name": "Subaru",
"bands": bands,
}

template_rendered = templates_dir.get_template(
Expand Down
59 changes: 59 additions & 0 deletions tests/models/sia_params_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from sia.models.common import CaseInsensitiveEnum
from sia.models.sia_query_params import (
BandInfo,
CalibLevel,
DPType,
Polarization,
Expand Down Expand Up @@ -135,6 +136,64 @@ async def test_sia_params_default_values() -> None:
assert params.responseformat is None


def test_band_info_initialization() -> None:
"""Test proper initialization of BandInfo."""
band = BandInfo(label="Rubin band u", low=330.0e-9, high=400.0e-9)
assert band.label == "Rubin band u"
assert band.low == 330.0e-9
assert band.high == 400.0e-9


def test_band_info_midpoint_calculation() -> None:
"""Test midpoint calculations for different bands."""
band_u = BandInfo(label="Rubin band u", low=330.0e-9, high=400.0e-9)
expected_u_midpoint = (330.0e-9 + 400.0e-9) / 2
assert band_u.midpoint == expected_u_midpoint

band_y = BandInfo(label="Rubin band y", low=970.0e-9, high=1060.0e-9)
expected_y_midpoint = (970.0e-9 + 1060.0e-9) / 2
assert band_y.midpoint == expected_y_midpoint


def test_band_info_formatted_midpoint() -> None:
"""Test formatted midpoint string representations."""
test_cases = [
{
"label": "Rubin band u",
"low": 330.0e-9,
"high": 400.0e-9,
"expected": "365.0e-9",
},
{
"label": "Rubin band g",
"low": 402.0e-9,
"high": 552.0e-9,
"expected": "477.0e-9",
},
{
"label": "Rubin band y",
"low": 970.0e-9,
"high": 1060.0e-9,
"expected": "1015.0e-9",
},
]

for case in test_cases:
low = (
float(case["low"])
if isinstance(case["low"], (int | float))
else 0.0
)
high = (
float(case["high"])
if isinstance(case["high"], (int | float))
else 0.0
)

band = BandInfo(label=str(case["label"]), low=low, high=high)
assert band.formatted_midpoint == case["expected"]


@pytest.fixture
def sample_sia_params() -> SIAQueryParams:
return SIAQueryParams(
Expand Down
22 changes: 12 additions & 10 deletions tests/templates/self_description.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@
<RESOURCE type="meta" utype="adhoc:this">
<DESCRIPTION>Self description and list of supported parameters</DESCRIPTION>
<GROUP name="inputParams">
<PARAM name="BAND" datatype="double" arraysize="2" unit="m" xtype="interval">
<DESCRIPTION>Energy bounds</DESCRIPTION>
<PARAM name="BAND" datatype="float" arraysize="2*" xtype="interval">
<DESCRIPTION>Wavelength/filter band selection</DESCRIPTION>
{%- for band in bands %}
<OPTION name="{{ band.label }}" value="{{ band.formatted_midpoint }}"/>
{%- endfor %}
</PARAM>
<PARAM name="CALIB" datatype="int" arraysize="*">
<DESCRIPTION>Calibration level</DESCRIPTION>
<VALUES type="actual">
<OPTION value="0"/>
<OPTION value="1"/>
<OPTION value="2"/>
<OPTION value="3"/>
<OPTION value="1" name="Raw Data"/>
<OPTION value="2" name="PVIs"/>
<OPTION value="3" name="Coadds and Difference Images"/>
</VALUES>
</PARAM>
<PARAM name="COLLECTION" datatype="char" arraysize="*">
Expand Down Expand Up @@ -81,10 +83,10 @@
</VALUES>
</PARAM>
<PARAM name="SPATRES" datatype="double" arraysize="2" unit="arcsec" xtype="interval">
<DESCRIPTION>Specify position resolution</DESCRIPTION>
<DESCRIPTION>Position resolution</DESCRIPTION>
</PARAM>
<PARAM name="SPECRP" datatype="double" arraysize="2" xtype="interval">
<DESCRIPTION>Specify energy resolving power</DESCRIPTION>
<DESCRIPTION>Energy resolving power</DESCRIPTION>
</PARAM>
<PARAM name="TARGET" datatype="char" arraysize="*">
<DESCRIPTION>Target name</DESCRIPTION>
Expand Down Expand Up @@ -121,7 +123,7 @@
<DESCRIPTION>Product type (e.g. science, calibration, auxiliary, preview, info)</DESCRIPTION>
</FIELD>
<FIELD name="calib_level" datatype="short" ucd="meta.code;obs.calib" utype="obscore:obsdataset.caliblevel">
<DESCRIPTION>Calibration level of the observation: in {0, 1, 2, 3, 4}</DESCRIPTION>
<DESCRIPTION>Calibration level of the observation: in {1, 2, 3}</DESCRIPTION>
</FIELD>
<FIELD name="dataproduct_type" datatype="char" arraysize="*" ucd="meta.id" utype="obscore:obsdataset.dataproducttype">
<DESCRIPTION>Data product (file content) primary type</DESCRIPTION>
Expand Down Expand Up @@ -198,4 +200,4 @@
</DATA>
</TABLE>
</RESOURCE>
</VOTABLE>
</VOTABLE>
Loading