diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 6b9caf214..823601409 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -52,7 +52,8 @@ def __get_table_of_series(project_id, data: schemas.CardSchema): results = [] for i, s in enumerate(data.series): results.append(sessions.search2_table(data=s.filter, project_id=project_id, density=data.density, - metric_of=data.metric_of, metric_value=data.metric_value)) + metric_of=data.metric_of, metric_value=data.metric_value, + metric_format=data.metric_format)) return results @@ -91,9 +92,9 @@ def __get_click_map_chart(project_id, user_id, data: schemas.CardClickMap, inclu data.series[0].filter.filters += data.series[0].filter.events data.series[0].filter.events = [] return heatmaps.search_short_session(project_id=project_id, user_id=user_id, - data=schemas.ClickMapSessionsSearch( - **data.series[0].filter.model_dump()), - include_mobs=include_mobs) + data=schemas.ClickMapSessionsSearch( + **data.series[0].filter.model_dump()), + include_mobs=include_mobs) def __get_path_analysis_chart(project_id: int, user_id: int, data: schemas.CardPathAnalysis): diff --git a/api/chalicelib/core/sessions.py b/api/chalicelib/core/sessions.py index 07a8ebfff..9fc8330ad 100644 --- a/api/chalicelib/core/sessions.py +++ b/api/chalicelib/core/sessions.py @@ -1,5 +1,5 @@ import logging -from typing import List +from typing import List, Union import schemas from chalicelib.core import events, metadata, projects, performance_event, sessions_favorite @@ -296,7 +296,8 @@ def search2_series(data: schemas.SessionsSearchPayloadSchema, project_id: int, d def search2_table(data: schemas.SessionsSearchPayloadSchema, project_id: int, density: int, - metric_of: schemas.MetricOfTable, metric_value: List): + metric_of: schemas.MetricOfTable, metric_value: List, + metric_format: Union[schemas.MetricExtendedFormatType, schemas.MetricExtendedFormatType]): step_size = int(metrics_helper.__get_step_size(endTimestamp=data.endTimestamp, startTimestamp=data.startTimestamp, density=density, factor=1, decimal=True)) extra_event = None @@ -331,7 +332,6 @@ def search2_table(data: schemas.SessionsSearchPayloadSchema, project_id: int, de main_col = "user_id" extra_col = "" extra_where = "" - pre_query = "" distinct_on = "s.session_id" if metric_of == schemas.MetricOfTable.user_country: main_col = "user_country" @@ -353,25 +353,46 @@ def search2_table(data: schemas.SessionsSearchPayloadSchema, project_id: int, de main_col = "path" extra_col = ", path" distinct_on += ",path" - main_query = cur.mogrify(f"""{pre_query} - SELECT COUNT(*) AS count, - COALESCE(SUM(users_sessions.session_count),0) AS total_sessions, - COALESCE(JSONB_AGG(users_sessions) FILTER ( WHERE rn <= 200 ), '[]'::JSONB) AS values - FROM (SELECT {main_col} AS name, - count(DISTINCT session_id) AS session_count, - ROW_NUMBER() OVER (ORDER BY count(full_sessions) DESC) AS rn - FROM (SELECT * - FROM (SELECT DISTINCT ON({distinct_on}) s.session_id, s.user_uuid, - s.user_id, s.user_os, - s.user_browser, s.user_device, - s.user_device_type, s.user_country, s.issue_types{extra_col} - {query_part} - ORDER BY s.session_id desc) AS filtred_sessions - ) AS full_sessions - {extra_where} - GROUP BY {main_col} - ORDER BY session_count DESC) AS users_sessions;""", - full_args) + if metric_format == schemas.MetricExtendedFormatType.session_count: + main_query = f"""SELECT COUNT(*) AS count, + COALESCE(SUM(users_sessions.session_count),0) AS total_sessions, + COALESCE(JSONB_AGG(users_sessions) FILTER ( WHERE rn <= 200 ), '[]'::JSONB) AS values + FROM (SELECT {main_col} AS name, + count(DISTINCT session_id) AS session_count, + ROW_NUMBER() OVER (ORDER BY count(full_sessions) DESC) AS rn + FROM (SELECT * + FROM (SELECT DISTINCT ON({distinct_on}) s.session_id, s.user_uuid, + s.user_id, s.user_os, + s.user_browser, s.user_device, + s.user_device_type, s.user_country, s.issue_types{extra_col} + {query_part} + ORDER BY s.session_id desc) AS filtred_sessions + ) AS full_sessions + {extra_where} + GROUP BY {main_col} + ORDER BY session_count DESC) AS users_sessions;""" + else: + main_query = f"""SELECT COUNT(*) AS count, + COALESCE(SUM(users_sessions.user_count),0) AS total_users, + COALESCE(JSONB_AGG(users_sessions) FILTER ( WHERE rn <= 200 ), '[]'::JSONB) AS values + FROM (SELECT {main_col} AS name, + count(DISTINCT user_id) AS user_count, + ROW_NUMBER() OVER (ORDER BY count(full_sessions) DESC) AS rn + FROM (SELECT * + FROM (SELECT DISTINCT ON({distinct_on}) s.session_id, s.user_uuid, + s.user_id, s.user_os, + s.user_browser, s.user_device, + s.user_device_type, s.user_country, s.issue_types{extra_col} + {query_part} + AND s.user_id IS NOT NULL + AND s.user_id !='' + ORDER BY s.session_id desc) AS filtred_sessions + ) AS full_sessions + {extra_where} + GROUP BY {main_col} + ORDER BY user_count DESC) AS users_sessions;""" + + main_query = cur.mogrify(main_query, full_args) logging.debug("--------------------") logging.debug(main_query) logging.debug("--------------------") diff --git a/api/schemas/schemas.py b/api/schemas/schemas.py index 33ee07a89..33a02c7e8 100644 --- a/api/schemas/schemas.py +++ b/api/schemas/schemas.py @@ -578,6 +578,11 @@ class MetricFormatType(str, Enum): session_count = 'sessionCount' +class MetricExtendedFormatType(str, Enum): + session_count = 'sessionCount' + user_count = 'userCount' + + class HttpMethod(str, Enum): _get = 'GET' _head = 'HEAD' @@ -1131,6 +1136,7 @@ class CardTable(__CardSchema): metric_type: Literal[MetricType.table] metric_of: MetricOfTable = Field(default=MetricOfTable.user_id) view_type: MetricTableViewType = Field(...) + metric_format: MetricExtendedFormatType = Field(default=MetricExtendedFormatType.session_count) @model_validator(mode="before") def __enforce_default(cls, values): @@ -1143,6 +1149,15 @@ class CardTable(__CardSchema): values.metric_of = MetricOfTable(values.metric_of) return values + @model_validator(mode="after") + def __validator(cls, values): + if values.metric_of not in (MetricOfTable.issues, MetricOfTable.user_browser, + MetricOfTable.user_device, MetricOfTable.user_country, + MetricOfTable.visited_url): + assert values.metric_format == MetricExtendedFormatType.session_count, \ + f'metricFormat:{MetricExtendedFormatType.user_count.value} is not supported for this metricOf' + return values + class CardFunnel(__CardSchema): metric_type: Literal[MetricType.funnel] diff --git a/ee/api/chalicelib/core/custom_metrics.py b/ee/api/chalicelib/core/custom_metrics.py index 9a546afd1..a1cf8c0a8 100644 --- a/ee/api/chalicelib/core/custom_metrics.py +++ b/ee/api/chalicelib/core/custom_metrics.py @@ -63,7 +63,8 @@ def __get_table_of_series(project_id, data: schemas.CardSchema): results = [] for i, s in enumerate(data.series): results.append(sessions.search2_table(data=s.filter, project_id=project_id, density=data.density, - metric_of=data.metric_of, metric_value=data.metric_value)) + metric_of=data.metric_of, metric_value=data.metric_value, + metric_format=data.metric_format)) return results diff --git a/ee/api/chalicelib/core/sessions_exp.py b/ee/api/chalicelib/core/sessions_exp.py index 24a4885ed..aeef72d23 100644 --- a/ee/api/chalicelib/core/sessions_exp.py +++ b/ee/api/chalicelib/core/sessions_exp.py @@ -366,7 +366,8 @@ def search2_series(data: schemas.SessionsSearchPayloadSchema, project_id: int, d def search2_table(data: schemas.SessionsSearchPayloadSchema, project_id: int, density: int, - metric_of: schemas.MetricOfTable, metric_value: List): + metric_of: schemas.MetricOfTable, metric_value: List, + metric_format: Union[schemas.MetricExtendedFormatType, schemas.MetricExtendedFormatType]): step_size = int(metrics_helper.__get_step_size(endTimestamp=data.endTimestamp, startTimestamp=data.startTimestamp, density=density)) extra_event = None @@ -413,7 +414,6 @@ def search2_table(data: schemas.SessionsSearchPayloadSchema, project_id: int, de main_col = "user_id" extra_col = "s.user_id" extra_where = "" - pre_query = "" if metric_of == schemas.MetricOfTable.user_country: main_col = "user_country" extra_col = "s.user_country" @@ -436,19 +436,33 @@ def search2_table(data: schemas.SessionsSearchPayloadSchema, project_id: int, de elif metric_of == schemas.MetricOfTable.visited_url: main_col = "url_path" extra_col = "s.url_path" - main_query = cur.format(f"""{pre_query} - SELECT COUNT(DISTINCT {main_col}) OVER () AS main_count, - {main_col} AS name, - count(DISTINCT session_id) AS session_count - FROM (SELECT s.session_id AS session_id, - {extra_col} - {query_part} - ORDER BY s.session_id desc) AS filtred_sessions - {extra_where} - GROUP BY {main_col} - ORDER BY session_count DESC - LIMIT %(limit_e)s OFFSET %(limit_s)s;""", - full_args) + + if metric_format == schemas.MetricExtendedFormatType.session_count: + main_query = f"""SELECT COUNT(DISTINCT {main_col}) OVER () AS main_count, + {main_col} AS name, + count(DISTINCT session_id) AS session_count + FROM (SELECT s.session_id AS session_id, + {extra_col} + {query_part}) AS filtred_sessions + {extra_where} + GROUP BY {main_col} + ORDER BY session_count DESC + LIMIT %(limit_e)s OFFSET %(limit_s)s;""" + else: + main_query = f"""SELECT COUNT(DISTINCT {main_col}) OVER () AS main_count, + {main_col} AS name, + count(DISTINCT user_id) AS user_count + FROM (SELECT s.user_id AS user_id, + {extra_col} + {query_part} + WHERE isNotNull(user_id) + AND user_id != '') AS filtred_sessions + {extra_where} + GROUP BY {main_col} + ORDER BY user_count DESC + LIMIT %(limit_e)s OFFSET %(limit_s)s;""" + + main_query = cur.format(main_query, full_args) logging.debug("--------------------") logging.debug(main_query) logging.debug("--------------------")