From 1203a07a61521ce0f3984f3aabfdd2b684ef546f Mon Sep 17 00:00:00 2001 From: kraysent Date: Sun, 9 Aug 2026 21:35:23 +0100 Subject: [PATCH] add spectroscopy catalog --- .schemalintrc.js | 1 + app/adminapi/domain/catalogs.py | 8 + app/data/model/__init__.py | 6 + app/data/model/helpers.py | 6 +- app/data/model/interface.py | 2 + app/data/model/spectroscopy.py | 59 ++++++++ postgres/drafts/17_modify_common_types.sql | 21 --- postgres/drafts/18_add_spectroscopy.sql | 123 --------------- .../migrations/V058__spectroscopy_catalog.sql | 140 ++++++++++++++++++ .../adminapi/integration/catalogs_api_test.py | 26 ++++ tests/regression/upload_simple_table.py | 48 ++++++ 11 files changed, 295 insertions(+), 145 deletions(-) create mode 100644 app/data/model/spectroscopy.py delete mode 100644 postgres/drafts/18_add_spectroscopy.sql create mode 100644 postgres/migrations/V058__spectroscopy_catalog.sql diff --git a/.schemalintrc.js b/.schemalintrc.js index 1a4b8ea4..b3232067 100644 --- a/.schemalintrc.js +++ b/.schemalintrc.js @@ -22,6 +22,7 @@ module.exports = { { name: "private" }, { name: "public" }, { name: "rawdata" }, + { name: "spectroscopy" }, ], rules: { "name-casing": ["error", "snake"], diff --git a/app/adminapi/domain/catalogs.py b/app/adminapi/domain/catalogs.py index c89abe8d..bda53b6e 100644 --- a/app/adminapi/domain/catalogs.py +++ b/app/adminapi/domain/catalogs.py @@ -13,6 +13,14 @@ model.RawCatalog.PHOTOMETRY__TOTAL: ("Photometry (total)", "Total magnitudes per band and method."), model.RawCatalog.PHOTOMETRY__ISOPHOTAL: ("Photometry (isophotal)", "Isophotal magnitudes per band and level."), model.RawCatalog.GEOMETRY: ("Geometry", "Isophotal ellipse geometry."), + model.RawCatalog.SPECTROSCOPY__INTEGRATED_FLUX_DENSITY: ( + "Spectroscopy (integrated flux density)", + "Integrated spectral line flux densities.", + ), + model.RawCatalog.SPECTROSCOPY__ENERGY_FLUX: ( + "Spectroscopy (energy flux)", + "Spectral line energy fluxes.", + ), model.RawCatalog.NOTE: ("Note", "Free-text notes attached to records."), } diff --git a/app/data/model/__init__.py b/app/data/model/__init__.py index a1b62f72..bd09e6d1 100644 --- a/app/data/model/__init__.py +++ b/app/data/model/__init__.py @@ -22,6 +22,10 @@ RedshiftRecord, ) from app.data.model.redshift import RedshiftCatalogObject +from app.data.model.spectroscopy import ( + SpectroscopyEnergyFluxCatalogObject, + SpectroscopyIntegratedFluxDensityCatalogObject, +) from app.data.model.table import ( CatalogProgress, ColumnDescription, @@ -61,6 +65,8 @@ "PhotometryTotalCatalogObject", "PhotometryIsophotalCatalogObject", "GeometryCatalogObject", + "SpectroscopyIntegratedFluxDensityCatalogObject", + "SpectroscopyEnergyFluxCatalogObject", "NoteCatalogObject", "get_catalog_object_type", "Bibliography", diff --git a/app/data/model/helpers.py b/app/data/model/helpers.py index 796ce599..52f45376 100644 --- a/app/data/model/helpers.py +++ b/app/data/model/helpers.py @@ -1,4 +1,4 @@ -from app.data.model import designation, geometry, icrs, interface, nature, note, photometry, redshift +from app.data.model import designation, geometry, icrs, interface, nature, note, photometry, redshift, spectroscopy _CATALOG_OBJECT_TYPES: dict[interface.RawCatalog, type[interface.CatalogObject]] = { interface.RawCatalog.DESIGNATION: designation.DesignationCatalogObject, @@ -8,6 +8,10 @@ interface.RawCatalog.PHOTOMETRY__TOTAL: photometry.PhotometryTotalCatalogObject, interface.RawCatalog.PHOTOMETRY__ISOPHOTAL: photometry.PhotometryIsophotalCatalogObject, interface.RawCatalog.GEOMETRY: geometry.GeometryCatalogObject, + interface.RawCatalog.SPECTROSCOPY__INTEGRATED_FLUX_DENSITY: ( + spectroscopy.SpectroscopyIntegratedFluxDensityCatalogObject + ), + interface.RawCatalog.SPECTROSCOPY__ENERGY_FLUX: spectroscopy.SpectroscopyEnergyFluxCatalogObject, interface.RawCatalog.NOTE: note.NoteCatalogObject, } diff --git a/app/data/model/interface.py b/app/data/model/interface.py index 0467e3f4..391855d3 100644 --- a/app/data/model/interface.py +++ b/app/data/model/interface.py @@ -18,6 +18,8 @@ class RawCatalog(enum.Enum): PHOTOMETRY__TOTAL = "photometry_total" PHOTOMETRY__ISOPHOTAL = "photometry_isophotal" GEOMETRY = "geometry" + SPECTROSCOPY__INTEGRATED_FLUX_DENSITY = "spectroscopy_integrated_flux_density" + SPECTROSCOPY__ENERGY_FLUX = "spectroscopy_energy_flux" NOTE = "note" diff --git a/app/data/model/spectroscopy.py b/app/data/model/spectroscopy.py new file mode 100644 index 00000000..5bfa5479 --- /dev/null +++ b/app/data/model/spectroscopy.py @@ -0,0 +1,59 @@ +from typing import Any, final + +from app.data.model import interface + + +@final +class SpectroscopyIntegratedFluxDensityCatalogObject(interface.CatalogObject): + def __init__( + self, + line_id: str, + flux: float, + e_flux: float | None = None, + method: str = "sum", + quality: str = "regular", + **kwargs: Any, + ) -> None: + self.line_id = line_id + self.flux = flux + self.e_flux = e_flux + self.method = method + self.quality = quality + + def catalog(self) -> interface.RawCatalog: + return interface.RawCatalog.SPECTROSCOPY__INTEGRATED_FLUX_DENSITY + + @classmethod + def layer1_table(cls) -> str: + return "spectroscopy.integrated_flux_density" + + @classmethod + def layer1_primary_keys(cls) -> list[str]: + return ["record_id", "line_id", "method"] + + +@final +class SpectroscopyEnergyFluxCatalogObject(interface.CatalogObject): + def __init__( + self, + line_id: str, + flux: float, + e_flux: float | None = None, + quality: str = "regular", + **kwargs: Any, + ) -> None: + self.line_id = line_id + self.flux = flux + self.e_flux = e_flux + self.quality = quality + + def catalog(self) -> interface.RawCatalog: + return interface.RawCatalog.SPECTROSCOPY__ENERGY_FLUX + + @classmethod + def layer1_table(cls) -> str: + return "spectroscopy.energy_flux" + + @classmethod + def layer1_primary_keys(cls) -> list[str]: + return ["record_id", "line_id"] diff --git a/postgres/drafts/17_modify_common_types.sql b/postgres/drafts/17_modify_common_types.sql index b062648c..ade1e26c 100644 --- a/postgres/drafts/17_modify_common_types.sql +++ b/postgres/drafts/17_modify_common_types.sql @@ -1,26 +1,5 @@ BEGIN; -ALTER TYPE common.quality RENAME VALUE 'ok' TO 'regular' ; -ALTER TYPE common.quality RENAME VALUE 'lowsnr' TO 'low_snr' ; -ALTER TYPE common.quality RENAME VALUE 'sus' TO 'suspected' ; -ALTER TYPE common.quality RENAME VALUE '>' TO 'lower_limit' ; -ALTER TYPE common.quality RENAME VALUE '<' TO 'upper_limit' ; - -ALTER TYPE common.quality RENAME TO qualityType ; - -COMMENT ON TYPE common.QualityType IS '{ -"description": "Quality flag of the measurement", -"values": { - "regular": "regular measurement", - "low_snr": "low signal-to-noise", - "suspected": "suspected measurement", - "lower_limit": "lower limit", - "upper_limit": "upper limit", - "wrong": "wrong measurement" - } -}' ; - - CREATE TYPE common.VelocityConventionType AS ENUM ( 'optical', 'radio', 'relativistic' ) ; COMMENT ON TYPE common.VelocityConventionType IS '{ diff --git a/postgres/drafts/18_add_spectroscopy.sql b/postgres/drafts/18_add_spectroscopy.sql deleted file mode 100644 index 0c4def2d..00000000 --- a/postgres/drafts/18_add_spectroscopy.sql +++ /dev/null @@ -1,123 +0,0 @@ -BEGIN; - ------------- Spectral lines ------------------- -CREATE TYPE common.LineType AS ENUM ( 'atomic', 'molecular', 'recombination', 'forbidden', 'fine-structure', 'hyperfine' ); -COMMENT ON TYPE common.LineType IS '{ -"description": "Classification of spectral lines by their physical origin or transition type", -"values": { - "atomic": "Electronic transitions in neutral atoms or ions", - "molecular": "Rotational or vibrational transitions in molecules", - "recombination": "Recombination transitions of hydrogen- and helium-like atoms", - "forbidden": "Forbidden atomic or ionic transitions", - "fine-structure": "Fine-structure transitions caused by electron spin-orbit interaction", - "hyperfine": "Hyperfine transitions caused by nuclear spin interaction" - } -}' ; - - -CREATE TABLE common.lines ( - id Text PRIMARY KEY -, species Text NOT NULL -, transition Text NOT NULL -, line_type common.LineType NOT NULL -, UNIQUE (species,transition) -); - -COMMENT ON TABLE common.lines IS 'Dictionary of spectral line identifiers' ; -COMMENT ON COLUMN common.lines.id IS 'Line ID' ; -COMMENT ON COLUMN common.lines.species IS 'Atomic, ionic or molecular species' ; -COMMENT ON COLUMN common.lines.transition IS 'Transition' ; -COMMENT ON COLUMN common.lines.line_type IS 'Physical origin or transition type' ; - - -INSERT INTO common.lines (id,species,transition,line_type) VALUES - ('HI', 'H', '21 cm', 'hyperfine' ) - -, ('CO(1-0)', 'CO', 'J=1→0', 'molecular' ) -, ('CO(2-1)', 'CO', 'J=2→1', 'molecular' ) - -, ('OH1612', 'OH', '1612 MHz', 'hyperfine' ) -, ('OH1665', 'OH', '1665 MHz', 'hyperfine' ) -, ('OH1667', 'OH', '1667 MHz', 'hyperfine' ) -, ('OH1720', 'OH', '1720 MHz', 'hyperfine' ) - -, ('Lyalpha', 'H', 'n=2→1', 'recombination') - -, ('Halpha', 'H', 'n=3→2', 'recombination' ) -, ('Hbeta', 'H', 'n=4→2', 'recombination' ) -, ('Hgamma', 'H', 'n=5→2', 'recombination' ) -, ('Hdelta', 'H', 'n=6→2', 'recombination' ) - -, ('[OII]3727', 'OII', '²D→⁴S', 'forbidden') -, ('[OIII]4959', 'OIII', '¹D₂→³P₁', 'forbidden' ) -, ('[OIII]5007', 'OIII', '¹D₂→³P₂', 'forbidden' ) - -, ('[NII]6548', 'NII', '¹D₂→³P₁', 'forbidden' ) -, ('[NII]6583', 'NII', '¹D₂→³P₂', 'forbidden' ) - -, ('[SII]6716', 'SII', '²D₃/₂→⁴S₃/₂', 'forbidden') -, ('[SII]6731', 'SII', '²D₅/₂→⁴S₃/₂', 'forbidden') -; - - ----------------------------------------------------- --------------- Spectroscopy schema ----------------- ----------------------------------------------------- -CREATE SCHEMA IF NOT EXISTS spectroscopy ; -COMMENT ON SCHEMA spectroscopy IS 'Catalog of the spectroscopy observations'; - -CREATE TYPE spectroscopy.FluxMethodType AS ENUM ( 'sum', 'gauss', 'busy' ) ; -COMMENT ON TYPE spectroscopy.FluxMethodType IS '{ -"description": "Method of the integrated line flux measurement", -"values": { - "sum": "Integrated flux density of the line determined by summing all spectral channels", - "gauss": "Line flux approximated by gaussian profile", - "busy": "Profile of the line approximated by busy function" - } -}' ; - - -------------- Line flux --------------------- -CREATE TABLE spectroscopy.integrated_flux_density ( - record_id Text NOT NULL REFERENCES layer0.records(id) ON UPDATE cascade ON DELETE restrict -, line_id Text NOT NULL REFERENCES common.lines(id) ON UPDATE cascade ON DELETE restrict -, flux real NOT NULL -, e_flux real -, method spectroscopy.FluxMethodType NOT NULL DEFAULT 'sum' -, quality common.QualityType NOT NULL DEFAULT 'regular' -, PRIMARY KEY (record_id, line_id, method) -); -CREATE INDEX ON spectroscopy.integrated_flux_density (record_id) ; -CREATE INDEX ON spectroscopy.integrated_flux_density (line_id) ; -CREATE INDEX ON spectroscopy.integrated_flux_density (method) ; -CREATE INDEX ON spectroscopy.integrated_flux_density (quality) ; - -COMMENT ON TABLE spectroscopy.integrated_flux_density IS 'Catalog of the integrated flux densities of the spectral lines' ; -COMMENT ON COLUMN spectroscopy.integrated_flux_density.record_id IS 'Record ID' ; -COMMENT ON COLUMN spectroscopy.integrated_flux_density.line_id IS 'Spectral line ID' ; -COMMENT ON COLUMN spectroscopy.integrated_flux_density.flux IS '{"description":"Integrated flux density", "unit":"Jy.km/s", "ucd":"spect.line;phot.flux.density;arith.sum"}' ; -COMMENT ON COLUMN spectroscopy.integrated_flux_density.e_flux IS '{"description":"Error of the integrated flux density", "unit":"Jy.km/s", "ucd":"stat.error"}' ; -COMMENT ON COLUMN spectroscopy.integrated_flux_density.method IS 'Measurement type (sum, gauss, busy)' ; - - - -CREATE TABLE spectroscopy.energy_flux ( - record_id Text NOT NULL REFERENCES layer0.records(id) ON UPDATE cascade ON DELETE restrict -, line_id Text NOT NULL REFERENCES common.lines(id) ON UPDATE cascade ON DELETE restrict -, flux real NOT NULL -, e_flux real -, quality common.QualityType NOT NULL DEFAULT 'regular' -, PRIMARY KEY (record_id, line_id) -, CHECK (flux > 0) -, CHECK (e_flux IS NULL OR e_flux >= 0) -); -CREATE INDEX ON spectroscopy.energy_flux (line_id) ; -CREATE INDEX ON spectroscopy.energy_flux (quality) ; - -COMMENT ON TABLE spectroscopy.energy_flux IS 'Catalog of spectral line energy fluxes' ; -COMMENT ON COLUMN spectroscopy.energy_flux.record_id IS 'Record ID' ; -COMMENT ON COLUMN spectroscopy.energy_flux.line_id IS 'Spectral line ID' ; -COMMENT ON COLUMN spectroscopy.energy_flux.flux IS '{"description":"Total energy flux in the line", "unit":"erg/cm2/s", "ucd":"spect.line;phot.flux"}' ; -COMMENT ON COLUMN spectroscopy.energy_flux.e_flux IS '{"description":"Error of the total energy flux in the line", "unit":"erg/cm2/s", "ucd":"stat.error"}' ; - -COMMIT; diff --git a/postgres/migrations/V058__spectroscopy_catalog.sql b/postgres/migrations/V058__spectroscopy_catalog.sql new file mode 100644 index 00000000..147933ed --- /dev/null +++ b/postgres/migrations/V058__spectroscopy_catalog.sql @@ -0,0 +1,140 @@ +/* pgmigrate-encoding: utf-8 */ + +ALTER TYPE common.quality RENAME VALUE 'ok' TO 'regular'; +ALTER TYPE common.quality RENAME VALUE 'lowsnr' TO 'low_snr'; +ALTER TYPE common.quality RENAME VALUE 'sus' TO 'suspected'; +ALTER TYPE common.quality RENAME VALUE '>' TO 'lower_limit'; +ALTER TYPE common.quality RENAME VALUE '<' TO 'upper_limit'; +ALTER TYPE common.quality RENAME TO quality_type; + +COMMENT ON TYPE common.quality_type IS '{ + "description": "Quality flag of the measurement", + "values": { + "regular": "regular measurement", + "low_snr": "low signal-to-noise", + "suspected": "suspected measurement", + "lower_limit": "lower limit", + "upper_limit": "upper limit", + "wrong": "wrong measurement" + } +}'; + +CREATE TYPE common.line_type AS ENUM ( + 'atomic', + 'molecular', + 'recombination', + 'forbidden', + 'fine-structure', + 'hyperfine' +); +COMMENT ON TYPE common.line_type IS '{ + "description": "Classification of spectral lines by their physical origin or transition type", + "values": { + "atomic": "Electronic transitions in neutral atoms or ions", + "molecular": "Rotational or vibrational transitions in molecules", + "recombination": "Recombination transitions of hydrogen- and helium-like atoms", + "forbidden": "Forbidden atomic or ionic transitions", + "fine-structure": "Fine-structure transitions caused by electron spin-orbit interaction", + "hyperfine": "Hyperfine transitions caused by nuclear spin interaction" + } +}'; + +CREATE TABLE common.lines ( + id text PRIMARY KEY, + species text NOT NULL, + transition text NOT NULL, + line_type common.line_type NOT NULL, + UNIQUE (species, transition) +); + +SELECT meta.setparams('common', 'lines', '{"description": "Dictionary of spectral line identifiers"}'); +SELECT meta.setparams('common', 'lines', 'id', '{"description": "Line ID"}'); +SELECT meta.setparams('common', 'lines', 'species', '{"description": "Atomic, ionic or molecular species"}'); +SELECT meta.setparams('common', 'lines', 'transition', '{"description": "Transition"}'); +SELECT meta.setparams('common', 'lines', 'line_type', '{"description": "Physical origin or transition type"}'); + +INSERT INTO common.lines (id, species, transition, line_type) VALUES + ('HI', 'H', '21 cm', 'hyperfine') +, ('CO(1-0)', 'CO', 'J=1→0', 'molecular') +, ('CO(2-1)', 'CO', 'J=2→1', 'molecular') +, ('OH1612', 'OH', '1612 MHz', 'hyperfine') +, ('OH1665', 'OH', '1665 MHz', 'hyperfine') +, ('OH1667', 'OH', '1667 MHz', 'hyperfine') +, ('OH1720', 'OH', '1720 MHz', 'hyperfine') +, ('Lyalpha', 'H', 'n=2→1', 'recombination') +, ('Halpha', 'H', 'n=3→2', 'recombination') +, ('Hbeta', 'H', 'n=4→2', 'recombination') +, ('Hgamma', 'H', 'n=5→2', 'recombination') +, ('Hdelta', 'H', 'n=6→2', 'recombination') +, ('[OII]3727', 'OII', '²D→⁴S', 'forbidden') +, ('[OIII]4959', 'OIII', '¹D₂→³P₁', 'forbidden') +, ('[OIII]5007', 'OIII', '¹D₂→³P₂', 'forbidden') +, ('[NII]6548', 'NII', '¹D₂→³P₁', 'forbidden') +, ('[NII]6583', 'NII', '¹D₂→³P₂', 'forbidden') +, ('[SII]6716', 'SII', '²D₃/₂→⁴S₃/₂', 'forbidden') +, ('[SII]6731', 'SII', '²D₅/₂→⁴S₃/₂', 'forbidden') +; + +CREATE SCHEMA IF NOT EXISTS spectroscopy; +SELECT meta.setparams('spectroscopy', '{"description": "Catalog of the spectroscopy observations"}'); + +CREATE TYPE spectroscopy.flux_method_type AS ENUM ('sum', 'gauss', 'busy'); +COMMENT ON TYPE spectroscopy.flux_method_type IS '{ + "description": "Method of the integrated line flux measurement", + "values": { + "sum": "Integrated flux density of the line determined by summing all spectral channels", + "gauss": "Line flux approximated by gaussian profile", + "busy": "Profile of the line approximated by busy function" + } +}'; + +CREATE TABLE spectroscopy.integrated_flux_density ( + record_id text NOT NULL REFERENCES layer0.records(id) ON UPDATE CASCADE ON DELETE RESTRICT, + line_id text NOT NULL REFERENCES common.lines(id) ON UPDATE CASCADE ON DELETE RESTRICT, + flux real NOT NULL, + e_flux real CHECK (e_flux IS NULL OR e_flux >= 0), + method spectroscopy.flux_method_type NOT NULL DEFAULT 'sum', + quality common.quality_type NOT NULL DEFAULT 'regular', + PRIMARY KEY (record_id, line_id, method) +); +CREATE INDEX ON spectroscopy.integrated_flux_density (record_id); +CREATE INDEX ON spectroscopy.integrated_flux_density (line_id); +CREATE INDEX ON spectroscopy.integrated_flux_density (method); + +SELECT meta.setparams('spectroscopy', 'integrated_flux_density', '{"description": "Catalog of the integrated flux densities of the spectral lines"}'); +SELECT meta.setparams('spectroscopy', 'integrated_flux_density', 'record_id', '{"description": "Record ID"}'); +SELECT meta.setparams('spectroscopy', 'integrated_flux_density', 'line_id', '{"description": "Spectral line ID"}'); +SELECT meta.setparams('spectroscopy', 'integrated_flux_density', 'flux', '{"description": "Integrated flux density", "unit": "Jy.km/s", "ucd": "spect.line;phot.flux.density;arith.sum"}'); +SELECT meta.setparams('spectroscopy', 'integrated_flux_density', 'e_flux', '{"description": "Error of the integrated flux density", "unit": "Jy.km/s", "ucd": "stat.error"}'); +SELECT meta.setparams('spectroscopy', 'integrated_flux_density', 'method', '{"description": "Measurement type (sum, gauss, busy)"}'); +SELECT meta.setparams('spectroscopy', 'integrated_flux_density', 'quality', '{"description": "Quality flag of the measurement"}'); + +CREATE TABLE spectroscopy.energy_flux ( + record_id text NOT NULL REFERENCES layer0.records(id) ON UPDATE CASCADE ON DELETE RESTRICT, + line_id text NOT NULL REFERENCES common.lines(id) ON UPDATE CASCADE ON DELETE RESTRICT, + flux real NOT NULL CHECK (flux > 0), + e_flux real CHECK (e_flux IS NULL OR e_flux >= 0), + quality common.quality_type NOT NULL DEFAULT 'regular', + PRIMARY KEY (record_id, line_id) +); +CREATE INDEX ON spectroscopy.energy_flux (record_id); +CREATE INDEX ON spectroscopy.energy_flux (line_id); + +SELECT meta.setparams('spectroscopy', 'energy_flux', '{"description": "Catalog of spectral line energy fluxes"}'); +SELECT meta.setparams('spectroscopy', 'energy_flux', 'record_id', '{"description": "Record ID"}'); +SELECT meta.setparams('spectroscopy', 'energy_flux', 'line_id', '{"description": "Spectral line ID"}'); +SELECT meta.setparams('spectroscopy', 'energy_flux', 'flux', '{"description": "Total energy flux in the line", "unit": "erg/cm2/s", "ucd": "spect.line;phot.flux"}'); +SELECT meta.setparams('spectroscopy', 'energy_flux', 'e_flux', '{"description": "Error of the total energy flux in the line", "unit": "erg/cm2/s", "ucd": "stat.error"}'); +SELECT meta.setparams('spectroscopy', 'energy_flux', 'quality', '{"description": "Quality flag of the measurement"}'); + +GRANT USAGE ON SCHEMA spectroscopy TO db_reader; +GRANT SELECT ON ALL TABLES IN SCHEMA spectroscopy TO db_reader; +ALTER DEFAULT PRIVILEGES IN SCHEMA spectroscopy GRANT SELECT ON TABLES TO db_reader; + +GRANT USAGE ON SCHEMA spectroscopy TO db_writer; +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA spectroscopy TO db_writer; +ALTER DEFAULT PRIVILEGES IN SCHEMA spectroscopy GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO db_writer; + +GRANT USAGE ON SCHEMA spectroscopy TO db_private_reader; +GRANT SELECT ON ALL TABLES IN SCHEMA spectroscopy TO db_private_reader; +ALTER DEFAULT PRIVILEGES IN SCHEMA spectroscopy GRANT SELECT ON TABLES TO db_private_reader; diff --git a/tests/adminapi/integration/catalogs_api_test.py b/tests/adminapi/integration/catalogs_api_test.py index a3115d5b..3d8cf76e 100644 --- a/tests/adminapi/integration/catalogs_api_test.py +++ b/tests/adminapi/integration/catalogs_api_test.py @@ -79,6 +79,32 @@ def test_get_catalogs_geometry(self) -> None: self.assertEqual(fields["isophote"]["unit"], "mag/arcmin2") self.assertEqual(fields["method"]["data_type"], "str") + def test_get_catalogs_spectroscopy_integrated_flux_density(self) -> None: + catalogs = self._catalogs_by_name() + catalog = catalogs["spectroscopy_integrated_flux_density"] + fields = {f["name"]: f for f in catalog["fields"]} + self.assertEqual(set(fields), {"line_id", "flux", "e_flux", "method", "quality"}) + self.assertTrue(fields["line_id"]["required"]) + self.assertTrue(fields["flux"]["required"]) + self.assertFalse(fields["e_flux"]["required"]) + self.assertTrue(fields["method"]["required"]) + self.assertTrue(fields["quality"]["required"]) + self.assertEqual(fields["flux"]["unit"], "Jy.km/s") + self.assertEqual(fields["e_flux"]["unit"], "Jy.km/s") + self.assertEqual(fields["method"]["data_type"], "str") + + def test_get_catalogs_spectroscopy_energy_flux(self) -> None: + catalogs = self._catalogs_by_name() + catalog = catalogs["spectroscopy_energy_flux"] + fields = {f["name"]: f for f in catalog["fields"]} + self.assertEqual(set(fields), {"line_id", "flux", "e_flux", "quality"}) + self.assertTrue(fields["line_id"]["required"]) + self.assertTrue(fields["flux"]["required"]) + self.assertFalse(fields["e_flux"]["required"]) + self.assertTrue(fields["quality"]["required"]) + self.assertEqual(fields["flux"]["unit"], "erg/cm2/s") + self.assertEqual(fields["e_flux"]["unit"], "erg/cm2/s") + def test_runtime_catalogs_excluded(self) -> None: catalogs = self._catalogs_by_name() self.assertNotIn("additional_designations", catalogs) diff --git a/tests/regression/upload_simple_table.py b/tests/regression/upload_simple_table.py index 3b29b6f3..69722457 100644 --- a/tests/regression/upload_simple_table.py +++ b/tests/regression/upload_simple_table.py @@ -27,6 +27,8 @@ PHOTOMETRY_BANDS = ["V", "B", "R"] PHOTOMETRY_METHOD = "psf" +SPECTROSCOPY_LINES = ["HI", "Halpha"] +SPECTROSCOPY_FLUX_METHOD = "sum" SIMPLE_FILTER_QUERY_CATALOGS = ["designation", "icrs", "redshift", "nature"] @@ -135,6 +137,8 @@ def upload_structured_data(session: requests.Session, records: list[dict]) -> No upload_photometry_catalog(session, ids=ids) upload_photometry_isophotal_catalog(session, ids=ids) upload_geometry_catalog(session, ids=ids) + upload_spectroscopy_integrated_flux_density_catalog(session, ids=ids) + upload_spectroscopy_energy_flux_catalog(session, ids=ids) @lib.test_logging_decorator @@ -241,6 +245,50 @@ def upload_geometry_catalog(session: requests.Session, ids: list[str]) -> None: response.raise_for_status() +@lib.test_logging_decorator +def upload_spectroscopy_integrated_flux_density_catalog(session: requests.Session, ids: list[str]) -> None: + flux_ids: list[str] = [] + flux_data: list[list[object]] = [] + for rid in ids: + for line_id in SPECTROSCOPY_LINES: + flux_ids.append(rid) + flux = random.uniform(0.1, 50.0) + e_flux = random.uniform(0.01, 0.5) + flux_data.append([line_id, flux, e_flux, SPECTROSCOPY_FLUX_METHOD, "regular"]) + + request = adminapi.SaveStructuredDataRequest( + catalog="spectroscopy_integrated_flux_density", + columns=["line_id", "flux", "e_flux", "method", "quality"], + units={"flux": "Jy.km/s", "e_flux": "Jy.km/s"}, + ids=flux_ids, + data=flux_data, + ) + response = session.post("/v1/data/structured", json=request.model_dump(mode="json")) + response.raise_for_status() + + +@lib.test_logging_decorator +def upload_spectroscopy_energy_flux_catalog(session: requests.Session, ids: list[str]) -> None: + flux_ids: list[str] = [] + flux_data: list[list[object]] = [] + for rid in ids: + for line_id in SPECTROSCOPY_LINES: + flux_ids.append(rid) + flux = random.uniform(1e-17, 1e-14) + e_flux = random.uniform(1e-19, 1e-16) + flux_data.append([line_id, flux, e_flux, "regular"]) + + request = adminapi.SaveStructuredDataRequest( + catalog="spectroscopy_energy_flux", + columns=["line_id", "flux", "e_flux", "quality"], + units={"flux": "erg/cm2/s", "e_flux": "erg/cm2/s"}, + ids=flux_ids, + data=flux_data, + ) + response = session.post("/v1/data/structured", json=request.model_dump(mode="json")) + response.raise_for_status() + + @lib.test_logging_decorator def upload_notes_catalog(session: requests.Session, ids: list[str], original_data: list[dict]) -> None: notes_request = adminapi.SaveStructuredDataRequest(