From 1a68b73ef16ab5caeddcbff08a7dfdfdcd1fcc2e Mon Sep 17 00:00:00 2001 From: kraysent Date: Sat, 29 Aug 2026 19:05:41 +0100 Subject: [PATCH 1/2] Add geometry to layer 2 --- app/dataapi/domain/parameterized_query.py | 1 + app/dataapi/model.py | 25 +++++++++++ app/dataapi/repository/repository.py | 42 +++++++++++++++++++ .../responders/structured_responder.py | 28 +++++++++++++ app/specs/dataapi/interface.py | 18 ++++++++ .../V072__photometry_ellipse_layer2_view.sql | 25 +++++++++++ 6 files changed, 139 insertions(+) create mode 100644 postgres/migrations/V072__photometry_ellipse_layer2_view.sql diff --git a/app/dataapi/domain/parameterized_query.py b/app/dataapi/domain/parameterized_query.py index 856755bd..b0506998 100644 --- a/app/dataapi/domain/parameterized_query.py +++ b/app/dataapi/domain/parameterized_query.py @@ -15,6 +15,7 @@ catalogs.RawCatalog.NATURE, catalogs.RawCatalog.NOTE, catalogs.RawCatalog.PHOTOMETRY__TOTAL, + catalogs.RawCatalog.GEOMETRY, ] diff --git a/app/dataapi/model.py b/app/dataapi/model.py index e8668aa7..18627249 100644 --- a/app/dataapi/model.py +++ b/app/dataapi/model.py @@ -72,6 +72,28 @@ class PhotometryTotalCatalog: measurements: list[PhotometryTotalMeasurement] +@dataclass +class GeometryMeasurement: + band: str + method: str + level: float | None + a: float | None + e_a: float | None + b: float | None + e_b: float | None + pa: float | None + e_pa: float | None + isophote: float | None + e_isophote: float | None + quality: str + source: Source + + +@dataclass +class GeometryCatalog: + measurements: list[GeometryMeasurement] + + @dataclass class Catalogs: designation: DesignationCatalog | None = None @@ -81,6 +103,7 @@ class Catalogs: nature: NatureCatalog | None = None notes: NotesCatalog | None = None photometry_total: PhotometryTotalCatalog | None = None + geometry: GeometryCatalog | None = None @dataclass @@ -94,6 +117,8 @@ class Layer2Object: "AdditionalDesignationsCatalog", "Catalogs", "DesignationCatalog", + "GeometryCatalog", + "GeometryMeasurement", "ICRSCatalog", "Layer2Object", "NatureCatalog", diff --git a/app/dataapi/repository/repository.py b/app/dataapi/repository/repository.py index 87874e5d..2c8dd9c5 100644 --- a/app/dataapi/repository/repository.py +++ b/app/dataapi/repository/repository.py @@ -219,6 +219,7 @@ def query_pgc( nature_task: concurrency.TaskResult[dict[int, model.NatureCatalog]] | None = None notes_task: concurrency.TaskResult[dict[int, model.NotesCatalog]] | None = None photometry_total_task: concurrency.TaskResult[dict[int, model.PhotometryTotalCatalog]] | None = None + geometry_task: concurrency.TaskResult[dict[int, model.GeometryCatalog]] | None = None if catalogs.RawCatalog.DESIGNATION in raw_catalogs: designation_task = errgr.run(self._query_designations, pgcs_page) @@ -234,6 +235,8 @@ def query_pgc( notes_task = errgr.run(self._query_notes, pgcs_page) if catalogs.RawCatalog.PHOTOMETRY__TOTAL in raw_catalogs: photometry_total_task = errgr.run(self._query_photometry_total, pgcs_page) + if catalogs.RawCatalog.GEOMETRY in raw_catalogs: + geometry_task = errgr.run(self._query_geometry, pgcs_page) errgr.wait() @@ -246,6 +249,7 @@ def query_pgc( nature_map = nature_task.result() if nature_task is not None else {} notes_map = notes_task.result() if notes_task is not None else {} photometry_total_map = photometry_total_task.result() if photometry_total_task is not None else {} + geometry_map = geometry_task.result() if geometry_task is not None else {} return [ _layer2_object_from_maps( @@ -258,6 +262,7 @@ def query_pgc( nature_map, notes_map, photometry_total_map, + geometry_map, ) for pgc in pgcs_page ] @@ -404,6 +409,40 @@ def _query_photometry_total(self, pgcs: list[int]) -> dict[int, model.Photometry result.setdefault(pgc, []).append(measurement) return {pgc: model.PhotometryTotalCatalog(measurements=measurements) for pgc, measurements in result.items()} + def _query_geometry(self, pgcs: list[int]) -> dict[int, model.GeometryCatalog]: + if not pgcs: + return {} + rows = self._storage.query( + """ + SELECT pgc, band, method, level, a, e_a, b, e_b, pa, e_pa, isophote, e_isophote, quality, + code, year, author, title + FROM layer2.photometry_ellipse + WHERE pgc = ANY(%s) + ORDER BY pgc, band, method + """, + params=[pgcs], + ) + result: dict[int, list[model.GeometryMeasurement]] = {} + for row in rows: + pgc = int(row["pgc"]) + measurement = model.GeometryMeasurement( + band=str(row["band"]), + method=str(row["method"]), + level=float(row["level"]) if row.get("level") is not None else None, + a=float(row["a"]) if row.get("a") is not None else None, + e_a=float(row["e_a"]) if row.get("e_a") is not None else None, + b=float(row["b"]) if row.get("b") is not None else None, + e_b=float(row["e_b"]) if row.get("e_b") is not None else None, + pa=float(row["pa"]) if row.get("pa") is not None else None, + e_pa=float(row["e_pa"]) if row.get("e_pa") is not None else None, + isophote=float(row["isophote"]) if row.get("isophote") is not None else None, + e_isophote=float(row["e_isophote"]) if row.get("e_isophote") is not None else None, + quality=str(row["quality"]), + source=_source_from_row(row), + ) + result.setdefault(pgc, []).append(measurement) + return {pgc: model.GeometryCatalog(measurements=measurements) for pgc, measurements in result.items()} + def _driving_table(search_types: Mapping[str, repofilters.Filter]) -> str | None: tables: set[str] = set() @@ -548,6 +587,7 @@ def _layer2_object_from_maps( nature_map: dict[int, model.NatureCatalog], notes_map: dict[int, model.NotesCatalog], photometry_total_map: dict[int, model.PhotometryTotalCatalog], + geometry_map: dict[int, model.GeometryCatalog], ) -> model.Layer2Object: designation = designation_map.get(pgc) if catalogs.RawCatalog.DESIGNATION in raw_catalogs else None additional_designations = ( @@ -558,6 +598,7 @@ def _layer2_object_from_maps( nature = nature_map.get(pgc) if catalogs.RawCatalog.NATURE in raw_catalogs else None notes = notes_map.get(pgc) if catalogs.RawCatalog.NOTE in raw_catalogs else None photometry_total = photometry_total_map.get(pgc) if catalogs.RawCatalog.PHOTOMETRY__TOTAL in raw_catalogs else None + geometry = geometry_map.get(pgc) if catalogs.RawCatalog.GEOMETRY in raw_catalogs else None return model.Layer2Object( pgc=pgc, @@ -569,6 +610,7 @@ def _layer2_object_from_maps( nature=nature, notes=notes, photometry_total=photometry_total, + geometry=geometry, ), ) diff --git a/app/dataapi/responders/structured_responder.py b/app/dataapi/responders/structured_responder.py index 09dd8fff..9c07dfde 100644 --- a/app/dataapi/responders/structured_responder.py +++ b/app/dataapi/responders/structured_responder.py @@ -90,6 +90,29 @@ def _photometry_total_measurement(measurement: model.PhotometryTotalMeasurement) ) +def _geometry_measurement(measurement: model.GeometryMeasurement) -> spec.GeometryMeasurement: + return spec.GeometryMeasurement( + band=measurement.band, + method=measurement.method, + level=measurement.level, + a=measurement.a, + e_a=measurement.e_a, + b=measurement.b, + e_b=measurement.e_b, + pa=measurement.pa, + e_pa=measurement.e_pa, + isophote=measurement.isophote, + e_isophote=measurement.e_isophote, + quality=measurement.quality, + source=spec.Source( + bibcode=measurement.source.bibcode, + title=measurement.source.title, + authors=measurement.source.authors, + year=measurement.source.year, + ), + ) + + class StructuredResponder(interface.ObjectResponder): def __init__(self, cfg: CatalogConfig, reddening_service: reddening.Reddening) -> None: self.config = cfg @@ -275,6 +298,11 @@ def build_response(self, objects: list[model.Layer2Object]) -> Any: for measurement in obj.catalogs.photometry_total.measurements ] + if obj.catalogs.geometry is not None: + catalogs.geometry = [ + _geometry_measurement(measurement) for measurement in obj.catalogs.geometry.measurements + ] + if corrected_photometry := corrected_photometry_by_pgc.get(obj.pgc): catalogs.photometry_total_corrected = corrected_photometry diff --git a/app/specs/dataapi/interface.py b/app/specs/dataapi/interface.py index 1c0a190a..cec6e340 100644 --- a/app/specs/dataapi/interface.py +++ b/app/specs/dataapi/interface.py @@ -21,6 +21,7 @@ "EquatorialCoordinatesUnits", "GalacticCoordinates", "GalacticCoordinatesUnits", + "GeometryMeasurement", "J2000Coordinate", "ListReddeningReferencesResponse", "Nature", @@ -131,6 +132,22 @@ class PhotometryTotalMeasurement(pydantic.BaseModel): e_mag: float | None +class GeometryMeasurement(pydantic.BaseModel): + band: str + method: str + level: float | None + a: float | None + e_a: float | None + b: float | None + e_b: float | None + pa: float | None + e_pa: float | None + isophote: float | None + e_isophote: float | None + quality: str + source: Source + + class Nature(pydantic.BaseModel): type_name: str @@ -145,6 +162,7 @@ class Catalogs(pydantic.BaseModel): notes: list[NoteEntry] | None = None photometry_total: list[PhotometryTotalMeasurement] | None = None photometry_total_corrected: list[PhotometryTotalMeasurement] | None = None + geometry: list[GeometryMeasurement] | None = None class PGCObject(pydantic.BaseModel): diff --git a/postgres/migrations/V072__photometry_ellipse_layer2_view.sql b/postgres/migrations/V072__photometry_ellipse_layer2_view.sql new file mode 100644 index 00000000..c7057a3e --- /dev/null +++ b/postgres/migrations/V072__photometry_ellipse_layer2_view.sql @@ -0,0 +1,25 @@ +CREATE VIEW layer2.photometry_ellipse AS +SELECT + r.pgc +, e.band +, e.method +, e.level +, e.a +, e.e_a +, e.b +, e.e_b +, e.pa +, e.e_pa +, e.isophote +, e.e_isophote +, e.quality +, t.bib +, b.code +, b.year +, b.author +, b.title +FROM photometry.ellipse AS e + JOIN layer0.records AS r ON e.record_id = r.id + LEFT JOIN layer0.tables AS t ON r.table_id = t.id + LEFT JOIN common.bib AS b ON t.bib = b.id +WHERE r.pgc IS NOT NULL; From 9d15c648e6597daa6d7af5e2e50049b0c76398a0 Mon Sep 17 00:00:00 2001 From: kraysent Date: Sat, 29 Aug 2026 19:11:32 +0100 Subject: [PATCH 2/2] remove quality --- app/dataapi/model.py | 1 - app/dataapi/repository/repository.py | 3 +-- app/dataapi/responders/structured_responder.py | 1 - app/specs/dataapi/interface.py | 1 - postgres/migrations/V072__photometry_ellipse_layer2_view.sql | 1 - 5 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app/dataapi/model.py b/app/dataapi/model.py index 18627249..a87558cc 100644 --- a/app/dataapi/model.py +++ b/app/dataapi/model.py @@ -85,7 +85,6 @@ class GeometryMeasurement: e_pa: float | None isophote: float | None e_isophote: float | None - quality: str source: Source diff --git a/app/dataapi/repository/repository.py b/app/dataapi/repository/repository.py index 2c8dd9c5..c748d320 100644 --- a/app/dataapi/repository/repository.py +++ b/app/dataapi/repository/repository.py @@ -414,7 +414,7 @@ def _query_geometry(self, pgcs: list[int]) -> dict[int, model.GeometryCatalog]: return {} rows = self._storage.query( """ - SELECT pgc, band, method, level, a, e_a, b, e_b, pa, e_pa, isophote, e_isophote, quality, + SELECT pgc, band, method, level, a, e_a, b, e_b, pa, e_pa, isophote, e_isophote, code, year, author, title FROM layer2.photometry_ellipse WHERE pgc = ANY(%s) @@ -437,7 +437,6 @@ def _query_geometry(self, pgcs: list[int]) -> dict[int, model.GeometryCatalog]: e_pa=float(row["e_pa"]) if row.get("e_pa") is not None else None, isophote=float(row["isophote"]) if row.get("isophote") is not None else None, e_isophote=float(row["e_isophote"]) if row.get("e_isophote") is not None else None, - quality=str(row["quality"]), source=_source_from_row(row), ) result.setdefault(pgc, []).append(measurement) diff --git a/app/dataapi/responders/structured_responder.py b/app/dataapi/responders/structured_responder.py index 9c07dfde..79a41d20 100644 --- a/app/dataapi/responders/structured_responder.py +++ b/app/dataapi/responders/structured_responder.py @@ -103,7 +103,6 @@ def _geometry_measurement(measurement: model.GeometryMeasurement) -> spec.Geomet e_pa=measurement.e_pa, isophote=measurement.isophote, e_isophote=measurement.e_isophote, - quality=measurement.quality, source=spec.Source( bibcode=measurement.source.bibcode, title=measurement.source.title, diff --git a/app/specs/dataapi/interface.py b/app/specs/dataapi/interface.py index cec6e340..5cd22ab7 100644 --- a/app/specs/dataapi/interface.py +++ b/app/specs/dataapi/interface.py @@ -144,7 +144,6 @@ class GeometryMeasurement(pydantic.BaseModel): e_pa: float | None isophote: float | None e_isophote: float | None - quality: str source: Source diff --git a/postgres/migrations/V072__photometry_ellipse_layer2_view.sql b/postgres/migrations/V072__photometry_ellipse_layer2_view.sql index c7057a3e..157b1527 100644 --- a/postgres/migrations/V072__photometry_ellipse_layer2_view.sql +++ b/postgres/migrations/V072__photometry_ellipse_layer2_view.sql @@ -12,7 +12,6 @@ SELECT , e.e_pa , e.isophote , e.e_isophote -, e.quality , t.bib , b.code , b.year