From 870d8c1e7ca5d26a986beac26e675dc3fe613f09 Mon Sep 17 00:00:00 2001 From: Justin Vasel Date: Fri, 16 Aug 2024 14:19:32 -0500 Subject: [PATCH 1/3] Allow automatic creation of Heartbeats from Coin Tier if requested by user --- snews/models/messages.py | 14 ++++++++++++-- test/unit/test_models.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/snews/models/messages.py b/snews/models/messages.py index cac49d4..c68248e 100644 --- a/snews/models/messages.py +++ b/snews/models/messages.py @@ -403,7 +403,7 @@ def _validate_neutrino_time(self): # ................................................................................................. -def compatible_message_types(**kwargs) -> list: +def compatible_message_types(include_heartbeats=False, **kwargs) -> list: """ Return a list of message types that are compatible with the given keyword arguments. """ @@ -421,6 +421,10 @@ def compatible_message_types(**kwargs) -> list: try: message_type(**kwargs) compatible_message_types.append(message_type) + + if include_heartbeats and message_type == CoincidenceTierMessage: + compatible_message_types.append(HeartbeatMessage) + except ValidationError: pass @@ -435,6 +439,12 @@ def create_messages(**kwargs) -> list: messages = [] for message_type in compatible_message_types(**kwargs): - messages.append(message_type(**kwargs)) + if message_type == HeartbeatMessage: + messages.append(message_type(detector_status="ON", **kwargs)) + else: + messages.append(message_type(**kwargs)) + + if len(messages) == 0: + raise ValueError("No compatible message types found") return messages diff --git a/test/unit/test_models.py b/test/unit/test_models.py index 08b3b59..25016af 100644 --- a/test/unit/test_models.py +++ b/test/unit/test_models.py @@ -15,3 +15,36 @@ def test_import_detector_models(): expected = models.detectors.__all__ actual = models.detectors.__dir__() assert expected == actual + + +# ................................................................................................. +def test_create_messages_function(): + inputs = { + "detector_name": "Super-K", + "neutrino_time_utc": "2012-06-09T15:31:08.109876", + "machine_time_utc": "2012-06-09T15:30:00.009876", + "is_firedrill": False, + "is_test": True, + } + + msgs = models.messages.create_messages(**inputs) + message_types = [type(msg) for msg in msgs] + assert message_types == [models.messages.CoincidenceTierMessage] + + +# ................................................................................................. +def test_create_messages_function_with_heartbeats(): + inputs = { + "detector_name": "Super-K", + "neutrino_time_utc": "2012-06-09T15:31:08.109876", + "machine_time_utc": "2012-06-09T15:30:00.009876", + "is_firedrill": False, + "is_test": True, + } + + msgs = models.messages.create_messages(**inputs, include_heartbeats=True) + message_types = [type(msg) for msg in msgs] + assert message_types == [ + models.messages.CoincidenceTierMessage, + models.messages.HeartbeatMessage + ] From 11a1729a03a096de8e6bce33ac87b5ed880c2000 Mon Sep 17 00:00:00 2001 From: Justin Vasel Date: Fri, 16 Aug 2024 14:20:33 -0500 Subject: [PATCH 2/3] Remove extra print statement and fix neutrino time validation bug --- snews/models/messages.py | 2 +- snews/models/timing.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/snews/models/messages.py b/snews/models/messages.py index c68248e..8d9b4b2 100644 --- a/snews/models/messages.py +++ b/snews/models/messages.py @@ -379,7 +379,7 @@ def _set_tier(cls, values): values['tier'] = Tier.COINCIDENCE_TIER return values - @field_validator("neutrino_time_utc") + @field_validator("neutrino_time_utc", mode="before") def _validate_neutrino_time_format(cls, v: str): return convert_timestamp_to_ns_precision(v) diff --git a/snews/models/timing.py b/snews/models/timing.py index 3042b98..5bf5295 100644 --- a/snews/models/timing.py +++ b/snews/models/timing.py @@ -64,7 +64,6 @@ def _validate_and_cast_timestamp(cls, v): v = v.replace(tzinfo=None) if not isinstance(v, np.datetime64): - print(v) v = np.datetime64(v) return v From e2a1fde453f0b8a4408b6d303cc5f94810d5c8db Mon Sep 17 00:00:00 2001 From: Justin Vasel Date: Fri, 16 Aug 2024 14:22:21 -0500 Subject: [PATCH 3/3] Fix type on RetractionMessage retract_latest property and rename --- snews/models/messages.py | 21 ++++++++++--------- snews/schema/RetractionMessage.schema.json | 24 ++++++++++++++-------- test/models/test_message_models.py | 12 +++++------ 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/snews/models/messages.py b/snews/models/messages.py index 8d9b4b2..fd0aed6 100644 --- a/snews/models/messages.py +++ b/snews/models/messages.py @@ -9,7 +9,8 @@ # Third-party modules import numpy as np from pydantic import (BaseModel, ConfigDict, Field, NonNegativeFloat, - ValidationError, field_validator, model_validator) + NonNegativeInt, ValidationError, field_validator, + model_validator) # Local modules from ..__version__ import schema_version @@ -232,20 +233,20 @@ class RetractionMessage(DetectorMessageBase): model_config = ConfigDict(validate_assignment=True) - retract_message_uid: Optional[str] = Field( + retract_message_uuid: Optional[str] = Field( default=None, title="Unique message ID", description="Unique identifier for the message to retract" ) - retract_latest: bool = Field( - default=False, + retract_latest_n: NonNegativeInt = Field( + default=0, title="Retract Latest Flag", description="True if the latest message is being retracted", ) - retraction_reason: str = Field( - ..., + retraction_reason: Optional[str] = Field( + default=None, title="Retraction reason", description="Reason for retraction", ) @@ -257,11 +258,11 @@ def _set_tier(cls, values): @model_validator(mode="after") def _validate_model(self): - if self.retract_latest and self.retract_message_uid is not None: - raise ValueError("retract_message_uuid cannot be specified when retract_latest=True") + if self.retract_latest_n > 0 and self.retract_message_uuid is not None: + raise ValueError("retract_message_uuid cannot be specified when retract_latest_n > 0") - if not self.retract_latest and self.retract_message_uid is None: - raise ValueError("Must specify either retract_message_uuid or retract_latest=True") + if self.retract_latest_n == 0 and self.retract_message_uuid is None: + raise ValueError("Must specify either retract_message_uuid or retract_latest_n > 0") return self diff --git a/snews/schema/RetractionMessage.schema.json b/snews/schema/RetractionMessage.schema.json index b630146..665b3f3 100644 --- a/snews/schema/RetractionMessage.schema.json +++ b/snews/schema/RetractionMessage.schema.json @@ -140,7 +140,7 @@ "title": "Detector Name", "type": "string" }, - "retract_message_uid": { + "retract_message_uuid": { "anyOf": [ { "type": "string" @@ -153,22 +153,30 @@ "description": "Unique identifier for the message to retract", "title": "Unique message ID" }, - "retract_latest": { - "default": false, + "retract_latest_n": { + "default": 0, "description": "True if the latest message is being retracted", + "minimum": 0, "title": "Retract Latest Flag", - "type": "boolean" + "type": "integer" }, "retraction_reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, "description": "Reason for retraction", - "title": "Retraction reason", - "type": "string" + "title": "Retraction reason" } }, "required": [ "tier", - "detector_name", - "retraction_reason" + "detector_name" ], "title": "RetractionMessage", "type": "object" diff --git a/test/models/test_message_models.py b/test/models/test_message_models.py index d4073fb..6cc711e 100644 --- a/test/models/test_message_models.py +++ b/test/models/test_message_models.py @@ -64,7 +64,7 @@ # Retraction message strategy_required_fields_retraction = { **strategy_required_fields_base, - "retract_message_uid": st.uuids(version=4).map(lambda x: str(x)), + "retract_message_uuid": st.uuids(version=4).map(lambda x: str(x)), "retraction_reason": st.text(min_size=1), } @@ -165,10 +165,10 @@ def test_snews_message_model_retraction_required(**kwargs): def test_snews_message_model_retraction_validation_both_indicators(**kwargs): with pytest.raises(ValueError) as exc_info: msg = RetractionMessage(**kwargs) - msg.retract_latest = True + msg.retract_latest_n = 3 msg.retract_message_uid = "1234567890" - assert "retract_message_uuid cannot be specified when retract_latest=True" in str( + assert "retract_message_uuid cannot be specified when retract_latest_n > 0" in str( exc_info.value ) @@ -177,9 +177,9 @@ def test_snews_message_model_retraction_validation_both_indicators(**kwargs): def test_snews_message_model_retraction_validation_neither_indicator(**kwargs): with pytest.raises(ValueError) as exc_info: msg = RetractionMessage(**kwargs) - msg.retract_latest = False - msg.retract_message_uid = None + msg.retract_latest_n = 0 + msg.retract_message_uuid = None - assert "Must specify either retract_message_uuid or retract_latest=True" in str( + assert "Must specify either retract_message_uuid or retract_latest_n > 0" in str( exc_info.value )