feat(api): sessions-search TIME_BETWEEN_EVENTS

This commit is contained in:
Taha Yassine Kraiem 2021-12-24 21:01:30 +01:00
parent 7349e7c560
commit adee10929f
2 changed files with 97 additions and 17 deletions

View file

@ -1,6 +1,6 @@
import schemas
from chalicelib.core import events, sessions_metas, metadata, events_ios, \
sessions_mobs, issues, projects, errors, resources, assist
sessions_mobs, issues, projects, errors, resources, assist, performance_event
from chalicelib.utils import pg_client, helper, dev
SESSION_PROJECTION_COLS = """s.project_id,
@ -318,16 +318,16 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, f
"main.session_id=event_0.session_id"]
if data.events_order == schemas.SearchEventOrder._then:
event_where.append(f"event_{event_index - 1}.timestamp <= main.timestamp")
event.value = helper.values_for_operator(value=event.value, op=event.operator)
# event_args = _multiple_values(event.value)
e_k = f"e_value{i}"
full_args = {**full_args, **_multiple_values(event.value, value_key=e_k)}
if event_type not in list(events.SUPPORTED_TYPES.keys()) \
or event.value in [None, "", "*"] \
and (event_type != events.event_type.ERROR.ui_type \
or event_type != events.event_type.ERROR_IOS.ui_type):
continue
if event.type != schemas.PerformanceEventType.time_between_events:
event.value = helper.values_for_operator(value=event.value, op=event.operator)
full_args = {**full_args, **_multiple_values(event.value, value_key=e_k)}
# if event_type not in list(events.SUPPORTED_TYPES.keys()) \
# or event.value in [None, "", "*"] \
# and (event_type != events.event_type.ERROR.ui_type \
# or event_type != events.event_type.ERROR_IOS.ui_type):
# continue
if event_type == events.event_type.CLICK.ui_type:
event_from = event_from % f"{events.event_type.CLICK.table} AS main "
if not is_any:
@ -432,6 +432,52 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, f
event_where.append(
_multiple_conditions(f"(main1.reason {op} %({e_k})s OR main1.name {op} %({e_k})s)",
event.value, value_key=e_k))
elif event_type == [schemas.PerformanceEventType.location_dom_complete,
schemas.PerformanceEventType.location_largest_contentful_paint_time]:
event_from = event_from % f"{events.event_type.LOCATION.table} AS main "
if not is_any:
event_where.append(
_multiple_conditions(f"main.{events.event_type.LOCATION.column} {op} %({e_k})s",
event.value, value_key=e_k))
e_k += "_custom"
full_args = {**full_args, **_multiple_values(event.custom, value_key=e_k)}
event_where.append(
_multiple_conditions(
f"main.{performance_event.get_col(event_type)} {event.customOperator} %({e_k})s",
event.custom, value_key=e_k))
elif event_type == schemas.PerformanceEventType.time_between_events:
event_from = event_from % f"{getattr(events.event_type, event.value[0].type).table} AS main INNER JOIN {getattr(events.event_type, event.value[1].type).table} AS main2 USING(session_id) "
if not isinstance(event.value[0].value, list):
event.value[0].value = [event.value[0].value]
if not isinstance(event.value[1].value, list):
event.value[1].value = [event.value[1].value]
event.value[0].value = helper.values_for_operator(value=event.value[0].value, op=event.operator)
event.value[1].value = helper.values_for_operator(value=event.value[1].value, op=event.operator)
e_k1 = e_k + "_e1"
e_k2 = e_k + "_e2"
full_args = {**full_args,
**_multiple_values(event.value[0].value, value_key=e_k1),
**_multiple_values(event.value[1].value, value_key=e_k2)}
s_op = __get_sql_operator(event.value[0].operator)
event_where += ["main2.timestamp >= %(startDate)s", "main2.timestamp <= %(endDate)s"]
if event_index > 0 and not or_events:
event_where.append("main2.session_id=event_0.session_id")
event_where.append(
_multiple_conditions(
f"main.{getattr(events.event_type, event.value[0].type).column} {s_op} %({e_k1})s",
event.value[0].value, value_key=e_k1))
s_op = __get_sql_operator(event.value[1].operator)
event_where.append(
_multiple_conditions(
f"main2.{getattr(events.event_type, event.value[1].type).column} {s_op} %({e_k2})s",
event.value[1].value, value_key=e_k2))
e_k += "_custom"
full_args = {**full_args, **_multiple_values(event.custom, value_key=e_k)}
event_where.append(
_multiple_conditions(f"main2.timestamp - main.timestamp {event.customOperator} %({e_k})s",
event.custom, value_key=e_k))
else:
continue
@ -464,7 +510,7 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, f
""")
else:
events_query_from.append(f"""\
(SELECT main.session_id, MIN(timestamp) AS timestamp
(SELECT main.session_id, MIN(main.timestamp) AS timestamp
FROM {event_from}
WHERE {" AND ".join(event_where)}
GROUP BY 1

View file

@ -1,5 +1,5 @@
from enum import Enum
from typing import Optional, List, Literal, Union
from typing import Optional, List, Union
from pydantic import BaseModel, Field, EmailStr, HttpUrl, root_validator
@ -312,10 +312,18 @@ class AlertColumn(str, Enum):
errors__backend__count = "errors.backend.count"
class MathOperator(str, Enum):
_less = "<"
_greater = ">"
_less_eq = "<="
_greater_eq = ">="
class _AlertQuerySchema(BaseModel):
left: AlertColumn = Field(...)
right: float = Field(...)
operator: Literal["<", ">", "<=", ">="] = Field(...)
# operator: Literal["<", ">", "<=", ">="] = Field(...)
operator: MathOperator = Field(...)
class AlertSchema(BaseModel):
@ -360,6 +368,12 @@ class EventType(str, Enum):
error_ios = "ERROR_IOS"
class PerformanceEventType(str, Enum):
location_dom_complete = "DOM_COMPLETE"
location_largest_contentful_paint_time = "LARGEST_CONTENTFUL_PAINT_TIME"
time_between_events = "TIME_BETWEEN_EVENTS"
class FilterType(str, Enum):
user_os = "USEROS"
user_browser = "USERBROWSER"
@ -399,6 +413,7 @@ class SearchEventOperator(str, Enum):
class PlatformType(str, Enum):
mobile = "mobile"
desktop = "desktop"
tablet = "tablet"
class SearchEventOrder(str, Enum):
@ -422,14 +437,33 @@ class IssueType(str, Enum):
js_exception = 'js_exception'
class _SessionSearchEventSchema(BaseModel):
custom: Optional[List[str]] = Field(None)
class _SessionSearchEventRaw(BaseModel):
custom: Optional[List[Union[str, int]]] = Field(None)
customOperator: Optional[MathOperator] = Field(None)
key: Optional[str] = Field(None)
value: Union[Optional[str], Optional[List[str]]] = Field(...)
type: EventType = Field(...)
value: Union[str, List[str]] = Field(...)
type: Union[EventType, PerformanceEventType] = Field(...)
operator: SearchEventOperator = Field(...)
source: Optional[ErrorSource] = Field(default=ErrorSource.js_exception)
@root_validator
def check_card_number_omitted(cls, values):
if isinstance(values.get("type"), PerformanceEventType):
assert values.get("custom") is not None, "custom should not be None for PerformanceEventType"
assert values.get("customOperator") is not None \
, "customOperator should not be None for PerformanceEventType"
if values["type"] == PerformanceEventType.time_between_events:
assert len(values.get("value", [])) == 2, \
f"must provide 2 Events as value for {PerformanceEventType.time_between_events}"
assert isinstance(values["value"][0], _SessionSearchEventRaw) \
and isinstance(values["value"][1], _SessionSearchEventRaw) \
, f"event should be of type _SessionSearchEventRaw for {PerformanceEventType.time_between_events}"
return values
class _SessionSearchEventSchema(_SessionSearchEventRaw):
value: Union[List[_SessionSearchEventRaw], str, List[str]] = Field(...)
class _SessionSearchFilterSchema(BaseModel):
custom: Optional[List[str]] = Field(None)