feat(chalice): insights as cards

This commit is contained in:
Taha Yassine Kraiem 2023-01-06 16:11:24 +01:00
parent be8a8409e0
commit cdc22f107e
5 changed files with 91 additions and 34 deletions

View file

@ -874,6 +874,7 @@ class MetricTableViewType(str, Enum):
class MetricOtherViewType(str, Enum): class MetricOtherViewType(str, Enum):
other_chart = "chart" other_chart = "chart"
list_chart = "list"
class MetricType(str, Enum): class MetricType(str, Enum):
@ -888,6 +889,7 @@ class MetricType(str, Enum):
retention = "retention" retention = "retention"
stickiness = "stickiness" stickiness = "stickiness"
click_map = "clickMap" click_map = "clickMap"
insights = "insights"
class MetricOfErrors(str, Enum): class MetricOfErrors(str, Enum):
@ -1017,6 +1019,11 @@ class CreateCardSchema(CardChartSchema):
return values return values
@root_validator
def restrictions(cls, values):
assert values.get("metric_type") != MetricType.insights, f"metricType:{MetricType.insights} not supported yet"
return values
@root_validator @root_validator
def validator(cls, values): def validator(cls, values):
if values.get("metric_type") == MetricType.timeseries: if values.get("metric_type") == MetricType.timeseries:
@ -1062,7 +1069,7 @@ class CreateCardSchema(CardChartSchema):
assert f.type == EventType.location, f"only events of type:{EventType.location} are allowed for metricOf:{MetricType.click_map}" assert f.type == EventType.location, f"only events of type:{EventType.location} are allowed for metricOf:{MetricType.click_map}"
assert isinstance(values.get("view_type"), MetricOtherViewType), \ assert isinstance(values.get("view_type"), MetricOtherViewType), \
f"viewType must be 'chart' for metricOf:{values.get('metric_of')}" f"viewType must be 'chart|list' for metricOf:{values.get('metric_of')}"
return values return values

View file

