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 @@ -5,6 +5,7 @@
from app.data.model.interface import CatalogObject, MeasuredValue, RawCatalog, get_object
from app.data.model.layer2 import Layer2CatalogObject, Layer2Object
from app.data.model.nature import NatureCatalogObject
from app.data.model.photometry import PhotometryCatalogObject
from app.data.model.records import (
CrossmatchRecordRow,
DesignationRecord,
Expand Down Expand Up @@ -49,6 +50,7 @@
"ICRSCatalogObject",
"RedshiftCatalogObject",
"NatureCatalogObject",
"PhotometryCatalogObject",
"get_catalog_object_type",
"MeasuredValue",
"Bibliography",
Expand Down
4 changes: 3 additions & 1 deletion app/data/model/helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from app.data.model import designation, icrs, interface, nature, redshift
from app.data.model import designation, icrs, interface, nature, photometry, redshift


def get_catalog_object_type(catalog: interface.RawCatalog) -> type[interface.CatalogObject]:
Expand All @@ -10,5 +10,7 @@ def get_catalog_object_type(catalog: interface.RawCatalog) -> type[interface.Cat
return redshift.RedshiftCatalogObject
if catalog == interface.RawCatalog.NATURE:
return nature.NatureCatalogObject
if catalog == interface.RawCatalog.PHOTOMETRY:
return photometry.PhotometryCatalogObject

raise ValueError(f"Unknown catalog: {catalog}")
5 changes: 5 additions & 0 deletions app/data/model/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class RawCatalog(enum.Enum):
ADDITIONAL_DESIGNATIONS = "additional_designations"
REDSHIFT = "redshift"
NATURE = "nature"
PHOTOMETRY = "photometry"


class CatalogObject(abc.ABC):
Expand All @@ -56,6 +57,10 @@ def layer1_table(cls) -> str:
def layer1_keys(cls) -> list[str]:
pass

@classmethod
def layer1_primary_keys(cls) -> list[str]:
return ["record_id"]

@classmethod
@abc.abstractmethod
def from_layer1(cls, data: dict[str, Any]) -> Self:
Expand Down
68 changes: 68 additions & 0 deletions app/data/model/photometry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from typing import Any, Self, final

from app.data.model import interface


@final
class PhotometryCatalogObject(interface.CatalogObject):
def __init__(
self,
band: str,
mag: float,
e_mag: float | None,
method: str,
**kwargs: Any,
) -> None:
self.band = band
self.mag = mag
self.e_mag = e_mag
self.method = method

def __eq__(self, value: object) -> bool:
if not isinstance(value, PhotometryCatalogObject):
return False
return (
self.band == value.band
and self.mag == value.mag
and self.e_mag == value.e_mag
and self.method == value.method
)

def catalog(self) -> interface.RawCatalog:
return interface.RawCatalog.PHOTOMETRY

@classmethod
def layer1_table(cls) -> str:
return "photometry.data"

@classmethod
def layer1_keys(cls) -> list[str]:
return ["band", "mag", "e_mag", "method"]

@classmethod
def layer1_primary_keys(cls) -> list[str]:
return ["record_id", "method", "band"]

@classmethod
def from_layer1(cls, data: dict[str, Any]) -> Self:
return cls(
band=data["band"],
mag=data["mag"],
e_mag=data.get("e_mag"),
method=data["method"],
)

@classmethod
def layer2_table(cls) -> str:
raise NotImplementedError

@classmethod
def layer2_keys(cls) -> list[str]:
raise NotImplementedError

def layer2_data(self) -> dict[str, Any]:
raise NotImplementedError

@classmethod
def from_layer2(cls, data: dict[str, Any]) -> Self:
raise NotImplementedError
16 changes: 13 additions & 3 deletions app/data/repositories/layer1.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,23 @@ def get_column_units(self, catalog: model.RawCatalog) -> dict[str, str]:
)
return {row["column_name"]: row["unit"] for row in rows}

def save_structured_data(self, table: str, columns: list[str], ids: list[str], data: list[list[Any]]) -> None:
def save_structured_data(
self,
table: str,
columns: list[str],
ids: list[str],
data: list[list[Any]],
conflict_keys: list[str] | None = None,
) -> None:
if conflict_keys is None:
conflict_keys = ["record_id"]
all_columns = ["record_id"] + columns
placeholders = ",".join(["%s"] * len(all_columns))
on_conflict = ", ".join(f"{c} = EXCLUDED.{c}" for c in all_columns)
update_columns = [c for c in all_columns if c not in conflict_keys]
on_conflict_set = ", ".join(f"{c} = EXCLUDED.{c}" for c in update_columns)
query = (
f"INSERT INTO {table} ({', '.join(all_columns)}) VALUES ({placeholders}) "
f"ON CONFLICT (record_id) DO UPDATE SET {on_conflict}"
f"ON CONFLICT ({', '.join(conflict_keys)}) DO UPDATE SET {on_conflict_set}"
)
rows = [[rid] + vals for rid, vals in zip(ids, data, strict=True)]
with self.with_tx():
Expand Down
5 changes: 4 additions & 1 deletion app/domain/adminapi/layer1_write.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ def save_data(self, request: adminapi.SaveStructuredDataRequest) -> adminapi.Sav
converted.append(new_row)

try:
self._layer1_repo.save_structured_data(table, request.columns, request.ids, converted)
conflict_keys = object_cls.layer1_primary_keys()
self._layer1_repo.save_structured_data(
table, request.columns, request.ids, converted, conflict_keys=conflict_keys
)
except psycopg.errors.ForeignKeyViolation as e:
diag = getattr(e, "diag", None)
detail = getattr(diag, "message_detail", None) if diag else None
Expand Down
7 changes: 0 additions & 7 deletions postgres/drafts/04_nature.sql
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@ CREATE TABLE nature.datasets (
) ;


CREATE TABLE nature.data (
record_id Text PRIMARY KEY REFERENCES layer0.records(id) ON UPDATE cascade ON DELETE restrict
, objtype Text NOT NULL REFERENCES nature.objectType(objtype) ON UPDATE cascade ON DELETE restrict
, dataset_id Integer NOT NULL REFERENCES nature.datasets(id) ON DELETE restrict ON UPDATE cascade
) ;


CREATE VIEW nature.dataview AS
SELECT
r.pgc
Expand Down
Loading
Loading