diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index d00a3a7a1..eeecf4fc1 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -70,13 +70,13 @@ def __get_sessions_list(project_id, user_id, data: schemas.CardSchema): return sessions.search_sessions(data=data.series[0].filter, project_id=project_id, user_id=user_id) -def __get_click_map_chart(project_id, user_id, data: schemas.CardClickMap, include_mobs: bool = True): +def __get_heat_map_chart(project_id, user_id, data: schemas.CardHeatMap, include_mobs: bool = True): if len(data.series) == 0: return None 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=schemas.HeatMapSessionsSearch( **data.series[0].filter.model_dump()), include_mobs=include_mobs) @@ -160,8 +160,7 @@ def get_chart(project_id: int, data: schemas.CardSchema, user_id: int): supported = { schemas.MetricType.timeseries: __get_timeseries_chart, schemas.MetricType.table: __get_table_chart, - schemas.MetricType.click_map: __get_click_map_chart, - schemas.MetricType.heat_map: __get_click_map_chart, + schemas.MetricType.heat_map: __get_heat_map_chart, schemas.MetricType.funnel: __get_funnel_chart, schemas.MetricType.insights: not_supported, schemas.MetricType.pathAnalysis: __get_path_analysis_chart @@ -290,7 +289,7 @@ def get_issues(project_id: int, user_id: int, data: schemas.CardSchema): supported = { schemas.MetricType.timeseries: not_supported, schemas.MetricType.table: not_supported, - schemas.MetricType.click_map: not_supported, + schemas.MetricType.heat_map: not_supported, schemas.MetricType.funnel: __get_funnel_issues, schemas.MetricType.insights: not_supported, schemas.MetricType.pathAnalysis: __get_path_analysis_issues, @@ -309,12 +308,12 @@ def __get_path_analysis_card_info(data: schemas.CardPathAnalysis): def create_card(project_id, user_id, data: schemas.CardSchema, dashboard=False): with pg_client.PostgresClient() as cur: session_data = None - if data.metric_type in (schemas.MetricType.click_map, schemas.MetricType.heat_map): + if data.metric_type == schemas.MetricType.heat_map: if data.session_id is not None: session_data = json.dumps({"sessionId": data.session_id}) else: - session_data = __get_click_map_chart(project_id=project_id, user_id=user_id, - data=data, include_mobs=False) + session_data = __get_heat_map_chart(project_id=project_id, user_id=user_id, + data=data, include_mobs=False) if session_data is not None: session_data = json.dumps({"sessionId": session_data["sessionId"]}) _data = {"session_data": session_data} @@ -392,7 +391,7 @@ def update_card(metric_id, user_id, project_id, data: schemas.CardSchema): params["session_data"] = json.dumps(metric["data"]) if data.metric_type == schemas.MetricType.pathAnalysis: params["card_info"] = json.dumps(__get_path_analysis_card_info(data=data)) - elif data.metric_type in (schemas.MetricType.click_map, schemas.MetricType.heat_map): + elif data.metric_type == schemas.MetricType.heat_map: if data.session_id is not None: params["session_data"] = json.dumps({"sessionId": data.session_id}) elif "data" in metric: @@ -682,11 +681,11 @@ def make_chart_from_card(project_id, user_id, metric_id, data: schemas.CardSessi return custom_metrics_predefined.get_metric(key=metric.metric_of, project_id=project_id, data=data.model_dump()) - elif metric.metric_type in (schemas.MetricType.click_map, schemas.MetricType.heat_map): + elif metric.metric_type == schemas.MetricType.heat_map: if raw_metric["data"] and raw_metric["data"].get("sessionId"): return heatmaps.get_selected_session(project_id=project_id, session_id=raw_metric["data"]["sessionId"]) else: - return heatmaps.search_short_session(project_id=project_id, data=metric) + return heatmaps.search_short_session(project_id=project_id, data=metric, user_id=user_id) return get_chart(project_id=project_id, data=metric, user_id=user_id) diff --git a/api/chalicelib/core/heatmaps.py b/api/chalicelib/core/heatmaps.py index b9be7ea24..0b1011ce8 100644 --- a/api/chalicelib/core/heatmaps.py +++ b/api/chalicelib/core/heatmaps.py @@ -80,7 +80,7 @@ def get_by_url(project_id, data: schemas.GetHeatmapPayloadSchema): return helper.list_to_camel_case(rows) -def get_x_y_by_url_and_session_id(project_id, session_id, data: schemas.GetHeatmapBasePayloadSchema): +def get_x_y_by_url_and_session_id(project_id, session_id, data: schemas.GetHeatMapPayloadSchema): args = {"session_id": session_id, "url": data.url} constraints = ["session_id = %(session_id)s", "(url = %(url)s OR path= %(url)s)", @@ -108,7 +108,7 @@ def get_x_y_by_url_and_session_id(project_id, session_id, data: schemas.GetHeatm return helper.list_to_camel_case(rows) -def get_selectors_by_url_and_session_id(project_id, session_id, data: schemas.GetHeatmapBasePayloadSchema): +def get_selectors_by_url_and_session_id(project_id, session_id, data: schemas.GetHeatMapPayloadSchema): args = {"session_id": session_id, "url": data.url} constraints = ["session_id = %(session_id)s", "(url = %(url)s OR path= %(url)s)"] @@ -143,7 +143,7 @@ s.start_ts, s.duration""" -def search_short_session(data: schemas.ClickMapSessionsSearch, project_id, user_id, +def search_short_session(data: schemas.HeatMapSessionsSearch, project_id, user_id, include_mobs: bool = True, exclude_sessions: list[str] = [], _depth: int = 3): no_platform = True diff --git a/api/routers/core_dynamic.py b/api/routers/core_dynamic.py index 577a982b7..3e3a9a62d 100644 --- a/api/routers/core_dynamic.py +++ b/api/routers/core_dynamic.py @@ -419,15 +419,15 @@ def get_heatmaps_by_url(projectId: int, data: schemas.GetHeatmapPayloadSchema = @app.post('/{projectId}/sessions/{sessionId}/heatmaps', tags=["heatmaps"]) def get_heatmaps_by_session_id_url(projectId: int, sessionId: int, - data: schemas.GetHeatmapBasePayloadSchema = Body(...), + data: schemas.GetHeatMapPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): return {"data": heatmaps.get_x_y_by_url_and_session_id(project_id=projectId, session_id=sessionId, data=data)} @app.post('/{projectId}/sessions/{sessionId}/clickmaps', tags=["heatmaps"]) def get_clickmaps_by_session_id_url(projectId: int, sessionId: int, - data: schemas.GetHeatmapBasePayloadSchema = Body(...), - context: schemas.CurrentContext = Depends(OR_context)): + data: schemas.GetClickMapPayloadSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): return {"data": heatmaps.get_selectors_by_url_and_session_id(project_id=projectId, session_id=sessionId, data=data)} diff --git a/api/schemas/schemas.py b/api/schemas/schemas.py index 92db8a6a2..295c7c9c2 100644 --- a/api/schemas/schemas.py +++ b/api/schemas/schemas.py @@ -936,8 +936,6 @@ class MetricType(str, Enum): pathAnalysis = "pathAnalysis" retention = "retention" stickiness = "stickiness" - click_map = "clickMap" - # click_map and heat_map are the same heat_map = "heatMap" insights = "insights" @@ -1024,8 +1022,8 @@ class MetricOfFunnels(str, Enum): user_count = MetricOfTimeseries.user_count.value -class MetricOfClickMap(str, Enum): - click_map_url = "clickMapUrl" +class MetricOfHeatMap(str, Enum): + heat_map_url = "heatMapUrl" class MetricOfPathAnalysis(str, Enum): @@ -1135,17 +1133,6 @@ class __CardSchema(CardSessionsSchema): return self.metric_type in [MetricType.errors, MetricType.performance, MetricType.resources, MetricType.web_vital] - # TODO: finish the reset of these conditions - # @model_validator(mode='after') - # def __validator(cls, values): - # if values.metric_type == MetricType.click_map: - # # assert isinstance(values.metric_of, MetricOfClickMap), \ - # # f"metricOf must be of type {MetricOfClickMap} for metricType:{MetricType.click_map}" - # for s in values.series: - # for f in s.filter.events: - # assert f.type == EventType.location, f"only events of type:{EventType.location} are allowed for metricOf:{MetricType.click_map}" - # return values - class CardTimeSeries(__CardSchema): metric_type: Literal[MetricType.timeseries] @@ -1274,9 +1261,9 @@ class CardWebVital(__CardSchema): return values -class CardClickMap(__CardSchema): - metric_type: Literal[MetricType.click_map] - metric_of: MetricOfClickMap = Field(default=MetricOfClickMap.click_map_url) +class CardHeatMap(__CardSchema): + metric_type: Literal[MetricType.heat_map] + metric_of: MetricOfHeatMap = Field(default=MetricOfHeatMap.heat_map_url) view_type: MetricOtherViewType = Field(...) @model_validator(mode="before") @@ -1285,7 +1272,7 @@ class CardClickMap(__CardSchema): @model_validator(mode="after") def __transform(cls, values): - values.metric_of = MetricOfClickMap(values.metric_of) + values.metric_of = MetricOfHeatMap(values.metric_of) return values @@ -1384,7 +1371,7 @@ class CardPathAnalysis(__CardSchema): __cards_union_base = Union[ CardTimeSeries, CardTable, CardFunnel, CardErrors, CardPerformance, CardResources, - CardWebVital, CardClickMap, + CardWebVital, CardHeatMap, CardPathAnalysis] CardSchema = ORUnion(Union[__cards_union_base, CardInsights], discriminator='metric_type') @@ -1559,13 +1546,13 @@ class SearchCardsSchema(_PaginatedSchema): query: Optional[str] = Field(default=None) -class _ClickMapSearchEventRaw(SessionSearchEventSchema2): +class _HeatMapSearchEventRaw(SessionSearchEventSchema2): type: Literal[EventType.location] = Field(...) -class ClickMapSessionsSearch(SessionsSearchPayloadSchema): - events: Optional[List[_ClickMapSearchEventRaw]] = Field(default=[]) - filters: List[Union[SessionSearchFilterSchema, _ClickMapSearchEventRaw]] = Field(default=[]) +class HeatMapSessionsSearch(SessionsSearchPayloadSchema): + events: Optional[List[_HeatMapSearchEventRaw]] = Field(default=[]) + filters: List[Union[SessionSearchFilterSchema, _HeatMapSearchEventRaw]] = Field(default=[]) @model_validator(mode="before") def __transform(cls, values): @@ -1578,7 +1565,7 @@ class ClickMapSessionsSearch(SessionsSearchPayloadSchema): return values -class ClickMapFilterSchema(BaseModel): +class HeatMapFilterSchema(BaseModel): value: List[Literal[IssueType.click_rage, IssueType.dead_click]] = Field(default=[]) type: Literal[FilterType.issue] = Field(...) operator: Literal[SearchEventOperator._is, MathOperator._equal] = Field(...) @@ -1586,14 +1573,18 @@ class ClickMapFilterSchema(BaseModel): class GetHeatmapPayloadSchema(_TimedSchema): url: str = Field(...) - filters: List[ClickMapFilterSchema] = Field(default=[]) + filters: List[HeatMapFilterSchema] = Field(default=[]) click_rage: bool = Field(default=False) -class GetHeatmapBasePayloadSchema(BaseModel): +class GetHeatMapPayloadSchema(BaseModel): url: str = Field(...) +class GetClickMapPayloadSchema(GetHeatMapPayloadSchema): + pass + + class FeatureFlagVariant(BaseModel): variant_id: Optional[int] = Field(default=None) value: str = Field(...) diff --git a/ee/api/chalicelib/core/custom_metrics.py b/ee/api/chalicelib/core/custom_metrics.py index c01284ce6..5c241a80c 100644 --- a/ee/api/chalicelib/core/custom_metrics.py +++ b/ee/api/chalicelib/core/custom_metrics.py @@ -81,13 +81,13 @@ def __get_sessions_list(project_id, user_id, data: schemas.CardSchema): return sessions.search_sessions(data=data.series[0].filter, project_id=project_id, user_id=user_id) -def __get_click_map_chart(project_id, user_id, data: schemas.CardClickMap, include_mobs: bool = True): +def __get_heat_map_chart(project_id, user_id, data: schemas.CardHeatMap, include_mobs: bool = True): if len(data.series) == 0: return None 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=schemas.HeatMapSessionsSearch( **data.series[0].filter.model_dump()), include_mobs=include_mobs) @@ -180,8 +180,7 @@ def get_chart(project_id: int, data: schemas.CardSchema, user_id: int): supported = { schemas.MetricType.timeseries: __get_timeseries_chart, schemas.MetricType.table: __get_table_chart, - schemas.MetricType.click_map: __get_click_map_chart, - schemas.MetricType.heat_map: __get_click_map_chart, + schemas.MetricType.heat_map: __get_heat_map_chart, schemas.MetricType.funnel: __get_funnel_chart, schemas.MetricType.insights: __get_insights_chart, schemas.MetricType.pathAnalysis: __get_path_analysis_chart @@ -310,7 +309,7 @@ def get_issues(project_id: int, user_id: int, data: schemas.CardSchema): supported = { schemas.MetricType.timeseries: not_supported, schemas.MetricType.table: not_supported, - schemas.MetricType.click_map: not_supported, + schemas.MetricType.heat_map: not_supported, schemas.MetricType.funnel: __get_funnel_issues, schemas.MetricType.insights: not_supported, schemas.MetricType.pathAnalysis: __get_path_analysis_issues, @@ -329,12 +328,12 @@ def __get_path_analysis_card_info(data: schemas.CardPathAnalysis): def create_card(project_id, user_id, data: schemas.CardSchema, dashboard=False): with pg_client.PostgresClient() as cur: session_data = None - if data.metric_type in (schemas.MetricType.click_map, schemas.MetricType.heat_map): + if data.metric_type == schemas.MetricType.heat_map: if data.session_id is not None: session_data = json.dumps({"sessionId": data.session_id}) else: - session_data = __get_click_map_chart(project_id=project_id, user_id=user_id, - data=data, include_mobs=False) + session_data = __get_heat_map_chart(project_id=project_id, user_id=user_id, + data=data, include_mobs=False) if session_data is not None: # for EE only keys = sessions_mobs. \ @@ -346,7 +345,7 @@ def create_card(project_id, user_id, data: schemas.CardSchema, dashboard=False): try: extra.tag_session(file_key=k, tag_value=tag) except Exception as e: - logger.warning(f"!!!Error while tagging: {k} to {tag} for clickMap") + logger.warning(f"!!!Error while tagging: {k} to {tag} for heatMap") logger.error(str(e)) session_data = json.dumps(session_data) _data = {"session_data": session_data} @@ -424,7 +423,7 @@ def update_card(metric_id, user_id, project_id, data: schemas.CardSchema): params["session_data"] = json.dumps(metric["data"]) if data.metric_type == schemas.MetricType.pathAnalysis: params["card_info"] = json.dumps(__get_path_analysis_card_info(data=data)) - elif data.metric_type in (schemas.MetricType.click_map, schemas.MetricType.heat_map): + elif data.metric_type == schemas.MetricType.heat_map: if data.session_id is not None: params["session_data"] = json.dumps({"sessionId": data.session_id}) elif "data" in metric: @@ -573,7 +572,7 @@ def delete_card(project_id, metric_id, user_id): try: extra.tag_session(file_key=k, tag_value=tag) except Exception as e: - logger.warning(f"!!!Error while tagging: {k} to {tag} for clickMap") + logger.warning(f"!!!Error while tagging: {k} to {tag} for heatMap") logger.error(str(e)) return {"state": "success"} @@ -729,11 +728,11 @@ def make_chart_from_card(project_id, user_id, metric_id, data: schemas.CardSessi return custom_metrics_predefined.get_metric(key=metric.metric_of, project_id=project_id, data=data.model_dump()) - elif metric.metric_type in (schemas.MetricType.click_map, schemas.MetricType.heat_map): + elif metric.metric_type == schemas.MetricType.heat_map: if raw_metric["data"] and raw_metric["data"].get("sessionId"): return heatmaps.get_selected_session(project_id=project_id, session_id=raw_metric["data"]["sessionId"]) else: - return heatmaps.search_short_session(project_id=project_id, data=metric) + return heatmaps.search_short_session(project_id=project_id, data=metric, user_id=user_id) return get_chart(project_id=project_id, data=metric, user_id=user_id) diff --git a/ee/api/chalicelib/core/heatmaps.py b/ee/api/chalicelib/core/heatmaps.py index 5d5daefc6..68ced64f1 100644 --- a/ee/api/chalicelib/core/heatmaps.py +++ b/ee/api/chalicelib/core/heatmaps.py @@ -159,7 +159,7 @@ if not config("EXP_SESSIONS_SEARCH", cast=bool, default=False): s.duration""" - def search_short_session(data: schemas.ClickMapSessionsSearch, project_id, user_id, + def search_short_session(data: schemas.HeatMapSessionsSearch, project_id, user_id, include_mobs: bool = True, exclude_sessions: list[str] = [], _depth: int = 3): no_platform = True @@ -290,7 +290,7 @@ else: s.duration AS duration""" - def search_short_session(data: schemas.ClickMapSessionsSearch, project_id, user_id, + def search_short_session(data: schemas.HeatMapSessionsSearch, project_id, user_id, include_mobs: bool = True, exclude_sessions: list[str] = [], _depth: int = 3): no_platform = True diff --git a/ee/api/routers/core_dynamic.py b/ee/api/routers/core_dynamic.py index cb6f353d1..b7d196ed9 100644 --- a/ee/api/routers/core_dynamic.py +++ b/ee/api/routers/core_dynamic.py @@ -449,7 +449,7 @@ def get_heatmaps_by_url(projectId: int, data: schemas.GetHeatmapPayloadSchema = @app.post('/{projectId}/sessions/{sessionId}/heatmaps', tags=["heatmaps"], dependencies=[OR_scope(Permissions.session_replay)]) def get_heatmaps_by_session_id_url(projectId: int, sessionId: int, - data: schemas.GetHeatmapBasePayloadSchema = Body(...), + data: schemas.GetHeatmapPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): return {"data": heatmaps.get_x_y_by_url_and_session_id(project_id=projectId, session_id=sessionId, data=data)} @@ -457,7 +457,7 @@ def get_heatmaps_by_session_id_url(projectId: int, sessionId: int, @app.post('/{projectId}/sessions/{sessionId}/clickmaps', tags=["heatmaps"], dependencies=[OR_scope(Permissions.session_replay)]) def get_clickmaps_by_session_id_url(projectId: int, sessionId: int, - data: schemas.GetHeatmapBasePayloadSchema = Body(...), + data: schemas.GetClickMapPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): return {"data": heatmaps.get_selectors_by_url_and_session_id(project_id=projectId, session_id=sessionId, data=data)} diff --git a/ee/scripts/schema/db/init_dbs/postgresql/1.19.0/1.19.0.sql b/ee/scripts/schema/db/init_dbs/postgresql/1.19.0/1.19.0.sql index 39d338407..2c2e3ff12 100644 --- a/ee/scripts/schema/db/init_dbs/postgresql/1.19.0/1.19.0.sql +++ b/ee/scripts/schema/db/init_dbs/postgresql/1.19.0/1.19.0.sql @@ -44,6 +44,11 @@ UPDATE public.metrics SET view_type='lineChart' WHERE view_type = 'progress'; +UPDATE public.metrics +SET metric_type='heatMap', + metric_of='heatMapUrl' +WHERE metric_type = 'clickMap'; + COMMIT; \elif :is_next diff --git a/ee/scripts/schema/db/rollback_dbs/postgresql/1.19.0/1.19.0.sql b/ee/scripts/schema/db/rollback_dbs/postgresql/1.19.0/1.19.0.sql index 21815318a..9d2c71e1a 100644 --- a/ee/scripts/schema/db/rollback_dbs/postgresql/1.19.0/1.19.0.sql +++ b/ee/scripts/schema/db/rollback_dbs/postgresql/1.19.0/1.19.0.sql @@ -23,6 +23,11 @@ ALTER TABLE IF EXISTS events.clicks ADD COLUMN IF NOT EXISTS x integer DEFAULT NULL, ADD COLUMN IF NOT EXISTS y integer DEFAULT NULL; +UPDATE public.metrics +SET metric_type='clickMap', + metric_of='clickMapUrl' +WHERE metric_type = 'heatMap'; + COMMIT; \elif :is_next diff --git a/scripts/schema/db/init_dbs/postgresql/1.19.0/1.19.0.sql b/scripts/schema/db/init_dbs/postgresql/1.19.0/1.19.0.sql index 2fee41c65..9cd060a95 100644 --- a/scripts/schema/db/init_dbs/postgresql/1.19.0/1.19.0.sql +++ b/scripts/schema/db/init_dbs/postgresql/1.19.0/1.19.0.sql @@ -44,6 +44,11 @@ UPDATE public.metrics SET view_type='lineChart' WHERE view_type = 'progress'; +UPDATE public.metrics +SET metric_type='heatMap', + metric_of='heatMapUrl' +WHERE metric_type = 'clickMap'; + COMMIT; \elif :is_next diff --git a/scripts/schema/db/rollback_dbs/postgresql/1.19.0/1.19.0.sql b/scripts/schema/db/rollback_dbs/postgresql/1.19.0/1.19.0.sql index 6c9f065b5..9c77d81c9 100644 --- a/scripts/schema/db/rollback_dbs/postgresql/1.19.0/1.19.0.sql +++ b/scripts/schema/db/rollback_dbs/postgresql/1.19.0/1.19.0.sql @@ -23,6 +23,11 @@ ALTER TABLE IF EXISTS events.clicks ADD COLUMN IF NOT EXISTS x integer DEFAULT NULL, ADD COLUMN IF NOT EXISTS y integer DEFAULT NULL; +UPDATE public.metrics +SET metric_type='clickMap', + metric_of='clickMapUrl' +WHERE metric_type = 'heatMap'; + COMMIT; \elif :is_next