Skip to content
Draft
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: 2 additions & 4 deletions app/data/model/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
from app.data.model.bibliography import Bibliography
from app.data.model.designation import DesignationCatalogObject
from app.data.model.helpers import (
CatalogObjectEncoder,
Layer0CatalogObjectDecoder,
get_catalog_object_type,
new_catalog_object,
)
from app.data.model.icrs import ICRSCatalogObject
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.records import (
CIResult,
CIResultObjectCollision,
Expand Down Expand Up @@ -48,11 +47,10 @@
"Layer2Object",
"RawCatalog",
"CatalogObject",
"CatalogObjectEncoder",
"Layer0CatalogObjectDecoder",
"DesignationCatalogObject",
"ICRSCatalogObject",
"RedshiftCatalogObject",
"NatureCatalogObject",
"get_catalog_object_type",
"new_catalog_object",
"HomogenizationRule",
Expand Down
23 changes: 4 additions & 19 deletions app/data/model/designation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re
import statistics
from typing import Any, Self, final

from app.data.model import interface
Expand All @@ -19,28 +20,12 @@ def __eq__(self, value: object) -> bool:
def from_custom(cls, design: Any) -> Self:
return cls(str(design))

def layer0_data(self) -> dict[str, Any]:
return {"design": self.designation}

@classmethod
def aggregate(cls, objects: list[Self]) -> Self:
"""
Aggregate designation is selected as the most common designation among all objects.
"""
name_counts = {}

for obj in objects:
name_counts[obj.designation] = name_counts.get(obj.designation, 0) + 1

max_name = ""
return cls(statistics.mode([obj.designation for obj in objects]))

for name, count in name_counts.items():
if count > name_counts.get(max_name, 0):
max_name = name

return cls(max_name)

def catalog(self) -> interface.RawCatalog:
@classmethod
def catalog(cls) -> interface.RawCatalog:
return interface.RawCatalog.DESIGNATION

@classmethod
Expand Down
2 changes: 2 additions & 0 deletions app/data/model/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class CatalogObjectCreationError(Exception):
pass
41 changes: 10 additions & 31 deletions app/data/model/helpers.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,18 @@
import json
from app.data.model import designation, icrs, interface, nature, redshift

from app.data.model import designation, icrs, interface, redshift
ALLOWED_CATALOG_OBJECTS = [
designation.DesignationCatalogObject,
icrs.ICRSCatalogObject,
redshift.RedshiftCatalogObject,
nature.NatureCatalogObject,
]


class CatalogObjectEncoder(json.JSONEncoder):
def default(self, obj):
if not isinstance(obj, interface.CatalogObject):
return json.JSONEncoder.default(self, obj)

data = obj.layer0_data()
data["catalog"] = obj.catalog().value

return data


class Layer0CatalogObjectDecoder(json.JSONDecoder):
def __init__(self, *args, **kwargs):
super().__init__(object_hook=self.object_hook, **kwargs)

def object_hook(self, obj):
if "catalog" not in obj:
return obj

catalog = interface.RawCatalog(obj.pop("catalog"))

return new_catalog_object(catalog, **obj)
catalog_to_objtype = {t.catalog(): t for t in ALLOWED_CATALOG_OBJECTS}


def get_catalog_object_type(catalog: interface.RawCatalog) -> type[interface.CatalogObject]:
if catalog == interface.RawCatalog.DESIGNATION:
return designation.DesignationCatalogObject
if catalog == interface.RawCatalog.ICRS:
return icrs.ICRSCatalogObject
if catalog == interface.RawCatalog.REDSHIFT:
return redshift.RedshiftCatalogObject
if catalog in catalog_to_objtype:
return catalog_to_objtype[catalog]

raise ValueError(f"Unknown catalog: {catalog}")

Expand Down
11 changes: 2 additions & 9 deletions app/data/model/icrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,6 @@ def from_custom(
astronomy.to(e_dec, "deg"),
)

def layer0_data(self) -> dict[str, Any]:
return {
"ra": self.ra,
"dec": self.dec,
"e_ra": self.e_ra,
"e_dec": self.e_dec,
}

def __eq__(self, value: object) -> bool:
if not isinstance(value, ICRSCatalogObject):
return False
Expand All @@ -79,7 +71,8 @@ def aggregate(cls, objects: list[Self]) -> Self:

return cls(ra, dec, e_ra, e_dec)

def catalog(self) -> interface.RawCatalog:
@classmethod
def catalog(cls) -> interface.RawCatalog:
return interface.RawCatalog.ICRS

def layer1_data(self) -> dict[str, Any]:
Expand Down
8 changes: 3 additions & 5 deletions app/data/model/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class RawCatalog(enum.Enum):
ICRS = "icrs"
DESIGNATION = "designation"
REDSHIFT = "redshift"
NATURE = "nature"


class CatalogObject(abc.ABC):
Expand All @@ -45,12 +46,9 @@ class CatalogObject(abc.ABC):
def aggregate(cls, objects: list[Self]) -> Self:
pass

@classmethod
@abc.abstractmethod
def catalog(self) -> RawCatalog:
pass

@abc.abstractmethod
def layer0_data(self) -> dict[str, Any]:
def catalog(cls) -> RawCatalog:
pass

@classmethod
Expand Down
72 changes: 72 additions & 0 deletions app/data/model/nature.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import statistics
from typing import Any, Self, final

from app.data.model import errors, interface
from app.lib.storage import enums

# all options are lowercase since we will .lower() everything anyway
options = {
enums.Nature.STAR: ["*", "s", "star"],
enums.Nature.STAR_SYSTEM: ["*s", "**", "stars", "c", "s2", "s3", "a", "s+", "gc", "oc"],
enums.Nature.INTERSTELLAR_MEDIUM: ["ism"],
enums.Nature.GALAXY: ["g", "gal", "galaxy"],
enums.Nature.MULTIPLE_GALAXIES: ["mg", "m2", "m3", "mc"],
enums.Nature.OTHER: ["o"],
enums.Nature.ERROR: ["!", "e", "x", "pg", "u"],
}

option_to_nature: dict[str, enums.Nature] = {}
for nature, names in options.items():
for name in names:
option_to_nature[name] = nature


@final
class NatureCatalogObject(interface.CatalogObject):
def __init__(self, nature: enums.Nature) -> None:
self.nature = nature

@classmethod
def from_custom(cls, nature: str) -> Self:
if (n := nature.lower()) in option_to_nature:
return cls(option_to_nature[n])

raise errors.CatalogObjectCreationError(f"Unknown object type: {nature}")

@classmethod
def aggregate(cls, objects: list[Self]) -> Self:
return cls(statistics.mode([obj.nature for obj in objects]))

@classmethod
def catalog(cls) -> interface.RawCatalog:
return interface.RawCatalog.NATURE

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

@classmethod
def layer1_keys(cls) -> list[str]:
return ["nature"]

def layer1_data(self) -> dict[str, Any]:
return {"nature": self.nature}

@classmethod
def from_layer1(cls, data: dict[str, Any]) -> Self:
return cls(nature=data["nature"])

@classmethod
def layer2_table(cls) -> str:
return "layer2.nature"

@classmethod
def layer2_keys(cls) -> list[str]:
return ["nature"]

def layer2_data(self) -> dict[str, Any]:
return {"nature": self.nature}

@classmethod
def from_layer2(cls, data: dict[str, Any]) -> Self:
return cls(nature=data["nature"])
6 changes: 2 additions & 4 deletions app/data/model/redshift.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ def from_custom(

return cls(data_cz, data_e_cz)

def layer0_data(self) -> dict[str, Any]:
return {"cz": self.cz, "e_cz": self.e_cz}

@classmethod
def aggregate(cls, objects: list[Self]) -> Self:
e_cz = [obj.e_cz for obj in objects]
Expand All @@ -52,7 +49,8 @@ def aggregate(cls, objects: list[Self]) -> Self:

return cls(cz, e_cz)

def catalog(self) -> interface.RawCatalog:
@classmethod
def catalog(cls) -> interface.RawCatalog:
return interface.RawCatalog.REDSHIFT

@classmethod
Expand Down
6 changes: 0 additions & 6 deletions app/data/repositories/layer2/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@
from app.lib import containers
from app.lib.storage import postgres

catalogs = [
model.RawCatalog.ICRS,
model.RawCatalog.DESIGNATION,
model.RawCatalog.REDSHIFT,
]


class Layer2Repository(postgres.TransactionalPGRepository):
def __init__(self, storage: postgres.PgStorage, logger: structlog.stdlib.BoundLogger) -> None:
Expand Down
10 changes: 10 additions & 0 deletions app/lib/storage/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,13 @@ class RecordCrossmatchStatus(str, enum.Enum):
NEW = "new"
COLLIDED = "collided"
EXISTING = "existing"


class Nature(enum.Enum):
STAR = "*"
STAR_SYSTEM = "*S"
INTERSTELLAR_MEDIUM = "ISM"
GALAXY = "G"
MULTIPLE_GALAXIES = "MG"
OTHER = "O"
ERROR = "X"
1 change: 1 addition & 0 deletions app/lib/storage/postgres/postgres_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def dump(self, obj: Any) -> bytes | bytearray | memoryview:
(enums.DataType, "common.datatype"),
(enums.RawDataStatus, "rawdata.status"),
(enums.RecordCrossmatchStatus, "rawdata.crossmatch_status"),
(enums.Nature, "nature.status"),
]


Expand Down
22 changes: 22 additions & 0 deletions postgres/migrations/V012__object_nature.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
CREATE SCHEMA IF NOT EXISTS nature;

CREATE TYPE nature.status AS ENUM(
'*',
'*S',
'ISM',
'G',
'MG',
'O',
'X'
);

CREATE TABLE nature.data (
object_id text PRIMARY KEY REFERENCES rawdata.objects (id),
nature nature.status NOT NULL,
modification_time timestamp without time zone NOT NULL DEFAULT NOW(),
);

CREATE TABLE layer2.nature (
pgc integer PRIMARY KEY,
nature nature.status NOT NULL
);
Loading