@ -6,7 +6,8 @@ from starlette import status
from decouple import config from decouple import config
import schemas import schemas
from chalicelib.core import funnels, issues, metrics, click_maps import schemas_ee
from chalicelib.core import funnels, issues, metrics, click_maps, sessions_insights
from chalicelib.utils import helper, pg_client from chalicelib.utils import helper, pg_client
from chalicelib.utils.TimeUTC import TimeUTC from chalicelib.utils.TimeUTC import TimeUTC
@ -24,7 +25,7 @@ else:
PIE_CHART_GROUP = 5 PIE_CHART_GROUP = 5
def __try_live(project_id, data: schemas.CreateCardSchema): def __try_live(project_id, data: schemas_ee.CreateCardSchema):
results = [] results = []
for i, s in enumerate(data.series): for i, s in enumerate(data.series):
s.filter.startDate = data.startTimestamp s.filter.startDate = data.startTimestamp
@ -57,11 +58,11 @@ def __try_live(project_id, data: schemas.CreateCardSchema):
return results return results
def __is_funnel_chart(data: schemas.CreateCardSchema): def __is_funnel_chart(data: schemas_ee.CreateCardSchema):
return data.metric_type == schemas.MetricType.funnel return data.metric_type == schemas.MetricType.funnel
def __get_funnel_chart(project_id, data: schemas.CreateCardSchema): def __get_funnel_chart(project_id, data: schemas_ee.CreateCardSchema):
if len(data.series) == 0: if len(data.series) == 0:
return { return {
"stages": [], "stages": [],
@ -72,12 +73,12 @@ def __get_funnel_chart(project_id, data: schemas.CreateCardSchema):
return funnels.get_top_insights_on_the_fly_widget(project_id=project_id, data=data.series[0].filter) return funnels.get_top_insights_on_the_fly_widget(project_id=project_id, data=data.series[0].filter)
def __is_errors_list(data: schemas.CreateCardSchema): def __is_errors_list(data: schemas_ee.CreateCardSchema):
return data.metric_type == schemas.MetricType.table \ return data.metric_type == schemas.MetricType.table \
and data.metric_of == schemas.MetricOfTable.errors and data.metric_of == schemas.MetricOfTable.errors
def __get_errors_list(project_id, user_id, data: schemas.CreateCardSchema): def __get_errors_list(project_id, user_id, data: schemas_ee.CreateCardSchema):
if len(data.series) == 0: if len(data.series) == 0:
return { return {
"total": 0, "total": 0,
@ -90,12 +91,12 @@ def __get_errors_list(project_id, user_id, data: schemas.CreateCardSchema):
return errors.search(data.series[0].filter, project_id=project_id, user_id=user_id) return errors.search(data.series[0].filter, project_id=project_id, user_id=user_id)
def __is_sessions_list(data: schemas.CreateCardSchema): def __is_sessions_list(data: schemas_ee.CreateCardSchema):
return data.metric_type == schemas.MetricType.table \ return data.metric_type == schemas.MetricType.table \
and data.metric_of == schemas.MetricOfTable.sessions and data.metric_of == schemas.MetricOfTable.sessions
def __get_sessions_list(project_id, user_id, data: schemas.CreateCardSchema): def __get_sessions_list(project_id, user_id, data: schemas_ee.CreateCardSchema):
if len(data.series) == 0: if len(data.series) == 0:
print("empty series") print("empty series")
return { return {
@ -109,15 +110,15 @@ def __get_sessions_list(project_id, user_id, data: schemas.CreateCardSchema):
return sessions.search_sessions(data=data.series[0].filter, project_id=project_id, user_id=user_id) return sessions.search_sessions(data=data.series[0].filter, project_id=project_id, user_id=user_id)
def __is_predefined(data: schemas.CreateCardSchema): def __is_predefined(data: schemas_ee.CreateCardSchema):
return data.is_template return data.is_template
def __is_click_map(data: schemas.CreateCardSchema): def __is_click_map(data: schemas_ee.CreateCardSchema):
return data.metric_type == schemas.MetricType.click_map return data.metric_type == schemas.MetricType.click_map
def __get_click_map_chat(project_id, user_id, data: schemas.CreateCardSchema): def __get_click_map_chat(project_id, user_id, data: schemas_ee.CreateCardSchema):
if len(data.series) == 0: if len(data.series) == 0:
return None return None
data.series[0].filter.startDate = data.startTimestamp data.series[0].filter.startDate = data.startTimestamp
@ -126,7 +127,20 @@ def __get_click_map_chat(project_id, user_id, data: schemas.CreateCardSchema):
data=schemas.FlatClickMapSessionsSearch(**data.series[0].filter.dict())) data=schemas.FlatClickMapSessionsSearch(**data.series[0].filter.dict()))
def merged_live(project_id, data: schemas.CreateCardSchema, user_id=None): # EE only
def __is_insights(data: schemas_ee.CreateCardSchema):
return data.metric_type == schemas.MetricType.insights
# EE only
def __get_insights_chat(project_id, user_id, data: schemas_ee.CreateCardSchema):
return sessions_insights.fetch_selected(project_id=project_id,
data=schemas_ee.GetInsightsSchema(startTimestamp=data.startTimestamp,
endTimestamp=data.endTimestamp,
categories=data.metric_value))
def merged_live(project_id, data: schemas_ee.CreateCardSchema, user_id=None):
if data.is_template: if data.is_template:
return get_predefined_metric(key=data.metric_of, project_id=project_id, data=data.dict()) return get_predefined_metric(key=data.metric_of, project_id=project_id, data=data.dict())
elif __is_funnel_chart(data): elif __is_funnel_chart(data):
@ -137,6 +151,9 @@ def merged_live(project_id, data: schemas.CreateCardSchema, user_id=None):
return __get_sessions_list(project_id=project_id, user_id=user_id, data=data) return __get_sessions_list(project_id=project_id, user_id=user_id, data=data)
elif __is_click_map(data): elif __is_click_map(data):
return __get_click_map_chat(project_id=project_id, user_id=user_id, data=data) return __get_click_map_chat(project_id=project_id, user_id=user_id, data=data)
# EE only
elif __is_insights(data):
return __get_insights_chat(project_id=project_id, user_id=user_id, data=data)
elif len(data.series) == 0: elif len(data.series) == 0:
return [] return []
series_charts = __try_live(project_id=project_id, data=data) series_charts = __try_live(project_id=project_id, data=data)
@ -150,11 +167,11 @@ def merged_live(project_id, data: schemas.CreateCardSchema, user_id=None):
return results return results
def __merge_metric_with_data(metric: schemas.CreateCardSchema, def __merge_metric_with_data(metric: schemas_ee.CreateCardSchema,
data: schemas.CardChartSchema) -> schemas.CreateCardSchema: data: schemas.CardChartSchema) -> schemas_ee.CreateCardSchema:
if data.series is not None and len(data.series) > 0: if data.series is not None and len(data.series) > 0:
metric.series = data.series metric.series = data.series
metric: schemas.CreateCardSchema = schemas.CreateCardSchema( metric: schemas_ee.CreateCardSchema = schemas_ee.CreateCardSchema(
**{**data.dict(by_alias=True), **metric.dict(by_alias=True)}) **{**data.dict(by_alias=True), **metric.dict(by_alias=True)})
if len(data.filters) > 0 or len(data.events) > 0: if len(data.filters) > 0 or len(data.events) > 0:
for s in metric.series: for s in metric.series:
@ -165,12 +182,13 @@ def __merge_metric_with_data(metric: schemas.CreateCardSchema,
return metric return metric
def make_chart(project_id, user_id, metric_id, data: schemas.CardChartSchema, metric: schemas.CreateCardSchema = None): def make_chart(project_id, user_id, metric_id, data: schemas.CardChartSchema,
metric: schemas_ee.CreateCardSchema = None):
if metric is None: if metric is None:
metric = get_card(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False) metric = get_card(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False)
if metric is None: if metric is None:
return None return None
metric: schemas.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data) metric: schemas_ee.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data)
return merged_live(project_id=project_id, data=metric, user_id=user_id) return merged_live(project_id=project_id, data=metric, user_id=user_id)
@ -179,8 +197,8 @@ def get_sessions(project_id, user_id, metric_id, data: schemas.CardSessionsSchem
raw_metric = get_card(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False) raw_metric = get_card(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False)
if raw_metric is None: if raw_metric is None:
return None return None
metric: schemas.CreateCardSchema = schemas.CreateCardSchema(**raw_metric) metric: schemas_ee.CreateCardSchema = schemas_ee.CreateCardSchema(**raw_metric)
metric: schemas.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data) metric: schemas_ee.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data)
if metric is None: if metric is None:
return None return None
results = [] results = []
@ -199,8 +217,8 @@ def get_funnel_issues(project_id, user_id, metric_id, data: schemas.CardSessions
raw_metric = get_card(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False) raw_metric = get_card(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False)
if raw_metric is None: if raw_metric is None:
return None return None
metric: schemas.CreateCardSchema = schemas.CreateCardSchema(**raw_metric) metric: schemas_ee.CreateCardSchema = schemas_ee.CreateCardSchema(**raw_metric)
metric: schemas.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data) metric: schemas_ee.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data)
if metric is None: if metric is None:
return None return None
for s in metric.series: for s in metric.series:
@ -216,8 +234,8 @@ def get_errors_list(project_id, user_id, metric_id, data: schemas.CardSessionsSc
raw_metric = get_card(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False) raw_metric = get_card(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False)
if raw_metric is None: if raw_metric is None:
return None return None
metric: schemas.CreateCardSchema = schemas.CreateCardSchema(**raw_metric) metric: schemas_ee.CreateCardSchema = schemas_ee.CreateCardSchema(**raw_metric)
metric: schemas.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data) metric: schemas_ee.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data)
if metric is None: if metric is None:
return None return None
for s in metric.series: for s in metric.series:
@ -244,7 +262,7 @@ def try_sessions(project_id, user_id, data: schemas.CardSessionsSchema):
return results return results
def create(project_id, user_id, data: schemas.CreateCardSchema, dashboard=False): def create(project_id, user_id, data: schemas_ee.CreateCardSchema, dashboard=False):
with pg_client.PostgresClient() as cur: with pg_client.PostgresClient() as cur:
_data = {} _data = {}
for i, s in enumerate(data.series): for i, s in enumerate(data.series):
@ -570,7 +588,7 @@ def get_funnel_sessions_by_issue(user_id, project_id, metric_id, issue_id,
metric = get_card(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False) metric = get_card(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False)
if metric is None: if metric is None:
return None return None
metric: schemas.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data) metric: schemas_ee.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data)
if metric is None: if metric is None:
return None return None
for s in metric.series: for s in metric.series:
@ -606,7 +624,7 @@ def make_chart_from_card(project_id, user_id, metric_id, data: schemas.CardChart
include_dashboard=False) include_dashboard=False)
if raw_metric is None: if raw_metric is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="card not found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="card not found")
metric: schemas.CreateCardSchema = schemas.CreateCardSchema(**raw_metric) metric: schemas_ee.CreateCardSchema = schemas_ee.CreateCardSchema(**raw_metric)
if metric.is_template: if metric.is_template:
return get_predefined_metric(key=metric.metric_of, project_id=project_id, data=data.dict()) return get_predefined_metric(key=metric.metric_of, project_id=project_id, data=data.dict())
else: else:

