Skip to content

Commit bc9899d

Browse files
aliciamatsumotoAlicia Matsumoto
and
Alicia Matsumoto
authored
add signal statistics support per signal (#5852)
* extend signal stats per signal id * remove debugging statements * rename signal data test file to signal stats --------- Co-authored-by: Alicia Matsumoto <[email protected]>
1 parent 59cc0e3 commit bc9899d

File tree

3 files changed

+109
-3
lines changed

3 files changed

+109
-3
lines changed

src/dispatch/signal/service.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -971,9 +971,13 @@ def get_cases_for_signal_by_resolution_reason(
971971

972972

973973
def get_signal_stats(
974-
*, db_session: Session, entity_value: str, entity_type_id: int, num_days: int | None
974+
*, db_session: Session, entity_value: str, entity_type_id: int, signal_id: int | None = None, num_days: int | None = None
975975
) -> Optional[SignalStats]:
976-
"""Gets a signal statistics for a given named entity and type."""
976+
"""
977+
Gets signal statistics for a given named entity and type.
978+
979+
If signal_id is provided, only returns stats for that specific signal definition.
980+
"""
977981
entity_subquery = (
978982
db_session.query(
979983
func.jsonb_build_array(
@@ -1027,6 +1031,7 @@ def get_signal_stats(
10271031
and_(
10281032
Entity.value == entity_value,
10291033
Entity.entity_type_id == entity_type_id,
1034+
SignalInstance.signal_id == signal_id if signal_id else True,
10301035
SignalInstance.created_at >= date_threshold if date_threshold else True,
10311036
)
10321037
)

src/dispatch/signal/views.py

+27-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def return_signal_stats(
290290
entity_type_id: int = Query(..., description="The ID of the entity type"),
291291
num_days: int = Query(None, description="The number of days to look back"),
292292
):
293-
"""Gets a signal statistics given a named entity and entity type id."""
293+
"""Gets signal statistics given a named entity and entity type id."""
294294
signal_data = get_signal_stats(
295295
db_session=db_session,
296296
entity_value=entity_value,
@@ -300,6 +300,32 @@ def return_signal_stats(
300300
return signal_data
301301

302302

303+
@router.get("/{signal_id}/stats", response_model=SignalStats)
304+
def return_single_signal_stats(
305+
db_session: DbSession,
306+
signal_id: Union[str, PrimaryKey],
307+
entity_value: str = Query(..., description="The name of the entity"),
308+
entity_type_id: int = Query(..., description="The ID of the entity type"),
309+
num_days: int = Query(None, description="The number of days to look back"),
310+
):
311+
"""Gets signal statistics for a specific signal given a named entity and entity type id."""
312+
signal = get_by_primary_or_external_id(db_session=db_session, signal_id=signal_id)
313+
if not signal:
314+
raise HTTPException(
315+
status_code=status.HTTP_404_NOT_FOUND,
316+
detail=[{"msg": "A signal with this id does not exist."}],
317+
)
318+
319+
signal_data = get_signal_stats(
320+
db_session=db_session,
321+
entity_value=entity_value,
322+
entity_type_id=entity_type_id,
323+
signal_id=signal.id,
324+
num_days=num_days,
325+
)
326+
return signal_data
327+
328+
303329
@router.get("/{signal_id}", response_model=SignalRead)
304330
def get_signal(db_session: DbSession, signal_id: Union[str, PrimaryKey]):
305331
"""Gets a signal by its id."""

tests/signal/test_signal_data_service.py tests/signal/test_signal_stats_service.py

+75
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,78 @@ def test_get_signal_stats_not_found(session, entity_type):
161161
assert signal_data.num_signal_instances_snoozed == 0
162162
assert signal_data.num_snoozes_active == 0
163163
assert signal_data.num_snoozes_expired == 0
164+
165+
166+
def test_get_signal_stats_with_signal_id_filter(session, entity, entity_type):
167+
"""Test get_signal_stats with a specific signal ID filter."""
168+
from dispatch.signal.service import get_signal_stats
169+
from dispatch.signal.models import Signal, SignalInstance
170+
171+
# Setup: Create two different signals
172+
signal1 = Signal(name="Test Signal 1", variant="test-signal-1", project_id=1)
173+
signal2 = Signal(name="Test Signal 2", variant="test-signal-2", project_id=1)
174+
session.add_all([signal1, signal2])
175+
session.flush()
176+
177+
# Associate entity with entity type
178+
entity.entity_type = entity_type
179+
180+
# Create a signal instance for signal1
181+
signal_instance1 = SignalInstance(signal=signal1, project_id=1)
182+
signal_instance1.entities.append(entity)
183+
184+
# Create two signal instances for signal2
185+
signal_instance2 = SignalInstance(signal=signal2, project_id=1)
186+
signal_instance2.entities.append(entity)
187+
signal_instance3 = SignalInstance(signal=signal2, project_id=1)
188+
signal_instance3.entities.append(entity)
189+
190+
session.add_all([signal_instance1, signal_instance2, signal_instance3])
191+
session.commit()
192+
193+
# Execute: Call the service function without signal_id (should count both signals)
194+
signal_data_all = get_signal_stats(
195+
db_session=session,
196+
entity_value=entity.value,
197+
entity_type_id=entity_type.id,
198+
num_days=None,
199+
)
200+
201+
# Execute: Call the service function with signal_id for signal1
202+
signal_data_signal1 = get_signal_stats(
203+
db_session=session,
204+
entity_value=entity.value,
205+
entity_type_id=entity_type.id,
206+
signal_id=signal1.id,
207+
num_days=None,
208+
)
209+
210+
# Execute: Call the service function with signal_id for signal2
211+
signal_data_signal2 = get_signal_stats(
212+
db_session=session,
213+
entity_value=entity.value,
214+
entity_type_id=entity_type.id,
215+
signal_id=signal2.id,
216+
num_days=None,
217+
)
218+
219+
# Assert: Without signal_id filter, we should count both instances
220+
assert (
221+
signal_data_all.num_signal_instances_alerted
222+
+ signal_data_all.num_signal_instances_snoozed
223+
== 3
224+
)
225+
226+
# Assert: With signal1 filter, we should count only signal_instance1
227+
assert (
228+
signal_data_signal1.num_signal_instances_alerted
229+
+ signal_data_signal1.num_signal_instances_snoozed
230+
== 1
231+
)
232+
233+
# Assert: With signal2 filter, we should count only signal_instance2
234+
assert (
235+
signal_data_signal2.num_signal_instances_alerted
236+
+ signal_data_signal2.num_signal_instances_snoozed
237+
== 2
238+
)

0 commit comments

Comments
 (0)