From c25cd15eebaa93facf85070e9ba0c43020d8b234 Mon Sep 17 00:00:00 2001 From: brandon Date: Fri, 10 Jan 2025 13:57:26 -0800 Subject: [PATCH 1/8] Adds ability to create alerts with multiple actions --- src/groundlight/experimental_api.py | 143 +++++++++++++++++++++++++++- test/unit/test_actions.py | 15 +++ 2 files changed, 156 insertions(+), 2 deletions(-) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index 5b8765b8..aaee1cab 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -16,6 +16,7 @@ from groundlight_openapi_client.api.detector_reset_api import DetectorResetApi from groundlight_openapi_client.api.image_queries_api import ImageQueriesApi from groundlight_openapi_client.api.notes_api import NotesApi + from groundlight_openapi_client.model.action_request import ActionRequest from groundlight_openapi_client.model.channel_enum import ChannelEnum from groundlight_openapi_client.model.condition_request import ConditionRequest @@ -27,12 +28,12 @@ from groundlight_openapi_client.model.rule_request import RuleRequest from groundlight_openapi_client.model.status_enum import StatusEnum from groundlight_openapi_client.model.verb_enum import VerbEnum -from model import ROI, BBoxGeometry, Detector, DetectorGroup, ModeEnum, PaginatedRuleList, Rule +from model import ROI, Action, ActionList, BBoxGeometry, Condition, Detector, DetectorGroup, ModeEnum, PaginatedRuleList, Rule from groundlight.images import parse_supported_image_types from groundlight.optional_imports import Image, np -from .client import DEFAULT_REQUEST_TIMEOUT, Groundlight +from .client import DEFAULT_REQUEST_TIMEOUT, Groundlight, logger class ExperimentalApi(Groundlight): @@ -93,6 +94,141 @@ def __init__( ITEMS_PER_PAGE = 100 + def make_condition( + self, + verb: str, + parameters: dict + ) -> Condition: + """ + Creates a Condition object for use in creating alerts + + This function serves as a convenience method; Condition objects can also be created directly. + + **Example usage**:: + + gl = ExperimentalApi() + + # Create a condition for a rule + condition = gl.make_condition("CHANGED_TO", {"label": "YES"}) + + :param verb: The condition verb to use. One of "ANSWERED_CONSECUTIVELY", "ANSWERED_WITHIN_TIME", + "CHANGED_TO", "NO_CHANGE", "NO_QUERIES" + :param condition_parameters: Additional parameters for the condition, dependant on the verb: + - For ANSWERED_CONSECUTIVELY: {"num_consecutive_labels": N, "label": "YES/NO"} + - For CHANGED_TO: {"label": "YES/NO"} + - For ANSWERED_WITHIN_TIME: {"time_value": N, "time_unit": "MINUTES/HOURS/DAYS"} + + :return: The created Condition object + """ + return Condition(verb=verb, parameters=parameters) + + def make_action( + self, + channel: str, + recipient: str, + include_image: bool, + ) -> Action: + """ + Creates an Action object for use in creating alerts + + This function serves as a convenience method; Action objects can also be created directly. + + **Example usage**:: + + gl = ExperimentalApi() + + # Create an action for a rule + action = gl.make_action("EMAIL", "example@example.com", include_image=True) + + :param channel: The notification channel to use. One of "EMAIL" or "TEXT" + :param recipient: The email address or phone number to send notifications to + :param include_image: Whether to include the triggering image in notifications + """ + return Action( + channel=channel, + recipient=recipient, + include_image=include_image, + ) + + def create_alert( + self, + detector: Union[str, Detector], + name, + condition: Condition, + actions: Union[Action, List[Action], ActionList], + *, + enabled: bool = True, + snooze_time_enabled: bool = False, + snooze_time_value: int = 3600, + snooze_time_unit: str = "SECONDS", + human_review_required: bool = False, + ) -> Rule: + """ + Creates an alert for a detector that will trigger actions based on specified conditions. + + An alert allows you to configure automated actions when certain conditions are met, + such as when a detector's prediction changes or maintains a particular state. + + .. note:: + Currently, only binary mode detectors (YES/NO answers) are supported for notification rules. + + **Example usage**:: + + gl = ExperimentalApi() + + # Create a rule to send email alerts when door is detected as open + condition = gl.make_condition( + verb="CHANGED_TO", + parameters={"label": "YES"} + ) + action1 = gl.make_action( + "EMAIL", + "alerts@company.com", + include_image=True + ) + action2 = gl.make_action( + "TEXT", + "+1234567890", + include_image=False + ) + alert = gl.create_alert( + detector="det_idhere", + name="Door Open Alert", + condition=condition, + actions=[action1, action2] + ) + + :param detector: The detector ID or Detector object to add the rule to + :param name: A unique name to identify this rule + :param enabled: Whether the rule should be active when created (default True) + :param snooze_time_enabled: Enable notification snoozing to prevent alert spam (default False) + :param snooze_time_value: Duration of snooze period (default 3600) + :param snooze_time_unit: Unit for snooze duration - "SECONDS", "MINUTES", "HOURS", or "DAYS" (default "SECONDS") + :param human_review_required: Require human verification before sending alerts (default False) + + :return: The created Alert object + """ + if isinstance(actions, Action): + actions = [actions] + elif isinstance(actions, ActionList): + actions = actions.root + if isinstance(detector, Detector): + detector = detector.id + # translate pydantic type to the openapi type + actions = [ActionRequest(channel=ChannelEnum(action.channel), recipient=action.recipient, include_image=action.include_image) for action in actions] + rule_input = RuleRequest( + detector_id=detector, + name=name, + enabled=enabled, + action=actions, + condition=ConditionRequest(verb=VerbEnum(condition.verb), parameters=condition.parameters), + snooze_time_enabled=snooze_time_enabled, + snooze_time_value=snooze_time_value, + snooze_time_unit=snooze_time_unit, + human_review_required=human_review_required, + ) + return Rule.model_validate(self.actions_api.create_rule(detector, rule_input).to_dict()) + def create_rule( # pylint: disable=too-many-locals # noqa: PLR0913 self, detector: Union[str, Detector], @@ -168,6 +304,9 @@ def create_rule( # pylint: disable=too-many-locals # noqa: PLR0913 :return: The created Rule object """ + + logger.warning("create_rule is no longer supported. Please use create_alert instead.") + if condition_parameters is None: condition_parameters = {} if isinstance(alert_on, str): diff --git a/test/unit/test_actions.py b/test/unit/test_actions.py index d620255b..c5749b25 100644 --- a/test/unit/test_actions.py +++ b/test/unit/test_actions.py @@ -52,3 +52,18 @@ def test_delete_action(gl_experimental: ExperimentalApi): gl_experimental.delete_rule(rule.id) with pytest.raises(NotFoundException) as _: gl_experimental.get_rule(rule.id) + + +def test_create_alert_multiple_actions(gl_experimental: ExperimentalApi): + name = f"Test {datetime.utcnow()}" + det = gl_experimental.get_or_create_detector(name, "test_query") + condition = gl_experimental.make_condition("CHANGED_TO", {"label": "YES"}) + action1 = gl_experimental.make_action("EMAIL", "test@groundlight.ai", False) + action2 = gl_experimental.make_action("EMAIL", "test@groundlight.ai", False) + alert = gl_experimental.create_alert( + det, + f"test_alert_{name}", + condition, + [action1, action2], + ) + assert len(alert.action.root) == 2 \ No newline at end of file From a94309857fdc1544deb5be2b6c145fe897dafed6 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Fri, 10 Jan 2025 22:00:39 +0000 Subject: [PATCH 2/8] Automatically reformatting code --- src/groundlight/experimental_api.py | 59 +++++++++++++++++------------ test/unit/test_actions.py | 2 +- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index aaee1cab..c3028d95 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -16,7 +16,6 @@ from groundlight_openapi_client.api.detector_reset_api import DetectorResetApi from groundlight_openapi_client.api.image_queries_api import ImageQueriesApi from groundlight_openapi_client.api.notes_api import NotesApi - from groundlight_openapi_client.model.action_request import ActionRequest from groundlight_openapi_client.model.channel_enum import ChannelEnum from groundlight_openapi_client.model.condition_request import ConditionRequest @@ -28,7 +27,18 @@ from groundlight_openapi_client.model.rule_request import RuleRequest from groundlight_openapi_client.model.status_enum import StatusEnum from groundlight_openapi_client.model.verb_enum import VerbEnum -from model import ROI, Action, ActionList, BBoxGeometry, Condition, Detector, DetectorGroup, ModeEnum, PaginatedRuleList, Rule +from model import ( + ROI, + Action, + ActionList, + BBoxGeometry, + Condition, + Detector, + DetectorGroup, + ModeEnum, + PaginatedRuleList, + Rule, +) from groundlight.images import parse_supported_image_types from groundlight.optional_imports import Image, np @@ -94,33 +104,29 @@ def __init__( ITEMS_PER_PAGE = 100 - def make_condition( - self, - verb: str, - parameters: dict - ) -> Condition: - """ - Creates a Condition object for use in creating alerts + def make_condition(self, verb: str, parameters: dict) -> Condition: + """ + Creates a Condition object for use in creating alerts - This function serves as a convenience method; Condition objects can also be created directly. + This function serves as a convenience method; Condition objects can also be created directly. - **Example usage**:: + **Example usage**:: - gl = ExperimentalApi() + gl = ExperimentalApi() - # Create a condition for a rule - condition = gl.make_condition("CHANGED_TO", {"label": "YES"}) + # Create a condition for a rule + condition = gl.make_condition("CHANGED_TO", {"label": "YES"}) - :param verb: The condition verb to use. One of "ANSWERED_CONSECUTIVELY", "ANSWERED_WITHIN_TIME", - "CHANGED_TO", "NO_CHANGE", "NO_QUERIES" - :param condition_parameters: Additional parameters for the condition, dependant on the verb: - - For ANSWERED_CONSECUTIVELY: {"num_consecutive_labels": N, "label": "YES/NO"} - - For CHANGED_TO: {"label": "YES/NO"} - - For ANSWERED_WITHIN_TIME: {"time_value": N, "time_unit": "MINUTES/HOURS/DAYS"} + :param verb: The condition verb to use. One of "ANSWERED_CONSECUTIVELY", "ANSWERED_WITHIN_TIME", + "CHANGED_TO", "NO_CHANGE", "NO_QUERIES" + :param condition_parameters: Additional parameters for the condition, dependant on the verb: + - For ANSWERED_CONSECUTIVELY: {"num_consecutive_labels": N, "label": "YES/NO"} + - For CHANGED_TO: {"label": "YES/NO"} + - For ANSWERED_WITHIN_TIME: {"time_value": N, "time_unit": "MINUTES/HOURS/DAYS"} - :return: The created Condition object - """ - return Condition(verb=verb, parameters=parameters) + :return: The created Condition object + """ + return Condition(verb=verb, parameters=parameters) def make_action( self, @@ -215,7 +221,12 @@ def create_alert( if isinstance(detector, Detector): detector = detector.id # translate pydantic type to the openapi type - actions = [ActionRequest(channel=ChannelEnum(action.channel), recipient=action.recipient, include_image=action.include_image) for action in actions] + actions = [ + ActionRequest( + channel=ChannelEnum(action.channel), recipient=action.recipient, include_image=action.include_image + ) + for action in actions + ] rule_input = RuleRequest( detector_id=detector, name=name, diff --git a/test/unit/test_actions.py b/test/unit/test_actions.py index c5749b25..cb7411a2 100644 --- a/test/unit/test_actions.py +++ b/test/unit/test_actions.py @@ -66,4 +66,4 @@ def test_create_alert_multiple_actions(gl_experimental: ExperimentalApi): condition, [action1, action2], ) - assert len(alert.action.root) == 2 \ No newline at end of file + assert len(alert.action.root) == 2 From 0472ff2a29552d27823e6f6298e9a86f78cfbe03 Mon Sep 17 00:00:00 2001 From: brandon Date: Fri, 10 Jan 2025 13:57:26 -0800 Subject: [PATCH 3/8] Adds ability to create alerts with multiple actions --- src/groundlight/experimental_api.py | 143 +++++++++++++++++++++++++++- test/unit/test_actions.py | 15 +++ 2 files changed, 156 insertions(+), 2 deletions(-) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index 5b8765b8..aaee1cab 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -16,6 +16,7 @@ from groundlight_openapi_client.api.detector_reset_api import DetectorResetApi from groundlight_openapi_client.api.image_queries_api import ImageQueriesApi from groundlight_openapi_client.api.notes_api import NotesApi + from groundlight_openapi_client.model.action_request import ActionRequest from groundlight_openapi_client.model.channel_enum import ChannelEnum from groundlight_openapi_client.model.condition_request import ConditionRequest @@ -27,12 +28,12 @@ from groundlight_openapi_client.model.rule_request import RuleRequest from groundlight_openapi_client.model.status_enum import StatusEnum from groundlight_openapi_client.model.verb_enum import VerbEnum -from model import ROI, BBoxGeometry, Detector, DetectorGroup, ModeEnum, PaginatedRuleList, Rule +from model import ROI, Action, ActionList, BBoxGeometry, Condition, Detector, DetectorGroup, ModeEnum, PaginatedRuleList, Rule from groundlight.images import parse_supported_image_types from groundlight.optional_imports import Image, np -from .client import DEFAULT_REQUEST_TIMEOUT, Groundlight +from .client import DEFAULT_REQUEST_TIMEOUT, Groundlight, logger class ExperimentalApi(Groundlight): @@ -93,6 +94,141 @@ def __init__( ITEMS_PER_PAGE = 100 + def make_condition( + self, + verb: str, + parameters: dict + ) -> Condition: + """ + Creates a Condition object for use in creating alerts + + This function serves as a convenience method; Condition objects can also be created directly. + + **Example usage**:: + + gl = ExperimentalApi() + + # Create a condition for a rule + condition = gl.make_condition("CHANGED_TO", {"label": "YES"}) + + :param verb: The condition verb to use. One of "ANSWERED_CONSECUTIVELY", "ANSWERED_WITHIN_TIME", + "CHANGED_TO", "NO_CHANGE", "NO_QUERIES" + :param condition_parameters: Additional parameters for the condition, dependant on the verb: + - For ANSWERED_CONSECUTIVELY: {"num_consecutive_labels": N, "label": "YES/NO"} + - For CHANGED_TO: {"label": "YES/NO"} + - For ANSWERED_WITHIN_TIME: {"time_value": N, "time_unit": "MINUTES/HOURS/DAYS"} + + :return: The created Condition object + """ + return Condition(verb=verb, parameters=parameters) + + def make_action( + self, + channel: str, + recipient: str, + include_image: bool, + ) -> Action: + """ + Creates an Action object for use in creating alerts + + This function serves as a convenience method; Action objects can also be created directly. + + **Example usage**:: + + gl = ExperimentalApi() + + # Create an action for a rule + action = gl.make_action("EMAIL", "example@example.com", include_image=True) + + :param channel: The notification channel to use. One of "EMAIL" or "TEXT" + :param recipient: The email address or phone number to send notifications to + :param include_image: Whether to include the triggering image in notifications + """ + return Action( + channel=channel, + recipient=recipient, + include_image=include_image, + ) + + def create_alert( + self, + detector: Union[str, Detector], + name, + condition: Condition, + actions: Union[Action, List[Action], ActionList], + *, + enabled: bool = True, + snooze_time_enabled: bool = False, + snooze_time_value: int = 3600, + snooze_time_unit: str = "SECONDS", + human_review_required: bool = False, + ) -> Rule: + """ + Creates an alert for a detector that will trigger actions based on specified conditions. + + An alert allows you to configure automated actions when certain conditions are met, + such as when a detector's prediction changes or maintains a particular state. + + .. note:: + Currently, only binary mode detectors (YES/NO answers) are supported for notification rules. + + **Example usage**:: + + gl = ExperimentalApi() + + # Create a rule to send email alerts when door is detected as open + condition = gl.make_condition( + verb="CHANGED_TO", + parameters={"label": "YES"} + ) + action1 = gl.make_action( + "EMAIL", + "alerts@company.com", + include_image=True + ) + action2 = gl.make_action( + "TEXT", + "+1234567890", + include_image=False + ) + alert = gl.create_alert( + detector="det_idhere", + name="Door Open Alert", + condition=condition, + actions=[action1, action2] + ) + + :param detector: The detector ID or Detector object to add the rule to + :param name: A unique name to identify this rule + :param enabled: Whether the rule should be active when created (default True) + :param snooze_time_enabled: Enable notification snoozing to prevent alert spam (default False) + :param snooze_time_value: Duration of snooze period (default 3600) + :param snooze_time_unit: Unit for snooze duration - "SECONDS", "MINUTES", "HOURS", or "DAYS" (default "SECONDS") + :param human_review_required: Require human verification before sending alerts (default False) + + :return: The created Alert object + """ + if isinstance(actions, Action): + actions = [actions] + elif isinstance(actions, ActionList): + actions = actions.root + if isinstance(detector, Detector): + detector = detector.id + # translate pydantic type to the openapi type + actions = [ActionRequest(channel=ChannelEnum(action.channel), recipient=action.recipient, include_image=action.include_image) for action in actions] + rule_input = RuleRequest( + detector_id=detector, + name=name, + enabled=enabled, + action=actions, + condition=ConditionRequest(verb=VerbEnum(condition.verb), parameters=condition.parameters), + snooze_time_enabled=snooze_time_enabled, + snooze_time_value=snooze_time_value, + snooze_time_unit=snooze_time_unit, + human_review_required=human_review_required, + ) + return Rule.model_validate(self.actions_api.create_rule(detector, rule_input).to_dict()) + def create_rule( # pylint: disable=too-many-locals # noqa: PLR0913 self, detector: Union[str, Detector], @@ -168,6 +304,9 @@ def create_rule( # pylint: disable=too-many-locals # noqa: PLR0913 :return: The created Rule object """ + + logger.warning("create_rule is no longer supported. Please use create_alert instead.") + if condition_parameters is None: condition_parameters = {} if isinstance(alert_on, str): diff --git a/test/unit/test_actions.py b/test/unit/test_actions.py index d620255b..cb7411a2 100644 --- a/test/unit/test_actions.py +++ b/test/unit/test_actions.py @@ -52,3 +52,18 @@ def test_delete_action(gl_experimental: ExperimentalApi): gl_experimental.delete_rule(rule.id) with pytest.raises(NotFoundException) as _: gl_experimental.get_rule(rule.id) + + +def test_create_alert_multiple_actions(gl_experimental: ExperimentalApi): + name = f"Test {datetime.utcnow()}" + det = gl_experimental.get_or_create_detector(name, "test_query") + condition = gl_experimental.make_condition("CHANGED_TO", {"label": "YES"}) + action1 = gl_experimental.make_action("EMAIL", "test@groundlight.ai", False) + action2 = gl_experimental.make_action("EMAIL", "test@groundlight.ai", False) + alert = gl_experimental.create_alert( + det, + f"test_alert_{name}", + condition, + [action1, action2], + ) + assert len(alert.action.root) == 2 From a07a0d3e3053ff60ce09cd1f3067d139a953ff19 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Fri, 10 Jan 2025 22:26:57 +0000 Subject: [PATCH 4/8] Automatically reformatting code --- src/groundlight/experimental_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index 5726f8ad..c3028d95 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -16,7 +16,6 @@ from groundlight_openapi_client.api.detector_reset_api import DetectorResetApi from groundlight_openapi_client.api.image_queries_api import ImageQueriesApi from groundlight_openapi_client.api.notes_api import NotesApi - from groundlight_openapi_client.model.action_request import ActionRequest from groundlight_openapi_client.model.channel_enum import ChannelEnum from groundlight_openapi_client.model.condition_request import ConditionRequest From 0c098ab90216fd2b9bcf29d1d73d12cca1ce38b3 Mon Sep 17 00:00:00 2001 From: brandon Date: Fri, 10 Jan 2025 14:33:49 -0800 Subject: [PATCH 5/8] test update to handle new result types --- test/integration/test_groundlight.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index e344f784..bc45dfd7 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -37,7 +37,8 @@ def is_valid_display_result(result: Any) -> bool: and not isinstance(result, MultiClassificationResult) ): return False - if not is_valid_display_label(result.label): + + if isinstance(result, BinaryClassificationResult) and not is_valid_display_label(result.label): return False return True From ffbb2db25ced4d5a30d9ae31315679b7664c12ee Mon Sep 17 00:00:00 2001 From: brandon Date: Fri, 10 Jan 2025 15:23:53 -0800 Subject: [PATCH 6/8] appease the linters --- src/groundlight/experimental_api.py | 2 +- test/unit/test_actions.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index c3028d95..73b565a2 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -156,7 +156,7 @@ def make_action( include_image=include_image, ) - def create_alert( + def create_alert( # pylint: disable=too-many-locals # noqa: PLR0913 self, detector: Union[str, Detector], name, diff --git a/test/unit/test_actions.py b/test/unit/test_actions.py index cb7411a2..a437acc6 100644 --- a/test/unit/test_actions.py +++ b/test/unit/test_actions.py @@ -60,10 +60,11 @@ def test_create_alert_multiple_actions(gl_experimental: ExperimentalApi): condition = gl_experimental.make_condition("CHANGED_TO", {"label": "YES"}) action1 = gl_experimental.make_action("EMAIL", "test@groundlight.ai", False) action2 = gl_experimental.make_action("EMAIL", "test@groundlight.ai", False) + actions = [action1, action2] alert = gl_experimental.create_alert( det, f"test_alert_{name}", condition, - [action1, action2], + actions, ) - assert len(alert.action.root) == 2 + assert len(alert.action.root) == len(actions) From 7d19208a61d6dc874492ca37d99ee9ce025864bc Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Fri, 10 Jan 2025 23:24:44 +0000 Subject: [PATCH 7/8] Automatically reformatting code --- src/groundlight/experimental_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index 73b565a2..23bb17ff 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -156,7 +156,7 @@ def make_action( include_image=include_image, ) - def create_alert( # pylint: disable=too-many-locals # noqa: PLR0913 + def create_alert( # pylint: disable=too-many-locals # noqa: PLR0913 self, detector: Union[str, Detector], name, From 116c6aac1a32fd1a0794b9933d52a0a0977fe8d6 Mon Sep 17 00:00:00 2001 From: brandon Date: Fri, 10 Jan 2025 15:50:17 -0800 Subject: [PATCH 8/8] good comments --- src/groundlight/experimental_api.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index 73b565a2..3876a6e8 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -143,12 +143,12 @@ def make_action( gl = ExperimentalApi() - # Create an action for a rule + # Create an action for an alert action = gl.make_action("EMAIL", "example@example.com", include_image=True) :param channel: The notification channel to use. One of "EMAIL" or "TEXT" :param recipient: The email address or phone number to send notifications to - :param include_image: Whether to include the triggering image in notifications + :param include_image: Whether to include the triggering image in action message """ return Action( channel=channel, @@ -176,7 +176,7 @@ def create_alert( # pylint: disable=too-many-locals # noqa: PLR0913 such as when a detector's prediction changes or maintains a particular state. .. note:: - Currently, only binary mode detectors (YES/NO answers) are supported for notification rules. + Currently, only binary mode detectors (YES/NO answers) are supported for alerts. **Example usage**:: @@ -204,9 +204,9 @@ def create_alert( # pylint: disable=too-many-locals # noqa: PLR0913 actions=[action1, action2] ) - :param detector: The detector ID or Detector object to add the rule to - :param name: A unique name to identify this rule - :param enabled: Whether the rule should be active when created (default True) + :param detector: The detector ID or Detector object to add the alert to + :param name: A unique name to identify this alert + :param enabled: Whether the alert should be active when created (default True) :param snooze_time_enabled: Enable notification snoozing to prevent alert spam (default False) :param snooze_time_value: Duration of snooze period (default 3600) :param snooze_time_unit: Unit for snooze duration - "SECONDS", "MINUTES", "HOURS", or "DAYS" (default "SECONDS")