View file

@ -147,8 +147,6 @@ def query_requests_by_period(project_id, start_time, end_time, time_step, conn=N
def query_most_errors_by_period(project_id, start_time, end_time, time_step, conn=None): def query_most_errors_by_period(project_id, start_time, end_time, time_step, conn=None):
function, steps = __handle_timestep(time_step) function, steps = __handle_timestep(time_step)
print(function)
print(steps)
query = f"""WITH {function.format(f"toDateTime64('{start_time}', 0)")} as start, query = f"""WITH {function.format(f"toDateTime64('{start_time}', 0)")} as start,
{function.format(f"toDateTime64('{end_time}', 0)")} as end {function.format(f"toDateTime64('{end_time}', 0)")} as end
SELECT T1.hh, count(T2.session_id) as sessions, T2.name as names, SELECT T1.hh, count(T2.session_id) as sessions, T2.name as names,

View file

@ -3,6 +3,7 @@ from typing import Union
from fastapi import Body, Depends from fastapi import Body, Depends
import schemas import schemas
import schemas_ee
from chalicelib.core import dashboards, custom_metrics, funnels from chalicelib.core import dashboards, custom_metrics, funnels
from or_dependencies import OR_context, OR_scope from or_dependencies import OR_context, OR_scope
from routers.base import get_routers from routers.base import get_routers
@ -62,7 +63,7 @@ def add_card_to_dashboard(projectId: int, dashboardId: int,
@app.post('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard"]) @app.post('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard"])
@app.put('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard"]) @app.put('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard"])
def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int, def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int,
data: schemas.CreateCardSchema = Body(...), data: schemas_ee.CreateCardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)): context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards.create_metric_add_widget(project_id=projectId, user_id=context.user_id, return {"data": dashboards.create_metric_add_widget(project_id=projectId, user_id=context.user_id,
dashboard_id=dashboardId, data=data)} dashboard_id=dashboardId, data=data)}
@ -100,7 +101,7 @@ def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetId: int
@app.put('/{projectId}/metrics/try', tags=["dashboard"]) @app.put('/{projectId}/metrics/try', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/try', tags=["customMetrics"]) @app.post('/{projectId}/custom_metrics/try', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics/try', tags=["customMetrics"]) @app.put('/{projectId}/custom_metrics/try', tags=["customMetrics"])
def try_card(projectId: int, data: schemas.CreateCardSchema = Body(...), def try_card(projectId: int, data: schemas_ee.CreateCardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)): context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.merged_live(project_id=projectId, data=data, user_id=context.user_id)} return {"data": custom_metrics.merged_live(project_id=projectId, data=data, user_id=context.user_id)}
@ -139,7 +140,7 @@ def get_cards(projectId: int, context: schemas.CurrentContext = Depends(OR_conte
@app.put('/{projectId}/metrics', tags=["dashboard"]) @app.put('/{projectId}/metrics', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics', tags=["customMetrics"]) @app.post('/{projectId}/custom_metrics', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics', tags=["customMetrics"]) @app.put('/{projectId}/custom_metrics', tags=["customMetrics"])
def create_card(projectId: int, data: schemas.CreateCardSchema = Body(...), def create_card(projectId: int, data: schemas_ee.CreateCardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)): context: schemas.CurrentContext = Depends(OR_context)):
return custom_metrics.create(project_id=projectId, user_id=context.user_id, data=data) return custom_metrics.create(project_id=projectId, user_id=context.user_id, data=data)

View file

@ -1,10 +1,11 @@
from typing import Optional, List, Literal from enum import Enum
from typing import Optional, List, Union, Literal
from pydantic import BaseModel, Field, EmailStr from pydantic import BaseModel, Field, EmailStr
from pydantic import root_validator
import schemas import schemas
from chalicelib.utils.TimeUTC import TimeUTC from chalicelib.utils.TimeUTC import TimeUTC
from enum import Enum
class Permissions(str, Enum): class Permissions(str, Enum):
@ -134,3 +135,35 @@ class AssistRecordSearchPayloadSchema(schemas._PaginatedSchema):
class Config: class Config:
alias_generator = schemas.attribute_to_camel_case alias_generator = schemas.attribute_to_camel_case
# TODO: move these to schema when Insights is supported on PG
class MetricOfInsights(str, Enum):
issue_categories = "issueCategories"
class CreateCardSchema(schemas.CreateCardSchema):
metric_of: Union[schemas.MetricOfTimeseries, schemas.MetricOfTable, \
schemas.MetricOfErrors, schemas.MetricOfPerformance, \
schemas.MetricOfResources, schemas.MetricOfWebVitals, \
schemas.MetricOfClickMap, MetricOfInsights] = Field(default=schemas.MetricOfTable.user_id)
metric_value: List[Union[schemas.IssueType, InsightCategories]] = Field(default=[])
@root_validator
def restrictions(cls, values):
return values
@root_validator
def validator(cls, values):
values = super().validator(values)
if values.get("metric_type") == schemas.MetricType.insights:
assert values.get("view_type") == schemas.MetricOtherViewType.list_chart, \
f"viewType must be 'list' for metricOf:{values.get('metric_of')}"
assert isinstance(values.get("metric_of"), MetricOfInsights), \
f"metricOf must be of type {MetricOfInsights} for metricType:{schemas.MetricType.insights}"
if values.get("metric_value") is not None and len(values.get("metric_value")) > 0:
for i in values.get("metric_value"):
assert isinstance(i, InsightCategories), \
f"metricValue should be of type [InsightCategories] for metricType:{schemas.MetricType.insights}"
return values