diff --git a/api/Pipfile b/api/Pipfile index 573bad09a..d7cdf4e58 100644 --- a/api/Pipfile +++ b/api/Pipfile @@ -5,7 +5,7 @@ name = "pypi" [packages] requests = "==2.31.0" -boto3 = "==1.28.63" +boto3 = "==1.28.64" pyjwt = "==2.8.0" psycopg2-binary = "==2.9.9" elasticsearch = "==8.10.1" diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 2a46b2d62..8a3d04cd8 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -101,7 +101,7 @@ def __get_click_map_chart(project_id, user_id, data: schemas.CardClickMap, inclu def __get_path_analysis_chart(project_id: int, user_id: int, data: schemas.CardPathAnalysis): if len(data.series) == 0: data.series.append( - schemas.CardPathAnalysisSchema(startTimestamp=data.startTimestamp, endTimestamp=data.endTimestamp)) + schemas.CardPathAnalysisSeriesSchema(startTimestamp=data.startTimestamp, endTimestamp=data.endTimestamp)) elif not isinstance(data.series[0].filter, schemas.PathAnalysisSchema): data.series[0].filter = schemas.PathAnalysisSchema() @@ -294,33 +294,28 @@ def __get_funnel_issues(project_id: int, user_id: int, data: schemas.CardFunnel) def __get_path_analysis_issues(project_id: int, user_id: int, data: schemas.CardPathAnalysis): if len(data.series) == 0: return {"data": {}} - card_table = schemas.CardTable( + search_data = schemas.SessionsSearchPayloadSchema( startTimestamp=data.startTimestamp, endTimestamp=data.endTimestamp, - metricType=schemas.MetricType.table, - metricOf=schemas.MetricOfTable.issues, - viewType=schemas.MetricTableViewType.table, - series=data.model_dump()["series"], limit=data.limit, - page=data.page + page=data.page, + filters=data.series[0].filter.model_dump(by_alias=True)["filters"] ) + for s in data.start_point: if data.start_type == "end": - card_table.series[0].filter.filters.append(schemas.SessionSearchEventSchema2(type=s.type, - operator=s.operator, - value=s.value)) + search_data.filters.append(schemas.SessionSearchEventSchema2(type=s.type, + operator=s.operator, + value=s.value)) else: - card_table.series[0].filter.filters.insert(0, schemas.SessionSearchEventSchema2(type=s.type, - operator=s.operator, - value=s.value)) + search_data.filters.insert(0, schemas.SessionSearchEventSchema2(type=s.type, + operator=s.operator, + value=s.value)) for s in data.excludes: - card_table.series[0].filter.filters.append(schemas.SessionSearchEventSchema2(type=s.type, - operator=schemas.SearchEventOperator._not_on, - value=s.value)) - # result = __get_table_of_issues(project_id=project_id, user_id=user_id, data=card_table) - result = sessions.search_table_of_individual_issues(project_id=project_id, - metric_value=card_table.metric_value, - data=card_table) + search_data.filters.append(schemas.SessionSearchEventSchema2(type=s.type, + operator=schemas.SearchEventOperator._not_on, + value=s.value)) + result = sessions.search_table_of_individual_issues(project_id=project_id, data=search_data) return result diff --git a/api/chalicelib/core/product_analytics.py b/api/chalicelib/core/product_analytics.py index 2b8756f66..8ae29e165 100644 --- a/api/chalicelib/core/product_analytics.py +++ b/api/chalicelib/core/product_analytics.py @@ -68,7 +68,7 @@ JOURNEY_TYPES = { # compute avg_time_from_previous at the same level as sessions_count # sort by top 5 according to sessions_count at the CTE level # final part project data without grouping -def path_analysis(project_id: int, data: schemas.CardPathAnalysis): +def path_analysis_deprecated(project_id: int, data: schemas.CardPathAnalysis): sub_events = [] start_points_from = "pre_ranked_events" start_points_conditions = [] @@ -388,6 +388,344 @@ WITH sub_sessions AS (SELECT session_id return __transform_journey(rows=rows, reverse_path=reverse) + +# query: Q5, the result is correct, +# startPoints are computed before ranked_events to reduce the number of window functions over rows +# replaced time_to_target by time_from_previous +# compute avg_time_from_previous at the same level as sessions_count +# sort by top 5 according to sessions_count at the CTE level +# final part project data without grouping +# if start-point is selected, the selected event is ranked n°1 +def path_analysis(project_id: int, data: schemas.CardPathAnalysis): + sub_events = [] + start_points_from = "pre_ranked_events" + sub_sessions_extra_projection = "" + start_points_conditions = [] + sessions_conditions = ["start_ts>=%(startTimestamp)s", "start_ts<%(endTimestamp)s", + "project_id=%(project_id)s", "events_count > 1", "duration>0"] + if len(data.metric_value) == 0: + data.metric_value.append(schemas.ProductAnalyticsSelectedEventType.location) + sub_events.append({"table": JOURNEY_TYPES[schemas.ProductAnalyticsSelectedEventType.location]["table"], + "column": JOURNEY_TYPES[schemas.ProductAnalyticsSelectedEventType.location]["column"], + "eventType": schemas.ProductAnalyticsSelectedEventType.location.value}) + else: + for v in data.metric_value: + if JOURNEY_TYPES.get(v): + sub_events.append({"table": JOURNEY_TYPES[v]["table"], + "column": JOURNEY_TYPES[v]["column"], + "eventType": v}) + + extra_values = {} + start_join = [] + reverse = data.start_type == "end" + for i, sf in enumerate(data.start_point): + f_k = f"start_point_{i}" + op = sh.get_sql_operator(sf.operator) + is_not = sh.is_negation_operator(sf.operator) + extra_values = {**extra_values, **sh.multi_values(sf.value, value_key=f_k)} + start_points_conditions.append(f"(event_type='{sf.type}' AND " + + sh.multi_conditions(f'e_value {op} %({f_k})s', sf.value, is_not=is_not, + value_key=f_k) + + ")") + main_column = JOURNEY_TYPES[sf.type]["column"] + sessions_conditions.append(sh.multi_conditions(f'{main_column} {op} %({f_k})s', sf.value, is_not=is_not, + value_key=f_k)) + sessions_conditions += ["timestamp>=%(startTimestamp)s", "timestamp<%(endTimestamp)s"] + start_join.append(f"INNER JOIN {JOURNEY_TYPES[sf.type]['table']} USING (session_id)") + + exclusions = {} + for i, ef in enumerate(data.excludes): + if ef.type in data.metric_value: + f_k = f"exclude_{i}" + extra_values = {**extra_values, **sh.multi_values(ef.value, value_key=f_k)} + exclusions[ef.type] = [ + sh.multi_conditions(f'{JOURNEY_TYPES[ef.type]["column"]} != %({f_k})s', ef.value, is_not=True, + value_key=f_k)] + + meta_keys = None + for i, f in enumerate(data.series[0].filter.filters): + op = sh.get_sql_operator(f.operator) + is_any = sh.isAny_opreator(f.operator) + is_not = sh.is_negation_operator(f.operator) + is_undefined = sh.isUndefined_operator(f.operator) + f_k = f"f_value_{i}" + extra_values = {**extra_values, **sh.multi_values(f.value, value_key=f_k)} + + if not is_any and len(f.value) == 0: + continue + + # ---- meta-filters + if f.type == schemas.FilterType.user_browser: + if is_any: + sessions_conditions.append('user_browser IS NOT NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f'user_browser {op} %({f_k})s', f.value, is_not=is_not, value_key=f_k)) + + elif f.type in [schemas.FilterType.user_os]: + if is_any: + sessions_conditions.append('user_os IS NOT NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f'user_os {op} %({f_k})s', f.value, is_not=is_not, value_key=f_k)) + + elif f.type in [schemas.FilterType.user_device]: + if is_any: + sessions_conditions.append('user_device IS NOT NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f'user_device {op} %({f_k})s', f.value, is_not=is_not, value_key=f_k)) + + elif f.type in [schemas.FilterType.user_country]: + if is_any: + sessions_conditions.append('user_country IS NOT NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f'user_country {op} %({f_k})s', f.value, is_not=is_not, value_key=f_k)) + + elif f.type == schemas.FilterType.user_city: + if is_any: + sessions_conditions.append('user_city IS NOT NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f'user_city {op} %({f_k})s', f.value, is_not=is_not, value_key=f_k)) + + elif f.type == schemas.FilterType.user_state: + if is_any: + sessions_conditions.append('user_state IS NOT NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f'user_state {op} %({f_k})s', f.value, is_not=is_not, value_key=f_k)) + + elif f.type in [schemas.FilterType.utm_source]: + if is_any: + sessions_conditions.append('utm_source IS NOT NULL') + elif is_undefined: + sessions_conditions.append('utm_source IS NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f'utm_source {op} %({f_k})s::text', f.value, is_not=is_not, + value_key=f_k)) + + elif f.type in [schemas.FilterType.utm_medium]: + if is_any: + sessions_conditions.append('utm_medium IS NOT NULL') + elif is_undefined: + sessions_conditions.append('utm_medium IS NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f'utm_medium {op} %({f_k})s::text', f.value, is_not=is_not, + value_key=f_k)) + + elif f.type in [schemas.FilterType.utm_campaign]: + if is_any: + sessions_conditions.append('utm_campaign IS NOT NULL') + elif is_undefined: + sessions_conditions.append('utm_campaign IS NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f'utm_campaign {op} %({f_k})s::text', f.value, is_not=is_not, + value_key=f_k)) + + elif f.type == schemas.FilterType.duration: + if len(f.value) > 0 and f.value[0] is not None: + sessions_conditions.append("duration >= %(minDuration)s") + extra_values["minDuration"] = f.value[0] + if len(f.value) > 1 and f.value[1] is not None and int(f.value[1]) > 0: + sessions_conditions.append("duration <= %(maxDuration)s") + extra_values["maxDuration"] = f.value[1] + elif f.type == schemas.FilterType.referrer: + # extra_from += f"INNER JOIN {events.event_type.LOCATION.table} AS p USING(session_id)" + if is_any: + sessions_conditions.append('base_referrer IS NOT NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f"base_referrer {op} %({f_k})s", f.value, is_not=is_not, + value_key=f_k)) + elif f.type == schemas.FilterType.metadata: + # get metadata list only if you need it + if meta_keys is None: + meta_keys = metadata.get(project_id=project_id) + meta_keys = {m["key"]: m["index"] for m in meta_keys} + if f.source in meta_keys.keys(): + if is_any: + sessions_conditions.append(f"{metadata.index_to_colname(meta_keys[f.source])} IS NOT NULL") + elif is_undefined: + sessions_conditions.append(f"{metadata.index_to_colname(meta_keys[f.source])} IS NULL") + else: + sessions_conditions.append( + sh.multi_conditions( + f"{metadata.index_to_colname(meta_keys[f.source])} {op} %({f_k})s::text", + f.value, is_not=is_not, value_key=f_k)) + + elif f.type in [schemas.FilterType.user_id, schemas.FilterType.user_id_ios]: + if is_any: + sessions_conditions.append('user_id IS NOT NULL') + elif is_undefined: + sessions_conditions.append('user_id IS NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f"s.user_id {op} %({f_k})s::text", f.value, is_not=is_not, + value_key=f_k)) + + elif f.type in [schemas.FilterType.user_anonymous_id, + schemas.FilterType.user_anonymous_id_ios]: + if is_any: + sessions_conditions.append('user_anonymous_id IS NOT NULL') + elif is_undefined: + sessions_conditions.append('user_anonymous_id IS NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f"user_anonymous_id {op} %({f_k})s::text", f.value, is_not=is_not, + value_key=f_k)) + + elif f.type in [schemas.FilterType.rev_id, schemas.FilterType.rev_id_ios]: + if is_any: + sessions_conditions.append('rev_id IS NOT NULL') + elif is_undefined: + sessions_conditions.append('rev_id IS NULL') + else: + sessions_conditions.append( + sh.multi_conditions(f"rev_id {op} %({f_k})s::text", f.value, is_not=is_not, value_key=f_k)) + + elif f.type == schemas.FilterType.platform: + # op = __ sh.get_sql_operator(f.operator) + sessions_conditions.append( + sh.multi_conditions(f"user_device_type {op} %({f_k})s", f.value, is_not=is_not, + value_key=f_k)) + + elif f.type == schemas.FilterType.issue: + if is_any: + sessions_conditions.append("array_length(issue_types, 1) > 0") + else: + sessions_conditions.append( + sh.multi_conditions(f"%({f_k})s {op} ANY (issue_types)", f.value, is_not=is_not, + value_key=f_k)) + + elif f.type == schemas.FilterType.events_count: + sessions_conditions.append( + sh.multi_conditions(f"events_count {op} %({f_k})s", f.value, is_not=is_not, + value_key=f_k)) + events_subquery = [] + for t in sub_events: + sub_events_conditions = ["e.timestamp >= %(startTimestamp)s", + "e.timestamp < %(endTimestamp)s"] + exclusions.get(t["eventType"], []) + if len(start_points_conditions) > 0: + sub_events_conditions.append("e.timestamp >= sub_sessions.start_event_timestamp") + events_subquery.append(f"""\ + SELECT session_id, {t["column"]} AS e_value, timestamp, '{t["eventType"]}' AS event_type + FROM {t["table"]} AS e INNER JOIN sub_sessions USING (session_id) + WHERE {" AND ".join(sub_events_conditions)}""") + events_subquery = "\n UNION ALL \n".join(events_subquery) + + if reverse: + path_direction = "DESC" + else: + path_direction = "" + + if len(start_points_conditions) == 0: + start_points_from = """(SELECT event_type, e_value + FROM pre_ranked_events + WHERE event_number_in_session = 1 + GROUP BY event_type, e_value + ORDER BY count(1) DESC + LIMIT 1) AS top_start_events + INNER JOIN pre_ranked_events + USING (event_type, e_value)""" + else: + sub_sessions_extra_projection = ", MIN(timestamp) AS start_event_timestamp" + start_points_conditions = ["(" + " OR ".join(start_points_conditions) + ")"] + start_points_conditions.append("event_number_in_session = 1") + + steps_query = ["""n1 AS (SELECT event_number_in_session, + event_type, + e_value, + next_type, + next_value, + AVG(time_from_previous) AS avg_time_from_previous, + COUNT(1) AS sessions_count + FROM ranked_events INNER JOIN start_points USING (session_id) + WHERE event_number_in_session = 1 + AND next_value IS NOT NULL + GROUP BY event_number_in_session, event_type, e_value, next_type, next_value + ORDER BY sessions_count DESC + LIMIT %(eventThresholdNumberInGroup)s)"""] + projection_query = ["""(SELECT event_number_in_session, + event_type, + e_value, + next_type, + next_value, + sessions_count, + avg_time_from_previous + FROM n1)"""] + for i in range(2, data.density): + steps_query.append(f"""n{i} AS (SELECT * + FROM (SELECT re.event_number_in_session, + re.event_type, + re.e_value, + re.next_type, + re.next_value, + AVG(re.time_from_previous) AS avg_time_from_previous, + COUNT(1) AS sessions_count + FROM ranked_events AS re + INNER JOIN n{i - 1} ON (n{i - 1}.next_value = re.e_value) + WHERE re.event_number_in_session = {i} + GROUP BY re.event_number_in_session, re.event_type, re.e_value, re.next_type, re.next_value) AS sub_level + ORDER BY sessions_count DESC + LIMIT %(eventThresholdNumberInGroup)s)""") + projection_query.append(f"""(SELECT event_number_in_session, + event_type, + e_value, + next_type, + next_value, + sessions_count, + avg_time_from_previous + FROM n{i})""") + + with pg_client.PostgresClient() as cur: + pg_query = f"""\ +WITH sub_sessions AS (SELECT session_id {sub_sessions_extra_projection} + FROM public.sessions {" ".join(start_join)} + WHERE {" AND ".join(sessions_conditions)} + {"GROUP BY session_id" if len(start_points_conditions) > 0 else ""}), + sub_events AS ({events_subquery}), + pre_ranked_events AS (SELECT * + FROM (SELECT session_id, + event_type, + e_value, + timestamp, + row_number() OVER (PARTITION BY session_id ORDER BY timestamp {path_direction}) AS event_number_in_session + FROM sub_events + ORDER BY session_id) AS full_ranked_events + WHERE event_number_in_session < %(density)s), + start_points AS (SELECT session_id + FROM {start_points_from} + WHERE {" AND ".join(start_points_conditions)}), + ranked_events AS (SELECT *, + LEAD(e_value, 1) OVER (PARTITION BY session_id ORDER BY timestamp {path_direction}) AS next_value, + LEAD(event_type, 1) OVER (PARTITION BY session_id ORDER BY timestamp {path_direction}) AS next_type, + abs(LAG(timestamp, 1) OVER (PARTITION BY session_id ORDER BY timestamp {path_direction}) - + timestamp) AS time_from_previous + FROM pre_ranked_events INNER JOIN start_points USING (session_id)), + {",".join(steps_query)} +{"UNION ALL".join(projection_query)};""" + params = {"project_id": project_id, "startTimestamp": data.startTimestamp, + "endTimestamp": data.endTimestamp, "density": data.density, + "eventThresholdNumberInGroup": 4 if data.hide_excess else 8, + **extra_values} + query = cur.mogrify(pg_query, params) + _now = time() + + cur.execute(query) + if time() - _now > 2: + logger.info(f">>>>>>>>>PathAnalysis long query ({int(time() - _now)}s)<<<<<<<<<") + logger.info("----------------------") + logger.info(query) + logger.info("----------------------") + rows = cur.fetchall() + + return __transform_journey(rows=rows, reverse_path=reverse) + # # def __compute_weekly_percentage(rows): # if rows is None or len(rows) == 0: diff --git a/api/chalicelib/core/sessions.py b/api/chalicelib/core/sessions.py index 77056891f..0a685f7ca 100644 --- a/api/chalicelib/core/sessions.py +++ b/api/chalicelib/core/sessions.py @@ -344,11 +344,7 @@ def search2_table(data: schemas.SessionsSearchPayloadSchema, project_id: int, de return sessions -def search_table_of_individual_issues(data: schemas.SessionsSearchPayloadSchema, project_id: int, - metric_value: List): - if len(metric_value) > 0: - data.filters.append(schemas.SessionSearchFilterSchema(value=metric_value, type=schemas.FilterType.issue, - operator=schemas.SearchEventOperator._is)) +def search_table_of_individual_issues(data: schemas.SessionsSearchPayloadSchema, project_id: int): full_args, query_part = search_query_parts(data=data, error_status=None, errors_only=False, favorite_only=False, issue=None, project_id=project_id, user_id=None) diff --git a/api/requirements-alerts.txt b/api/requirements-alerts.txt index f1a0b5547..ce2617758 100644 --- a/api/requirements-alerts.txt +++ b/api/requirements-alerts.txt @@ -1,7 +1,7 @@ # Keep this version to not have conflicts between requests and boto3 urllib3==1.26.16 requests==2.31.0 -boto3==1.28.63 +boto3==1.28.64 pyjwt==2.8.0 psycopg2-binary==2.9.9 elasticsearch==8.10.1 diff --git a/api/requirements.txt b/api/requirements.txt index 0c162fa42..d16a0eb86 100644 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,7 +1,7 @@ # Keep this version to not have conflicts between requests and boto3 urllib3==1.26.16 requests==2.31.0 -boto3==1.28.63 +boto3==1.28.64 pyjwt==2.8.0 psycopg2-binary==2.9.9 elasticsearch==8.10.1 diff --git a/api/schemas/schemas.py b/api/schemas/schemas.py index d48a03aa8..eb709da6e 100644 --- a/api/schemas/schemas.py +++ b/api/schemas/schemas.py @@ -790,7 +790,8 @@ class PathAnalysisSubFilterSchema(BaseModel): _remove_duplicate_values = field_validator('value', mode='before')(remove_duplicate_values) -class ProductAnalyticsFilter(BaseModel): +class _ProductAnalyticsFilter(BaseModel): + is_event: Literal[False] = False type: FilterType operator: Union[SearchEventOperator, ClickEventExtraOperator, MathOperator] = Field(...) # TODO: support session metadata filters @@ -799,6 +800,21 @@ class ProductAnalyticsFilter(BaseModel): _remove_duplicate_values = field_validator('value', mode='before')(remove_duplicate_values) +class _ProductAnalyticsEventFilter(BaseModel): + is_event: Literal[True] = True + type: ProductAnalyticsSelectedEventType + operator: Union[SearchEventOperator, ClickEventExtraOperator, MathOperator] = Field(...) + # TODO: support session metadata filters + value: List[Union[IssueType, PlatformType, int, str]] = Field(...) + + _remove_duplicate_values = field_validator('value', mode='before')(remove_duplicate_values) + + +# this type is created to allow mixing events&filters and specifying a discriminator for PathAnalysis series filter +ProductAnalyticsFilter = Annotated[Union[_ProductAnalyticsFilter, _ProductAnalyticsEventFilter], \ + Field(discriminator='is_event')] + + class PathAnalysisSchema(_TimedSchema, _PaginatedSchema): density: int = Field(default=7) filters: List[ProductAnalyticsFilter] = Field(default=[]) @@ -1164,7 +1180,7 @@ class CardInsights(__CardSchema): raise ValueError(f"metricType:{MetricType.insights} not supported yet.") -class CardPathAnalysisSchema(CardSessionsSchema): +class CardPathAnalysisSeriesSchema(CardSeriesSchema): name: Optional[str] = Field(default=None) filter: PathAnalysisSchema = Field(...) density: int = Field(default=4, ge=2, le=10) @@ -1182,14 +1198,14 @@ class CardPathAnalysis(__CardSchema): metric_type: Literal[MetricType.pathAnalysis] metric_of: MetricOfPathAnalysis = Field(default=MetricOfPathAnalysis.session_count) view_type: MetricOtherViewType = Field(...) - metric_value: List[ProductAnalyticsSelectedEventType] = Field(default=[ProductAnalyticsSelectedEventType.location]) + metric_value: List[ProductAnalyticsSelectedEventType] = Field(default=[]) density: int = Field(default=4, ge=2, le=10) start_type: Literal["start", "end"] = Field(default="start") start_point: List[PathAnalysisSubFilterSchema] = Field(default=[]) excludes: List[PathAnalysisSubFilterSchema] = Field(default=[]) - series: List[CardPathAnalysisSchema] = Field(default=[]) + series: List[CardPathAnalysisSeriesSchema] = Field(default=[]) @model_validator(mode="before") def __enforce_default(cls, values): @@ -1201,17 +1217,14 @@ class CardPathAnalysis(__CardSchema): @model_validator(mode="after") def __clean_start_point_and_enforce_metric_value(cls, values): start_point = [] - metric_value = [] for s in values.start_point: if len(s.value) == 0: continue start_point.append(s) - metric_value.append(s.type) + values.metric_value.append(s.type) values.start_point = start_point - if len(metric_value) > 0: - metric_value = remove_duplicate_values(metric_value) - values.metric_value = metric_value + values.metric_value = remove_duplicate_values(values.metric_value) return values diff --git a/ee/api/Pipfile b/ee/api/Pipfile index ace2a3813..85db4af71 100644 --- a/ee/api/Pipfile +++ b/ee/api/Pipfile @@ -6,10 +6,10 @@ name = "pypi" [packages] urllib3 = "==1.26.16" requests = "==2.31.0" -boto3 = "==1.28.62" +boto3 = "==1.28.64" pyjwt = "==2.8.0" psycopg2-binary = "==2.9.9" -elasticsearch = "==8.10.0" +elasticsearch = "==8.10.1" jira = "==3.5.2" fastapi = "==0.103.2" gunicorn = "==21.2.0" @@ -18,7 +18,7 @@ apscheduler = "==3.10.4" python3-saml = "==1.15.0" python-multipart = "==0.0.6" redis = "==5.0.1" -azure-storage-blob = "==12.18.2" +azure-storage-blob = "==12.18.3" uvicorn = {extras = ["standard"], version = "==0.23.2"} pydantic = {extras = ["email"], version = "==2.3.0"} clickhouse-driver = {extras = ["lz4"], version = "==0.2.6"} diff --git a/ee/api/chalicelib/core/custom_metrics.py b/ee/api/chalicelib/core/custom_metrics.py index 69f983168..0163771d2 100644 --- a/ee/api/chalicelib/core/custom_metrics.py +++ b/ee/api/chalicelib/core/custom_metrics.py @@ -122,7 +122,7 @@ def __get_insights_chart(project_id: int, data: schemas.CardInsights, user_id: i def __get_path_analysis_chart(project_id: int, user_id: int, data: schemas.CardPathAnalysis): if len(data.series) == 0: data.series.append( - schemas.CardPathAnalysisSchema(startTimestamp=data.startTimestamp, endTimestamp=data.endTimestamp)) + schemas.CardPathAnalysisSeriesSchema(startTimestamp=data.startTimestamp, endTimestamp=data.endTimestamp)) elif not isinstance(data.series[0].filter, schemas.PathAnalysisSchema): data.series[0].filter = schemas.PathAnalysisSchema() @@ -315,28 +315,29 @@ def __get_funnel_issues(project_id: int, user_id: int, data: schemas.CardFunnel) def __get_path_analysis_issues(project_id: int, user_id: int, data: schemas.CardPathAnalysis): if len(data.series) == 0: return {"data": {}} - card_table = schemas.CardTable( + search_data = schemas.SessionsSearchPayloadSchema( startTimestamp=data.startTimestamp, endTimestamp=data.endTimestamp, - metricType=schemas.MetricType.table, - metricOf=schemas.MetricOfTable.issues, - viewType=schemas.MetricTableViewType.table, - series=data.model_dump()["series"]) + limit=data.limit, + page=data.page, + filters=data.series[0].filter.model_dump(by_alias=True)["filters"] + ) + for s in data.start_point: if data.start_type == "end": - card_table.series[0].filter.filters.append(schemas.SessionSearchEventSchema2(type=s.type, - operator=s.operator, - value=s.value)) + search_data.filters.append(schemas.SessionSearchEventSchema2(type=s.type, + operator=s.operator, + value=s.value)) else: - card_table.series[0].filter.filters.insert(0, schemas.SessionSearchEventSchema2(type=s.type, - operator=s.operator, - value=s.value)) + search_data.filters.insert(0, schemas.SessionSearchEventSchema2(type=s.type, + operator=s.operator, + value=s.value)) for s in data.excludes: - card_table.series[0].filter.filters.append(schemas.SessionSearchEventSchema2(type=s.type, - operator=schemas.SearchEventOperator._not_on, - value=s.value)) - result = __get_table_of_issues(project_id=project_id, user_id=user_id, data=card_table) - return result[0] if len(result) > 0 else {} + search_data.filters.append(schemas.SessionSearchEventSchema2(type=s.type, + operator=schemas.SearchEventOperator._not_on, + value=s.value)) + result = sessions.search_table_of_individual_issues(project_id=project_id, data=search_data) + return result def get_issues(project_id: int, user_id: int, data: schemas.CardSchema): diff --git a/ee/api/requirements-alerts.txt b/ee/api/requirements-alerts.txt index 92e88aa66..0d5cf1468 100644 --- a/ee/api/requirements-alerts.txt +++ b/ee/api/requirements-alerts.txt @@ -1,7 +1,7 @@ # Keep this version to not have conflicts between requests and boto3 urllib3==1.26.16 requests==2.31.0 -boto3==1.28.63 +boto3==1.28.64 pyjwt==2.8.0 psycopg2-binary==2.9.9 elasticsearch==8.10.1 @@ -17,4 +17,4 @@ apscheduler==3.10.4 clickhouse-driver[lz4]==0.2.6 python-multipart==0.0.6 -azure-storage-blob==12.18.2 \ No newline at end of file +azure-storage-blob==12.18.3 \ No newline at end of file diff --git a/ee/api/requirements-crons.txt b/ee/api/requirements-crons.txt index 9e2591178..b142a37c5 100644 --- a/ee/api/requirements-crons.txt +++ b/ee/api/requirements-crons.txt @@ -1,7 +1,7 @@ # Keep this version to not have conflicts between requests and boto3 urllib3==1.26.16 requests==2.31.0 -boto3==1.28.63 +boto3==1.28.64 pyjwt==2.8.0 psycopg2-binary==2.9.9 elasticsearch==8.10.1 @@ -16,4 +16,4 @@ apscheduler==3.10.4 clickhouse-driver[lz4]==0.2.6 redis==5.0.1 -azure-storage-blob==12.18.2 +azure-storage-blob==12.18.3 diff --git a/ee/api/requirements.txt b/ee/api/requirements.txt index 594e989a0..45027c708 100644 --- a/ee/api/requirements.txt +++ b/ee/api/requirements.txt @@ -1,7 +1,7 @@ # Keep this version to not have conflicts between requests and boto3 urllib3==1.26.16 requests==2.31.0 -boto3==1.28.63 +boto3==1.28.64 pyjwt==2.8.0 psycopg2-binary==2.9.9 elasticsearch==8.10.1 @@ -25,4 +25,4 @@ python-multipart==0.0.6 redis==5.0.1 #confluent-kafka==2.1.0 -azure-storage-blob==12.18.2 +azure-storage-blob==12.18.3