Coverage for middle_layer/allocate/domain_layer/entities/allocated_reference_target.py: 84.44%

45 statements  

« prev     ^ index     » next       coverage.py v7.10.5, created at 2026-03-09 06:13 +0000

1# Copyright 2024 Associated Universities, Inc. 

2# 

3# This file is part of Telescope Time Allocation Tools (TTAT). 

4# 

5# TTAT is free software: you can redistribute it and/or modify 

6# it under the terms of the GNU General Public License as published by 

7# the Free Software Foundation, either version 3 of the License, or 

8# any later version. 

9# 

10# TTAT is distributed in the hope that it will be useful, 

11# but WITHOUT ANY WARRANTY; without even the implied warranty of 

12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

13# GNU General Public License for more details. 

14# 

15# You should have received a copy of the GNU General Public License 

16# along with TTAT. If not, see <https://www.gnu.org/licenses/>. 

17# 

18from typing import TYPE_CHECKING, override 

19 

20import astropy.units as u 

21from astropy.coordinates import SkyCoord 

22from astropy.units import Quantity 

23from sqlalchemy import Enum as SAEnum 

24from sqlalchemy import ForeignKey 

25from sqlalchemy.orm import Mapped, mapped_column, relationship 

26 

27from common.application_layer.orm_repositories.orm_types import QuantitySeconds 

28from common.domain_layer import JSON 

29from common.domain_layer.entities.base import Base 

30from propose.domain_layer.entities import HasSkyCoord 

31from propose.domain_layer.entities.hardware_configuration import HardwareConfiguration 

32from propose.domain_layer.entities.reference_target import ReferenceTarget 

33from propose.domain_layer.entities.scan import ScanIntent, Subscan 

34from propose.domain_layer.entities.source import Source 

35 

36if TYPE_CHECKING: 

37 from allocate.domain_layer.entities.allocation_disposition import AllocationDisposition 

38 

39 

40time = u.get_physical_type("time") 

41 

42 

43class AllocatedReferenceTarget(Base, HasSkyCoord): 

44 __tablename__ = "allocated_reference_targets" 

45 

46 allocated_reference_target_id: Mapped[int] = mapped_column(primary_key=True) 

47 

48 source_id: Mapped[int] = mapped_column(ForeignKey("sources.source_id"), nullable=False) 

49 source: Mapped[Source] = relationship(Source) 

50 

51 hardware_configuration_id: Mapped[int] = mapped_column( 

52 ForeignKey("hardware_configurations.hardware_configuration_id"), nullable=False 

53 ) 

54 hardware_configuration: Mapped[HardwareConfiguration] = relationship() 

55 

56 requested_time: Mapped[Quantity["time"]] = mapped_column(QuantitySeconds, nullable=False, default=0 * u.s) 

57 # intent_name: Mapped[str] = mapped_column("intent", ForeignKey("scan_intents.name"), nullable=False) 

58 intent_name: Mapped[str] = mapped_column(ForeignKey("scan_intents.name"), nullable=False) 

59 

60 intent: Mapped[ScanIntent] = relationship() 

61 subscans: Mapped[list["Subscan"]] = relationship("Subscan", back_populates="allocated_reference_target") 

62 

63 # The remaining columns here are unique to the AST and not copied from the ST 

64 allocation_disposition_id: Mapped[int] = mapped_column( 

65 ForeignKey("allocation_dispositions.allocation_disposition_id", ondelete="CASCADE"), 

66 nullable=False, 

67 ) 

68 allocation_disposition: Mapped["AllocationDisposition"] = relationship( 

69 "AllocationDisposition", back_populates="allocated_reference_targets" 

70 ) 

71 

72 def __json__(self) -> JSON: 

73 overrides: JSON = { 

74 "source": self.source.__json__() if self.source else None, 

75 "hardwareConfiguration": self.hardware_configuration.__json__() if self.hardware_configuration else None, 

76 "intent": self.intent.__json__() if self.intent else None, 

77 } 

78 return super().__json__() | overrides 

79 

80 def __eq__(self, other: object) -> bool: 

81 return isinstance(other, AllocatedReferenceTarget) and ( 

82 self.source, 

83 self.hardware_configuration, 

84 self.requested_time, 

85 self.intent_name, 

86 ) == ( 

87 other.source, 

88 other.hardware_configuration, 

89 other.requested_time, 

90 other.intent_name, 

91 ) 

92 

93 def clone(self) -> "AllocatedReferenceTarget": 

94 return AllocatedReferenceTarget( 

95 source=self.source.clone(), 

96 hardware_configuration=self.hardware_configuration.clone(), 

97 requested_time=self.requested_time, 

98 intent_name=self.intent_name, 

99 ) 

100 

101 @classmethod 

102 def from_reference_target( 

103 cls: type["AllocatedReferenceTarget"], original: ReferenceTarget, ad: "AllocationDisposition" 

104 ) -> "AllocatedReferenceTarget": 

105 return cls( 

106 source=original.source.clone(), 

107 hardware_configuration=original.hardware_configuration.clone(), 

108 requested_time=original.requested_time, 

109 intent_name=original.intent_name, 

110 intent=original.intent, 

111 allocation_disposition=ad, 

112 ) 

113 

114 @property 

115 @override 

116 def sky_coord(self) -> SkyCoord: 

117 return SkyCoord(ra=self.long * u.deg, dec=self.lat * u.deg)