diff --git a/app/api/components.py b/app/api/components.py deleted file mode 100644 index a51259ef..00000000 --- a/app/api/components.py +++ /dev/null @@ -1,146 +0,0 @@ -""" -This module contains definitions of the science payload components. Functions -that turn Blast database scheme into a flat science payload to be served through -the API. -""" -from typing import List - -from host import models - -from . import serializers -from .datamodel import DataModelComponent - - -def transient_component(transient_name) -> List[DataModelComponent]: - """ - Transient model component defined what transient information is in the - science payload. - - parameters: - transient_name (str): name of the transient. - returns: - component (List[DataModelComponent]): data model component to be added - to the Blast science payload. - - """ - component = DataModelComponent( - prefix="transient_", - query={"name__exact": transient_name}, - model=models.Transient, - serializer=serializers.TransientSerializer, - ) - return [component] - - -def host_component(transient_name) -> List[DataModelComponent]: - """ - Host model component defined what transient information is in the - science payload. - - parameters: - transient_name (str): name of the transient. - returns: - component (List[DataModelComponent]): data model component to be added - to the Blast science payload. - """ - component = DataModelComponent( - prefix="host_", - query={"transient__name__exact": transient_name}, - model=models.Host, - serializer=serializers.HostSerializer, - ) - return [component] - - -def aperture_component(transient_name) -> List[DataModelComponent]: - """ - Apeture model component defined what transient information is in the - science payload. - - parameters: - transient_name (str): name of the transient. - returns: - component (List[DataModelComponent]): data model component to be added - to the Blast science payload. - """ - components = [] - for aperture_type in ["local", "global"]: - components.append( - DataModelComponent( - prefix=f"{aperture_type}_aperture_", - query={ - "transient__name__exact": transient_name, - "type__exact": aperture_type, - }, - model=models.Aperture, - serializer=serializers.ApertureSerializer, - ) - ) - - return components - - -def photometry_component(transient_name) -> List[DataModelComponent]: - """ - Photometry model component defined what transient information is in the - science payload. - - parameters: - transient_name (str): name of the transient. - returns: - component (List[DataModelComponent]): data model component to be added - to the Blast science payload. - """ - components = [] - filters = models.Filter.objects.all() - for aperture_type in ["local", "global"]: - for filter in filters: - components.append( - DataModelComponent( - prefix=f"{aperture_type}_aperture_{filter.name}_", - query={ - "transient__name__exact": transient_name, - "filter__name__exact": filter.name, - "aperture__type__exact": aperture_type, - }, - model=models.AperturePhotometry, - serializer=serializers.AperturePhotometrySerializer, - ) - ) - - return components - - -def sed_fit_component(transient_name: str) -> List[DataModelComponent]: - """ - SED fit component which defines what is in the Blast science payload. - - parameters: - transient_name (str): name of the transient. - returns: - component (List[DataModelComponent]): data model component to be added - to the Blast science payload. - """ - components = [] - for aperture_type in ["local", "global"]: - components.append( - DataModelComponent( - prefix=f"{aperture_type}_aperture_host_", - query={ - "transient__name__exact": transient_name, - "aperture__type__exact": aperture_type, - }, - model=models.SEDFittingResult, - serializer=serializers.SEDFittingResultSerializer, - ) - ) - return components - - -data_model_components = [ - transient_component, - host_component, - aperture_component, - photometry_component, - sed_fit_component, -] diff --git a/app/api/datamodel.py b/app/api/datamodel.py deleted file mode 100644 index 45becc9c..00000000 --- a/app/api/datamodel.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -This module contains helper functions to serialize Blast data into a flat -science payload to be delivered by the Blast web API. -""" -import itertools -from dataclasses import dataclass -from typing import List - -import django -from host import models -from rest_framework import serializers - - -@dataclass -class DataModelComponent: - """ - A dataclass to store all information on how to - serialise a Blast model into the Blast science payload. - - Attributes: - prefix (str): What will be appended to the column name in the Blast - science payload - query (dict): A django query that will uniquely identify the data for - the Blast science payload - model (django.db.models.Model): The Blast model that the query will be - passed to - serializer (rest_framework.serializers.Serializer) The serializer - associated with the model - """ - - prefix: str - query: dict - model: django.db.models.Model - serializer: serializers.Serializer - - -def serialize_blast_science_data(datamodel: List[DataModelComponent]) -> dict: - """ - Serializes all data associated with a transient into a flat structure. - - parameters: - datamodel: (List[DataModelComponent]): datamodel to be serialized. - returns: - science_payload: Flat dictionary containing science data fields and - values. - """ - science_payload = {} - for component in datamodel: - prefix, serializer = component.prefix, component.serializer - try: - object = component.model.objects.get(**component.query) - object_data = serializer(object).data - object_dict = {prefix + name: value for name, value in object_data.items()} - except: - object_dict = {prefix + name: None for name in serializer().fields} - - science_payload = {**science_payload, **object_dict} - return science_payload - - -def unpack_component_groups( - component_groups: List[List[DataModelComponent]], -) -> List[DataModelComponent]: - """ - Unpacks list of datamodel component groups into a flat structure - - parameters: - component_groups (List[List[DataModelComponent]]): list of groups to be unpacked - returns: - flat_components (List[DataModelComponent]): flattened list of components - """ - return list(itertools.chain(*component_groups)) diff --git a/app/api/tests/test_datamodel.py b/app/api/tests/test_datamodel.py deleted file mode 100644 index dc16aea7..00000000 --- a/app/api/tests/test_datamodel.py +++ /dev/null @@ -1,122 +0,0 @@ -import itertools -from typing import List - -from django.test import TestCase -from host import models - -from .. import serializers -from ..components import aperture_component -from ..components import data_model_components -from ..components import host_component -from ..components import photometry_component -from ..components import sed_fit_component -from ..components import transient_component -from ..datamodel import DataModelComponent -from ..datamodel import serialize_blast_science_data -from ..datamodel import unpack_component_groups - - -class DatamodelConstructionTest(TestCase): - fixtures = ["../fixtures/test/test_transient_data.yaml"] - - def test_datamodel_build_with_data(self): - host = host_component("2022testone") - transient = transient_component("2022testone") - data = serialize_blast_science_data(host + transient) - self.assertTrue(data["transient_name"] == "2022testone") - self.assertTrue(data["host_name"] == "PSO J080624.103+010209.859") - - def test_datamodel_build_without_data(self): - host = host_component("thisTransientDoesNotExist") - transient = transient_component("thisTransientDoesNotExist") - data = serialize_blast_science_data(host + transient) - self.assertTrue(data["transient_name"] is None) - self.assertTrue(data["host_name"] is None) - - def test_photometry_build(self): - photometry = photometry_component("2022testone") - data = serialize_blast_science_data(photometry) - self.assertTrue(data["local_aperture_2MASS_H_flux"] == 2183.8) - self.assertTrue(data["local_aperture_2MASS_H_flux_error"] == 224.97) - self.assertTrue(data["local_aperture_2MASS_H_magnitude"] == 0.0) - self.assertTrue(data["local_aperture_2MASS_H_magnitude_error"] == 0.0) - - self.assertTrue(data["local_aperture_2MASS_J_flux"] == 1091.48) - self.assertTrue(data["local_aperture_2MASS_J_flux_error"] == 130.38) - self.assertTrue(data["local_aperture_2MASS_J_magnitude"] == 0.0) - self.assertTrue(data["local_aperture_2MASS_J_magnitude_error"] == 0.0) - - self.assertTrue(data["global_aperture_2MASS_J_flux"] == 99.0) - self.assertTrue(data["global_aperture_2MASS_J_flux_error"] == 99.0) - self.assertTrue(data["global_aperture_2MASS_J_magnitude"] == 0.0) - self.assertTrue(data["global_aperture_2MASS_J_magnitude_error"] == 0.0) - - self.assertTrue(data["global_aperture_2MASS_H_flux"] == 1.0) - self.assertTrue(data["global_aperture_2MASS_H_flux_error"] == 1.0) - self.assertTrue(data["global_aperture_2MASS_H_magnitude"] == 10.0) - self.assertTrue(data["global_aperture_2MASS_H_magnitude_error"] == 0.2) - - def test_aperture_build(self): - aperture = aperture_component("2022testone") - data = serialize_blast_science_data(aperture) - - self.assertTrue(data["local_aperture_ra_deg"] == 121.6015) - self.assertTrue(data["local_aperture_dec_deg"] == 1.03586) - self.assertTrue(data["local_aperture_semi_major_axis_arcsec"] == 1.0) - self.assertTrue(data["local_aperture_semi_minor_axis_arcsec"] == 1.0) - self.assertTrue(data["local_aperture_cutout"] is None) - - self.assertTrue(data["global_aperture_ra_deg"] == 11.6015) - self.assertTrue(data["global_aperture_dec_deg"] == 10.03586) - self.assertTrue(data["global_aperture_semi_major_axis_arcsec"] == 0.4) - self.assertTrue(data["global_aperture_semi_minor_axis_arcsec"] == 0.5) - self.assertTrue(data["global_aperture_cutout"]["name"] == "2022testone_2MASS_J") - - def test_sed_fitting_result_build(self): - sed_fit = sed_fit_component("2022testone") - data = serialize_blast_science_data(sed_fit) - self.assertTrue(data["local_aperture_host_log_mass_16"] == 10.0) - self.assertTrue(data["local_aperture_host_log_mass_50"] == 20.0) - self.assertTrue(data["local_aperture_host_log_mass_84"] == 30.0) - self.assertTrue(data["local_aperture_host_log_sfr_16"] == 123.4546) - self.assertTrue(data["local_aperture_host_log_sfr_50"] == 123.4566) - self.assertTrue(data["local_aperture_host_log_sfr_84"] == 56.564565) - self.assertTrue(data["local_aperture_host_log_ssfr_16"] == 15.676) - self.assertTrue(data["local_aperture_host_log_ssfr_50"] == 12.34343) - self.assertTrue(data["local_aperture_host_log_ssfr_84"] == 12) - self.assertTrue(data["local_aperture_host_log_age_16"] == 1.0) - self.assertTrue(data["local_aperture_host_log_age_50"] == 0.1) - self.assertTrue(data["local_aperture_host_log_age_84"] == 5.0) - - self.assertTrue(data["global_aperture_host_log_mass_16"] == 1.0) - self.assertTrue(data["global_aperture_host_log_mass_50"] == 2.0) - self.assertTrue(data["global_aperture_host_log_mass_84"] == 3.0) - self.assertTrue(data["global_aperture_host_log_sfr_16"] == 123.4546) - self.assertTrue(data["global_aperture_host_log_sfr_50"] == 123.4566) - self.assertTrue(data["global_aperture_host_log_sfr_84"] == 56.564565) - self.assertTrue(data["global_aperture_host_log_ssfr_16"] == 15.676) - self.assertTrue(data["global_aperture_host_log_ssfr_50"] == 12.34343) - self.assertTrue(data["global_aperture_host_log_ssfr_84"] == 12) - self.assertTrue(data["global_aperture_host_log_age_16"] == 1.0) - self.assertTrue(data["global_aperture_host_log_age_50"] == 0.1) - self.assertTrue(data["global_aperture_host_log_age_84"] == 5.0) - - -class DataModelComponentTests(TestCase): - def test_all_datamodel_components_output_type(self): - for component in data_model_components: - output = component("thisTransientDoesNotExist") - self.assertIsInstance(output, List) - for model_component in output: - self.assertIsInstance(model_component, DataModelComponent) - - -class GroupComponentUnpackTest(TestCase): - def test_component_group_unpacking(self): - nested = [[1], [2], [3, 4]] - nested_two = [[1], [2], [3], [4]] - flat = [1, 2, 3, 4] - unpack_nested = unpack_component_groups(nested) - self.assertTrue(flat == unpack_nested) - unpack_flat = unpack_component_groups(nested_two) - self.assertTrue(flat == unpack_flat) diff --git a/app/api/urls.py b/app/api/urls.py index aa0c412b..74cb5c34 100644 --- a/app/api/urls.py +++ b/app/api/urls.py @@ -9,10 +9,6 @@ base_path = f"""{base_path}/""" urlpatterns = [ - path( - f"""{base_path}transient/get/""", - views.get_transient_science_payload, - ), re_path( base_path + r"^transient/delete/(?P[a-zA-Z0-9_-]+)/(?Pall/|)$", views.delete_transient_view, diff --git a/app/api/views.py b/app/api/views.py index d2445d8f..76f8e420 100644 --- a/app/api/views.py +++ b/app/api/views.py @@ -41,9 +41,6 @@ from api.serializers import TaskSerializer from api.serializers import HostSerializer from api.serializers import AliasSerializer -from api.datamodel import unpack_component_groups -from api.datamodel import serialize_blast_science_data -from api.components import data_model_components from host.log import get_logger logger = get_logger(__name__) @@ -273,24 +270,6 @@ def ra_dec_valid(ra: str, dec: str) -> bool: # return exists -@api_view(["GET"]) -@log_usage_metric() -def get_transient_science_payload(request, transient_name): - if not transient_exists(transient_name): - return Response( - {"message": f"{transient_name} not in database"}, - status=status.HTTP_404_NOT_FOUND, - ) - - component_groups = [ - component_group(transient_name) for component_group in data_model_components - ] - components = unpack_component_groups(component_groups) - data = serialize_blast_science_data(components) - - return Response(data, status=status.HTTP_200_OK) - - @api_view(["POST"]) def post_transient(request, transient_name, transient_ra, transient_dec): if transient_exists(transient_name): diff --git a/app/host/templates/host/transient_actions.html b/app/host/templates/host/transient_actions.html index bec471cc..5e4d8910 100644 --- a/app/host/templates/host/transient_actions.html +++ b/app/host/templates/host/transient_actions.html @@ -11,7 +11,7 @@ Export with all files (.tar.gz) Export without files (.json) - Export without files (legacy .json) + {% if local_sed_results %} Local SED best-fit model (.npz)