19
19
import google .cloud .pubsub_v1
20
20
21
21
from . import exceptions , registry , types_
22
- from . schema import (
23
- Schema , # so 'schema' module doesn't clobber 'Alert.schema' attribute
24
- )
22
+
23
+ # so 'schema' module doesn't clobber 'Alert.schema' attribute
24
+ from . schema import Schema
25
25
26
26
if TYPE_CHECKING :
27
27
import astropy .table
@@ -96,7 +96,7 @@ def from_cloud_functions(
96
96
describes the service API endpoint pubsub.googleapis.com, the triggering topic's name,
97
97
and the triggering event type `type.googleapis.com/google.pubsub.v1.PubsubMessage`.
98
98
"""
99
- return cls (
99
+ alert = cls (
100
100
msg = types_ .PubsubMessageLike (
101
101
# data is required. the rest should be present in the message, but use get to be lenient
102
102
data = base64 .b64decode (event ["data" ]),
@@ -107,6 +107,8 @@ def from_cloud_functions(
107
107
context = context ,
108
108
schema_name = schema_name ,
109
109
)
110
+ alert .schema ._init_from_msg (alert )
111
+ return alert
110
112
111
113
@classmethod
112
114
def from_cloud_run (cls , envelope : Mapping , schema_name : str | None = None ) -> "Alert" :
@@ -162,7 +164,7 @@ def index():
162
164
if not isinstance (envelope , dict ) or "message" not in envelope :
163
165
raise exceptions .BadRequest ("Bad Request: invalid Pub/Sub message format" )
164
166
165
- return cls (
167
+ alert = cls (
166
168
msg = types_ .PubsubMessageLike (
167
169
# data is required. the rest should be present in the message, but use get to be lenient
168
170
data = base64 .b64decode (envelope ["message" ]["data" ].encode ("utf-8" )),
@@ -173,6 +175,8 @@ def index():
173
175
),
174
176
schema_name = schema_name ,
175
177
)
178
+ alert .schema ._init_from_msg (alert )
179
+ return alert
176
180
177
181
@classmethod
178
182
def from_dict (
@@ -213,7 +217,9 @@ def from_msg(
213
217
Alert:
214
218
The created `Alert` object.
215
219
"""
216
- return cls (msg = msg , schema_name = schema_name )
220
+ alert = cls (msg = msg , schema_name = schema_name )
221
+ alert .schema ._init_from_msg (alert )
222
+ return alert
217
223
218
224
@classmethod
219
225
def from_path (cls , path : str | Path , schema_name : str | None = None ) -> "Alert" :
@@ -237,9 +243,11 @@ def from_path(cls, path: str | Path, schema_name: str | None = None) -> "Alert":
237
243
"""
238
244
with open (path , "rb" ) as f :
239
245
bytes_ = f .read ()
240
- return cls (
246
+ alert = cls (
241
247
msg = types_ .PubsubMessageLike (data = bytes_ ), schema_name = schema_name , path = Path (path )
242
248
)
249
+ alert .schema ._init_from_msg (alert )
250
+ return alert
243
251
244
252
def to_mock_input (self , cloud_functions : bool = False ):
245
253
if not cloud_functions :
@@ -340,6 +348,22 @@ def sourceid(self) -> str | int:
340
348
"""
341
349
return self .get ("sourceid" )
342
350
351
+ @property
352
+ def ra (self ) -> float :
353
+ """Return the source's right ascension. Convenience wrapper around :attr:`Alert.get`.
354
+
355
+ The "source" is the detection that triggered the alert.
356
+ """
357
+ return self .get ("ra" )
358
+
359
+ @property
360
+ def dec (self ) -> float :
361
+ """Return the source's declination. Convenience wrapper around :attr:`Alert.get`.
362
+
363
+ The "source" is the detection that triggered the alert.
364
+ """
365
+ return self .get ("dec" )
366
+
343
367
@property
344
368
def schema (self ) -> Schema :
345
369
"""Return the schema from the :class:`pittgoogle.registry.Schemas` registry.
@@ -412,22 +436,29 @@ def skymap(self) -> Union["astropy.table.QTable", None]:
412
436
413
437
return self ._skymap
414
438
439
+ @property
440
+ def name_in_bucket (self ) -> str :
441
+ """Name of the alert object (file) in Google Cloud Storage."""
442
+ return self .schema ._name_in_bucket (alert = self )
443
+
415
444
# ---- methods ---- #
416
445
def _add_id_attributes (self ) -> None :
417
- """Add the IDs ("alertid", "objectid", "sourceid") to :attr:`Alert.attributes`."""
446
+ """Add the IDs 'alertid', 'objectid', 'sourceid' and 'schema.version' to :attr:`Alert.attributes`."""
447
+ # Get the data IDs and corresponding survey-specific field names. If the field is nested, the
448
+ # key will be a list. Join list -> string. These are likely to become Pub/Sub message attributes.
418
449
ids = ["alertid" , "objectid" , "sourceid" ]
450
+ _names = [self .get_key (id ) for id in ids ]
451
+ names = ["." .join (id ) if isinstance (id , list ) else id for id in _names ]
419
452
values = [self .get (id ) for id in ids ]
453
+ attributes = dict (zip (names , values ))
420
454
421
- # get the survey-specific field names
422
- survey_names = [self .get_key (id ) for id in ids ]
423
- # if the field is nested, the key will be a list
424
- # but pubsub message attributes must be strings. join to avoid a future error on publish
425
- names = ["." .join (id ) if isinstance (id , list ) else id for id in survey_names ]
455
+ # Add the schema version.
456
+ attributes ["schema.version" ] = self .schema .version
426
457
427
- # only add to attributes if the survey has defined this field and it's not already in the attributes
428
- for idname , idvalue in zip ( names , values ):
429
- if idname is not None and idname not in self ._attributes :
430
- self ._attributes [idname ] = idvalue
458
+ # Add attributes to self, but only if the survey has defined the field and it's not already there.
459
+ for name , value in attributes . items ( ):
460
+ if name is not None and name not in self ._attributes :
461
+ self ._attributes [name ] = value
431
462
432
463
def get (self , field : str , default : Any = None ) -> Any :
433
464
"""Return the value of a field from the alert data.
0 commit comments