feat(chalice): cards 2/5
feat(DB): transform predefined metrics to new format
This commit is contained in:
parent
e634bacc3a
commit
5e80a016bf
6 changed files with 345 additions and 170 deletions
|
|
@ -9,7 +9,7 @@ from chalicelib.utils.TimeUTC import TimeUTC
|
|||
PIE_CHART_GROUP = 5
|
||||
|
||||
|
||||
def __try_live(project_id, data: schemas.TryCardSchema):
|
||||
def __try_live(project_id, data: schemas.CreateCardSchema):
|
||||
results = []
|
||||
for i, s in enumerate(data.series):
|
||||
s.filter.startDate = data.startTimestamp
|
||||
|
|
@ -42,11 +42,11 @@ def __try_live(project_id, data: schemas.TryCardSchema):
|
|||
return results
|
||||
|
||||
|
||||
def __is_funnel_chart(data: schemas.TryCardSchema):
|
||||
def __is_funnel_chart(data: schemas.CreateCardSchema):
|
||||
return data.metric_type == schemas.MetricType.funnel
|
||||
|
||||
|
||||
def __get_funnel_chart(project_id, data: schemas.TryCardSchema):
|
||||
def __get_funnel_chart(project_id, data: schemas.CreateCardSchema):
|
||||
if len(data.series) == 0:
|
||||
return {
|
||||
"stages": [],
|
||||
|
|
@ -94,7 +94,7 @@ def __get_sessions_list(project_id, user_id, data):
|
|||
return sessions.search_sessions(data=data.series[0].filter, project_id=project_id, user_id=user_id)
|
||||
|
||||
|
||||
def merged_live(project_id, data: schemas.TryCardSchema, user_id=None):
|
||||
def merged_live(project_id, data: schemas.CreateCardSchema, user_id=None):
|
||||
if __is_funnel_chart(data):
|
||||
return __get_funnel_chart(project_id=project_id, data=data)
|
||||
elif __is_errors_list(data):
|
||||
|
|
@ -130,7 +130,7 @@ def __merge_metric_with_data(metric, data: Union[schemas.CustomMetricChartPayloa
|
|||
|
||||
def make_chart(project_id, user_id, metric_id, data: schemas.CustomMetricChartPayloadSchema, metric=None):
|
||||
if metric is None:
|
||||
metric = get(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:
|
||||
return None
|
||||
metric: schemas.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data)
|
||||
|
|
@ -153,7 +153,7 @@ def make_chart(project_id, user_id, metric_id, data: schemas.CustomMetricChartPa
|
|||
|
||||
|
||||
def get_sessions(project_id, user_id, metric_id, data: schemas.CustomMetricSessionsPayloadSchema):
|
||||
metric = get(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:
|
||||
return None
|
||||
metric: schemas.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data)
|
||||
|
|
@ -172,7 +172,7 @@ def get_sessions(project_id, user_id, metric_id, data: schemas.CustomMetricSessi
|
|||
|
||||
|
||||
def get_funnel_issues(project_id, user_id, metric_id, data: schemas.CustomMetricSessionsPayloadSchema):
|
||||
metric = get(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:
|
||||
return None
|
||||
metric: schemas.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data)
|
||||
|
|
@ -188,7 +188,7 @@ def get_funnel_issues(project_id, user_id, metric_id, data: schemas.CustomMetric
|
|||
|
||||
|
||||
def get_errors_list(project_id, user_id, metric_id, data: schemas.CustomMetricSessionsPayloadSchema):
|
||||
metric = get(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:
|
||||
return None
|
||||
metric: schemas.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data)
|
||||
|
|
@ -227,35 +227,35 @@ def create(project_id, user_id, data: schemas.CreateCardSchema, dashboard=False)
|
|||
_data[f"index_{i}"] = i
|
||||
_data[f"filter_{i}"] = s.filter.json()
|
||||
series_len = len(data.series)
|
||||
data.series = None
|
||||
params = {"user_id": user_id, "project_id": project_id,
|
||||
"default_config": json.dumps(data.config.dict()),
|
||||
**data.dict(), **_data}
|
||||
query = cur.mogrify(f"""\
|
||||
WITH m AS (INSERT INTO metrics (project_id, user_id, name, is_public,
|
||||
view_type, metric_type, metric_of, metric_value,
|
||||
metric_format, default_config)
|
||||
VALUES (%(project_id)s, %(user_id)s, %(name)s, %(is_public)s,
|
||||
%(view_type)s, %(metric_type)s, %(metric_of)s, %(metric_value)s,
|
||||
%(metric_format)s, %(default_config)s)
|
||||
RETURNING *)
|
||||
INSERT
|
||||
INTO metric_series(metric_id, index, name, filter)
|
||||
VALUES {",".join([f"((SELECT metric_id FROM m), %(index_{i})s, %(name_{i})s, %(filter_{i})s::jsonb)"
|
||||
for i in range(series_len)])}
|
||||
RETURNING metric_id;""", params)
|
||||
params = {"user_id": user_id, "project_id": project_id, **data.dict(), **_data}
|
||||
params["default_config"] = json.dumps(data.default_config.dict())
|
||||
query = """INSERT INTO metrics_clone (project_id, user_id, name, is_public,
|
||||
view_type, metric_type, metric_of, metric_value,
|
||||
metric_format, default_config)
|
||||
VALUES (%(project_id)s, %(user_id)s, %(name)s, %(is_public)s,
|
||||
%(view_type)s, %(metric_type)s, %(metric_of)s, %(metric_value)s,
|
||||
%(metric_format)s, %(default_config)s)
|
||||
RETURNING metric_id"""
|
||||
if len(data.series) > 0:
|
||||
query = f"""WITH m AS ({query})
|
||||
INSERT INTO metric_series(metric_id, index, name, filter)
|
||||
VALUES {",".join([f"((SELECT metric_id FROM m), %(index_{i})s, %(name_{i})s, %(filter_{i})s::jsonb)"
|
||||
for i in range(series_len)])}
|
||||
RETURNING metric_id;"""
|
||||
|
||||
cur.execute(
|
||||
query
|
||||
)
|
||||
query = cur.mogrify(query, params)
|
||||
print("-------")
|
||||
print(query)
|
||||
print("-------")
|
||||
cur.execute(query)
|
||||
r = cur.fetchone()
|
||||
if dashboard:
|
||||
return r["metric_id"]
|
||||
return {"data": get(metric_id=r["metric_id"], project_id=project_id, user_id=user_id)}
|
||||
return {"data": get_card(metric_id=r["metric_id"], project_id=project_id, user_id=user_id)}
|
||||
|
||||
|
||||
def update(metric_id, user_id, project_id, data: schemas.UpdateCardSchema):
|
||||
metric = get(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:
|
||||
return None
|
||||
series_ids = [r["seriesId"] for r in metric["series"]]
|
||||
|
|
@ -312,7 +312,7 @@ def update(metric_id, user_id, project_id, data: schemas.UpdateCardSchema):
|
|||
RETURNING 1)""")
|
||||
query = cur.mogrify(f"""\
|
||||
{"WITH " if len(sub_queries) > 0 else ""}{",".join(sub_queries)}
|
||||
UPDATE metrics
|
||||
UPDATE metrics_clone
|
||||
SET name = %(name)s, is_public= %(is_public)s,
|
||||
view_type= %(view_type)s, metric_type= %(metric_type)s,
|
||||
metric_of= %(metric_of)s, metric_value= %(metric_value)s,
|
||||
|
|
@ -324,7 +324,7 @@ def update(metric_id, user_id, project_id, data: schemas.UpdateCardSchema):
|
|||
AND (user_id = %(user_id)s OR is_public)
|
||||
RETURNING metric_id;""", params)
|
||||
cur.execute(query)
|
||||
return get(metric_id=metric_id, project_id=project_id, user_id=user_id)
|
||||
return get_card(metric_id=metric_id, project_id=project_id, user_id=user_id)
|
||||
|
||||
|
||||
def get_all(project_id, user_id, include_series=False):
|
||||
|
|
@ -333,31 +333,31 @@ def get_all(project_id, user_id, include_series=False):
|
|||
if include_series:
|
||||
sub_join = """LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(metric_series.* ORDER BY index),'[]'::jsonb) AS series
|
||||
FROM metric_series
|
||||
WHERE metric_series.metric_id = metrics.metric_id
|
||||
WHERE metric_series.metric_id = metrics_clone.metric_id
|
||||
AND metric_series.deleted_at ISNULL
|
||||
) AS metric_series ON (TRUE)"""
|
||||
cur.execute(
|
||||
cur.mogrify(
|
||||
f"""SELECT *
|
||||
FROM metrics
|
||||
FROM metrics_clone
|
||||
{sub_join}
|
||||
LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards
|
||||
FROM (SELECT DISTINCT dashboard_id, name, is_public
|
||||
FROM dashboards INNER JOIN dashboard_widgets USING (dashboard_id)
|
||||
WHERE deleted_at ISNULL
|
||||
AND dashboard_widgets.metric_id = metrics.metric_id
|
||||
AND dashboard_widgets.metric_id = metrics_clone.metric_id
|
||||
AND project_id = %(project_id)s
|
||||
AND ((dashboards.user_id = %(user_id)s OR is_public))) AS connected_dashboards
|
||||
) AS connected_dashboards ON (TRUE)
|
||||
LEFT JOIN LATERAL (SELECT email AS owner_email
|
||||
FROM users
|
||||
WHERE deleted_at ISNULL
|
||||
AND users.user_id = metrics.user_id
|
||||
AND users.user_id = metrics_clone.user_id
|
||||
) AS owner ON (TRUE)
|
||||
WHERE metrics.project_id = %(project_id)s
|
||||
AND metrics.deleted_at ISNULL
|
||||
AND (user_id = %(user_id)s OR metrics.is_public)
|
||||
ORDER BY metrics.edited_at DESC, metrics.created_at DESC;""",
|
||||
WHERE metrics_clone.project_id = %(project_id)s
|
||||
AND metrics_clone.deleted_at ISNULL
|
||||
AND (user_id = %(user_id)s OR metrics_clone.is_public)
|
||||
ORDER BY metrics_clone.edited_at DESC, metrics_clone.created_at DESC;""",
|
||||
{"project_id": project_id, "user_id": user_id}
|
||||
)
|
||||
)
|
||||
|
|
@ -379,7 +379,7 @@ def delete(project_id, metric_id, user_id):
|
|||
with pg_client.PostgresClient() as cur:
|
||||
cur.execute(
|
||||
cur.mogrify("""\
|
||||
UPDATE public.metrics
|
||||
UPDATE public.metrics_clone
|
||||
SET deleted_at = timezone('utc'::text, now()), edited_at = timezone('utc'::text, now())
|
||||
WHERE project_id = %(project_id)s
|
||||
AND metric_id = %(metric_id)s
|
||||
|
|
@ -390,37 +390,37 @@ def delete(project_id, metric_id, user_id):
|
|||
return {"state": "success"}
|
||||
|
||||
|
||||
def get(metric_id, project_id, user_id, flatten=True):
|
||||
def get_card(metric_id, project_id, user_id, flatten=True):
|
||||
with pg_client.PostgresClient() as cur:
|
||||
cur.execute(
|
||||
cur.mogrify(
|
||||
"""SELECT *, default_config AS config
|
||||
FROM metrics
|
||||
LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(metric_series.* ORDER BY index),'[]'::jsonb) AS series
|
||||
FROM metric_series
|
||||
WHERE metric_series.metric_id = metrics.metric_id
|
||||
AND metric_series.deleted_at ISNULL
|
||||
) AS metric_series ON (TRUE)
|
||||
LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards
|
||||
FROM (SELECT dashboard_id, name, is_public
|
||||
FROM dashboards
|
||||
WHERE deleted_at ISNULL
|
||||
AND project_id = %(project_id)s
|
||||
AND ((user_id = %(user_id)s OR is_public))) AS connected_dashboards
|
||||
) AS connected_dashboards ON (TRUE)
|
||||
LEFT JOIN LATERAL (SELECT email AS owner_email
|
||||
FROM users
|
||||
WHERE deleted_at ISNULL
|
||||
AND users.user_id = metrics.user_id
|
||||
) AS owner ON (TRUE)
|
||||
WHERE metrics.project_id = %(project_id)s
|
||||
AND metrics.deleted_at ISNULL
|
||||
AND (metrics.user_id = %(user_id)s OR metrics.is_public)
|
||||
AND metrics.metric_id = %(metric_id)s
|
||||
ORDER BY created_at;""",
|
||||
{"metric_id": metric_id, "project_id": project_id, "user_id": user_id}
|
||||
)
|
||||
query = cur.mogrify(
|
||||
"""SELECT *, default_config AS config
|
||||
FROM metrics_clone
|
||||
LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(metric_series.* ORDER BY index),'[]'::jsonb) AS series
|
||||
FROM metric_series
|
||||
WHERE metric_series.metric_id = metrics_clone.metric_id
|
||||
AND metric_series.deleted_at ISNULL
|
||||
) AS metric_series ON (TRUE)
|
||||
LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards
|
||||
FROM (SELECT dashboard_id, name, is_public
|
||||
FROM dashboards INNER JOIN dashboard_widgets USING (dashboard_id)
|
||||
WHERE deleted_at ISNULL
|
||||
AND project_id = %(project_id)s
|
||||
AND ((dashboards.user_id = %(user_id)s OR is_public))
|
||||
AND metric_id = %(metric_id)s) AS connected_dashboards
|
||||
) AS connected_dashboards ON (TRUE)
|
||||
LEFT JOIN LATERAL (SELECT email AS owner_email
|
||||
FROM users
|
||||
WHERE deleted_at ISNULL
|
||||
AND users.user_id = metrics_clone.user_id
|
||||
) AS owner ON (TRUE)
|
||||
WHERE metrics_clone.project_id = %(project_id)s
|
||||
AND metrics_clone.deleted_at ISNULL
|
||||
AND (metrics_clone.user_id = %(user_id)s OR metrics_clone.is_public)
|
||||
AND metrics_clone.metric_id = %(metric_id)s
|
||||
ORDER BY created_at;""",
|
||||
{"metric_id": metric_id, "project_id": project_id, "user_id": user_id}
|
||||
)
|
||||
cur.execute(query)
|
||||
row = cur.fetchone()
|
||||
if row is None:
|
||||
return None
|
||||
|
|
@ -446,17 +446,17 @@ def get_with_template(metric_id, project_id, user_id, include_dashboard=True):
|
|||
cur.execute(
|
||||
cur.mogrify(
|
||||
f"""SELECT *, default_config AS config
|
||||
FROM metrics
|
||||
FROM metrics_clone
|
||||
LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(metric_series.* ORDER BY index),'[]'::jsonb) AS series
|
||||
FROM metric_series
|
||||
WHERE metric_series.metric_id = metrics.metric_id
|
||||
WHERE metric_series.metric_id = metrics_clone.metric_id
|
||||
AND metric_series.deleted_at ISNULL
|
||||
) AS metric_series ON (TRUE)
|
||||
{sub_query}
|
||||
WHERE (metrics.project_id = %(project_id)s OR metrics.project_id ISNULL)
|
||||
AND metrics.deleted_at ISNULL
|
||||
AND (metrics.user_id = %(user_id)s OR metrics.is_public)
|
||||
AND metrics.metric_id = %(metric_id)s
|
||||
WHERE (metrics_clone.project_id = %(project_id)s OR metrics_clone.project_id ISNULL)
|
||||
AND metrics_clone.deleted_at ISNULL
|
||||
AND (metrics_clone.user_id = %(user_id)s OR metrics_clone.is_public)
|
||||
AND metrics_clone.metric_id = %(metric_id)s
|
||||
ORDER BY created_at;""",
|
||||
{"metric_id": metric_id, "project_id": project_id, "user_id": user_id}
|
||||
)
|
||||
|
|
@ -470,16 +470,16 @@ def get_series_for_alert(project_id, user_id):
|
|||
cur.execute(
|
||||
cur.mogrify(
|
||||
"""SELECT series_id AS value,
|
||||
metrics.name || '.' || (COALESCE(metric_series.name, 'series ' || index)) || '.count' AS name,
|
||||
metrics_clone.name || '.' || (COALESCE(metric_series.name, 'series ' || index)) || '.count' AS name,
|
||||
'count' AS unit,
|
||||
FALSE AS predefined,
|
||||
metric_id,
|
||||
series_id
|
||||
FROM metric_series
|
||||
INNER JOIN metrics USING (metric_id)
|
||||
WHERE metrics.deleted_at ISNULL
|
||||
AND metrics.project_id = %(project_id)s
|
||||
AND metrics.metric_type = 'timeseries'
|
||||
INNER JOIN metrics_clone USING (metric_id)
|
||||
WHERE metrics_clone.deleted_at ISNULL
|
||||
AND metrics_clone.project_id = %(project_id)s
|
||||
AND metrics_clone.metric_type = 'timeseries'
|
||||
AND (user_id = %(user_id)s OR is_public)
|
||||
ORDER BY name;""",
|
||||
{"project_id": project_id, "user_id": user_id}
|
||||
|
|
@ -493,20 +493,20 @@ def change_state(project_id, metric_id, user_id, status):
|
|||
with pg_client.PostgresClient() as cur:
|
||||
cur.execute(
|
||||
cur.mogrify("""\
|
||||
UPDATE public.metrics
|
||||
UPDATE public.metrics_clone
|
||||
SET active = %(status)s
|
||||
WHERE metric_id = %(metric_id)s
|
||||
AND (user_id = %(user_id)s OR is_public);""",
|
||||
{"metric_id": metric_id, "status": status, "user_id": user_id})
|
||||
)
|
||||
return get(metric_id=metric_id, project_id=project_id, user_id=user_id)
|
||||
return get_card(metric_id=metric_id, project_id=project_id, user_id=user_id)
|
||||
|
||||
|
||||
def get_funnel_sessions_by_issue(user_id, project_id, metric_id, issue_id,
|
||||
data: schemas.CustomMetricSessionsPayloadSchema
|
||||
# , range_value=None, start_date=None, end_date=None
|
||||
):
|
||||
metric = get(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:
|
||||
return None
|
||||
metric: schemas.CreateCardSchema = __merge_metric_with_data(metric=metric, data=data)
|
||||
|
|
|
|||
|
|
@ -304,13 +304,13 @@ def search_sessions(data: schemas.SessionsSearchPayloadSchema, project_id, user_
|
|||
|
||||
def search2_series(data: schemas.SessionsSearchPayloadSchema, project_id: int, density: int,
|
||||
view_type: schemas.MetricTimeseriesViewType, metric_type: schemas.MetricType,
|
||||
metric_of: schemas.TableMetricOfType, metric_value: List):
|
||||
metric_of: schemas.MetricOfTable, metric_value: List):
|
||||
step_size = int(metrics_helper.__get_step_size(endTimestamp=data.endDate, startTimestamp=data.startDate,
|
||||
density=density, factor=1, decimal=True))
|
||||
extra_event = None
|
||||
if metric_of == schemas.TableMetricOfType.visited_url:
|
||||
if metric_of == schemas.MetricOfTable.visited_url:
|
||||
extra_event = "events.pages"
|
||||
elif metric_of == schemas.TableMetricOfType.issues and len(metric_value) > 0:
|
||||
elif metric_of == schemas.MetricOfTable.issues and len(metric_value) > 0:
|
||||
data.filters.append(schemas.SessionSearchFilterSchema(value=metric_value, type=schemas.FilterType.issue,
|
||||
operator=schemas.SearchEventOperator._is))
|
||||
full_args, query_part = search_query_parts(data=data, error_status=None, errors_only=False,
|
||||
|
|
|
|||
|
|
@ -46,11 +46,12 @@ def pin_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentCont
|
|||
return {"data": dashboards.pin_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboards/{dashboardId}/cards', tags=["cards"])
|
||||
@app.post('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"])
|
||||
@app.put('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"])
|
||||
def add_widget_to_dashboard(projectId: int, dashboardId: int,
|
||||
data: schemas.AddWidgetToDashboardPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
def add_card_to_dashboard(projectId: int, dashboardId: int,
|
||||
data: schemas.AddWidgetToDashboardPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": dashboards.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId,
|
||||
data=data)}
|
||||
|
||||
|
|
@ -100,7 +101,7 @@ def get_templates(projectId: int, context: schemas.CurrentContext = Depends(OR_c
|
|||
@app.put('/{projectId}/metrics/try', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics/try', tags=["customMetrics"])
|
||||
@app.put('/{projectId}/custom_metrics/try', tags=["customMetrics"])
|
||||
def try_custom_metric(projectId: int, data: schemas.TryCardSchema = Body(...),
|
||||
def try_custom_metric(projectId: int, data: schemas.CreateCardSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": custom_metrics.merged_live(project_id=projectId, data=data, user_id=context.user_id)}
|
||||
|
||||
|
|
@ -132,7 +133,6 @@ def try_custom_metric_funnel_issues(projectId: int, data: schemas.CustomMetricSe
|
|||
@app.put('/{projectId}/custom_metrics', tags=["customMetrics"])
|
||||
def create_card(projectId: int, data: schemas.CreateCardSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": data.dict()}
|
||||
return custom_metrics.create(project_id=projectId, user_id=context.user_id, data=data)
|
||||
|
||||
|
||||
|
|
@ -140,35 +140,6 @@ def create_card(projectId: int, data: schemas.CreateCardSchema = Body(...),
|
|||
@app.get('/{projectId}/metrics', tags=["dashboard"])
|
||||
@app.get('/{projectId}/custom_metrics', tags=["customMetrics"])
|
||||
def get_cards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {
|
||||
"data": [
|
||||
{
|
||||
"metricId": 1180,
|
||||
"projectId": 5053,
|
||||
"userId": 283,
|
||||
"name": "ts1",
|
||||
"isPublic": true,
|
||||
"createdAt": 1669818461027,
|
||||
"deletedAt": null,
|
||||
"active": true,
|
||||
"metricType": "timeseries",
|
||||
"viewType": "lineChart",
|
||||
"metricOf": "sessionCount",
|
||||
"metricValue": [],
|
||||
"metricFormat": "sessionCount",
|
||||
"editedAt": 1669818461027,
|
||||
"category": "custom",
|
||||
"predefinedKey": null,
|
||||
"defaultConfig": {
|
||||
"col": 2,
|
||||
"row": 2,
|
||||
"position": 0
|
||||
},
|
||||
"dashboards": [],
|
||||
"ownerEmail": "tahay@asayer.io"
|
||||
}
|
||||
]
|
||||
}
|
||||
return {"data": custom_metrics.get_all(project_id=projectId, user_id=context.user_id)}
|
||||
|
||||
|
||||
|
|
@ -176,7 +147,7 @@ def get_cards(projectId: int, context: schemas.CurrentContext = Depends(OR_conte
|
|||
@app.get('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
|
||||
@app.get('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
|
||||
def get_card(projectId: int, metric_id: str, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = custom_metrics.get(project_id=projectId, user_id=context.user_id, metric_id=metric_id)
|
||||
data = custom_metrics.get_card(project_id=projectId, user_id=context.user_id, metric_id=metric_id)
|
||||
if data is None:
|
||||
return {"errors": ["card not found"]}
|
||||
return {"data": data}
|
||||
|
|
|
|||
160
api/schemas.py
160
api/schemas.py
|
|
@ -580,7 +580,7 @@ class _SessionSearchEventSchema(_SessionSearchEventRaw):
|
|||
class SessionSearchFilterSchema(__MixedSearchFilter):
|
||||
is_event: bool = Field(False, const=False)
|
||||
value: Union[Optional[Union[IssueType, PlatformType, int, str]],
|
||||
Optional[List[Union[IssueType, PlatformType, int, str]]]] = Field(...)
|
||||
Optional[List[Union[IssueType, PlatformType, int, str]]]] = Field(...)
|
||||
type: FilterType = Field(...)
|
||||
operator: Union[SearchEventOperator, MathOperator] = Field(...)
|
||||
source: Optional[Union[ErrorSource, str]] = Field(default=None)
|
||||
|
|
@ -792,26 +792,94 @@ class MetricTableViewType(str, Enum):
|
|||
pie_chart = "pieChart"
|
||||
|
||||
|
||||
class MetricOtherViewType(str, Enum):
|
||||
other_chart = "chart"
|
||||
|
||||
|
||||
class MetricType(str, Enum):
|
||||
timeseries = "timeseries"
|
||||
table = "table"
|
||||
predefined = "predefined"
|
||||
funnel = "funnel"
|
||||
errors = "errors"
|
||||
performance = "performance"
|
||||
resources = "resources"
|
||||
web_vital = "webVital"
|
||||
pathAnalysis = "pathAnalysis"
|
||||
retention = "retention"
|
||||
stickiness = "stickiness"
|
||||
click_map = "clickMap"
|
||||
|
||||
|
||||
class TableMetricOfType(str, Enum):
|
||||
user_os = FilterType.user_os.value
|
||||
user_browser = FilterType.user_browser.value
|
||||
user_device = FilterType.user_device.value
|
||||
user_country = FilterType.user_country.value
|
||||
user_id = FilterType.user_id.value
|
||||
issues = FilterType.issue.value
|
||||
visited_url = EventType.location.value
|
||||
sessions = "SESSIONS"
|
||||
errors = IssueType.js_exception.value
|
||||
class MetricOfErrors(str, Enum):
|
||||
calls_errors = "callsErrors" # calls_errors
|
||||
domains_errors_4xx = "domainsErrors4Xx" # domains_errors_4xx
|
||||
domains_errors_5xx = "domainsErrors5Xx" # domains_errors_5xx
|
||||
errors_per_domains = "errorsPerDomains" # errors_per_domains
|
||||
errors_per_type = "errorsPerType" # errors_per_type
|
||||
impacted_sessions_by_js_errors = "impactedSessionsByJsErrors" # impacted_sessions_by_js_errors
|
||||
resources_by_party = "resourcesByParty" # resources_by_party
|
||||
|
||||
|
||||
class TimeseriesMetricOfType(str, Enum):
|
||||
class MetricOfPerformance(str, Enum):
|
||||
cpu = "cpu" # cpu
|
||||
crashes = "crashes" # crashes
|
||||
fps = "fps" # fps
|
||||
impacted_sessions_by_slow_pages = "impactedSessionsBySlowPages" # impacted_sessions_by_slow_pages
|
||||
memory_consumption = "memoryConsumption" # memory_consumption
|
||||
pages_dom_buildtime = "pagesDomBuildtime" # pages_dom_buildtime
|
||||
pages_response_time = "pagesResponseTime" # pages_response_time
|
||||
pages_response_time_distribution = "pagesResponseTimeDistribution" # pages_response_time_distribution
|
||||
resources_vs_visually_complete = "resourcesVsVisuallyComplete" # resources_vs_visually_complete
|
||||
sessions_per_browser = "sessionsPerBrowser" # sessions_per_browser
|
||||
slowest_domains = "slowestDomains" # slowest_domains
|
||||
speed_location = "speedLocation" # speed_location
|
||||
time_to_render = "timeToRender" # time_to_render
|
||||
|
||||
|
||||
class MetricOfResources(str, Enum):
|
||||
missing_resources = "missingResources" # missing_resources
|
||||
resources_count_by_type = "resourcesCountByType" # resources_count_by_type
|
||||
resources_loading_time = "resourcesLoadingTime" # resources_loading_time
|
||||
resource_type_vs_response_end = "resourceTypeVsResponseEnd" # resource_type_vs_response_end
|
||||
slowest_resources = "slowestResources" # slowest_resources
|
||||
|
||||
|
||||
class MetricOfWebVitals(str, Enum):
|
||||
avg_cpu = "avgCpu" # avg_cpu
|
||||
avg_dom_content_loaded = "avgDomContentLoaded" # avg_dom_content_loaded
|
||||
avg_dom_content_load_start = "avgDomContentLoadStart" # avg_dom_content_load_start
|
||||
avg_first_contentful_pixel = "avgFirstContentfulPixel" # avg_first_contentful_pixel
|
||||
avg_first_paint = "avgFirstPaint" # avg_first_paint
|
||||
avg_fps = "avgFps" # avg_fps
|
||||
avg_image_load_time = "avgImageLoadTime" # avg_image_load_time
|
||||
avg_page_load_time = "avgPageLoadTime" # avg_page_load_time
|
||||
avg_pages_dom_buildtime = "avgPagesDomBuildtime" # avg_pages_dom_buildtime
|
||||
avg_pages_response_time = "avgPagesResponseTime" # avg_pages_response_time
|
||||
avg_request_load_time = "avgRequestLoadTime" # avg_request_load_time
|
||||
avg_response_time = "avgResponseTime" # avg_response_time
|
||||
avg_session_duration = "avgSessionDuration" # avg_session_duration
|
||||
avg_till_first_byte = "avgTillFirstByte" # avg_till_first_byte
|
||||
avg_time_to_interactive = "avgTimeToInteractive" # avg_time_to_interactive
|
||||
avg_time_to_render = "avgTimeToRender" # avg_time_to_render
|
||||
avg_used_js_heap_size = "avgUsedJsHeapSize" # avg_used_js_heap_size
|
||||
avg_visited_pages = "avgVisitedPages" # avg_visited_pages
|
||||
count_requests = "countRequests" # count_requests
|
||||
count_sessions = "countSessions" # count_sessions
|
||||
|
||||
|
||||
class MetricOfTable(str, Enum):
|
||||
user_os = "userOS" # USEROS
|
||||
user_browser = "userBrowser" # USERBROWSER
|
||||
user_device = "userDevice" # USERDEVICE
|
||||
user_country = "userCountry" # USERCOUNTRY
|
||||
user_id = "userId" # USERID
|
||||
issues = "issue" # ISSUE
|
||||
visited_url = "location" # LOCATION
|
||||
sessions = "sessions" # SESSIONS
|
||||
errors = "jsException" # js_exception
|
||||
|
||||
|
||||
class MetricOfTimeseries(str, Enum):
|
||||
session_count = "sessionCount"
|
||||
|
||||
|
||||
|
|
@ -831,22 +899,31 @@ class CustomMetricChartPayloadSchema(CustomMetricSessionsPayloadSchema, _Paginat
|
|||
alias_generator = attribute_to_camel_case
|
||||
|
||||
|
||||
class TryCardSchema(CustomMetricChartPayloadSchema):
|
||||
class CustomMetricsConfigSchema(BaseModel):
|
||||
col: Optional[int] = Field(...)
|
||||
row: Optional[int] = Field(default=2)
|
||||
position: Optional[int] = Field(default=0)
|
||||
|
||||
|
||||
class CreateCardSchema(CustomMetricChartPayloadSchema):
|
||||
name: Optional[str] = Field(...)
|
||||
series: List[CustomMetricCreateSeriesSchema] = Field(...)
|
||||
series: List[CustomMetricCreateSeriesSchema] = Field(default=[])
|
||||
is_public: bool = Field(default=True)
|
||||
view_type: Union[MetricTimeseriesViewType, MetricTableViewType, str] = Field(MetricTimeseriesViewType.line_chart)
|
||||
metric_type: Union[MetricType, str] = Field(MetricType.timeseries)
|
||||
metric_of: Union[TableMetricOfType, TimeseriesMetricOfType, str] = Field(TableMetricOfType.user_id)
|
||||
view_type: Union[MetricTimeseriesViewType, MetricTableViewType, MetricOtherViewType] \
|
||||
= Field(MetricTimeseriesViewType.line_chart)
|
||||
metric_type: Union[MetricType] = Field(default=MetricType.timeseries)
|
||||
metric_of: Union[MetricOfTimeseries, MetricOfTable, MetricOfErrors, MetricOfPerformance,
|
||||
MetricOfResources, MetricOfWebVitals] = Field(MetricOfTable.user_id)
|
||||
metric_value: List[IssueType] = Field([])
|
||||
metric_format: Optional[MetricFormatType] = Field(None)
|
||||
default_config: CustomMetricsConfigSchema = Field(...)
|
||||
|
||||
# This is used to handle wrong values sent by the UI
|
||||
@root_validator(pre=True)
|
||||
def transform(cls, values):
|
||||
if values.get("metricType") == MetricType.timeseries \
|
||||
or values.get("metricType") == MetricType.table \
|
||||
and values.get("metricOf") != TableMetricOfType.issues:
|
||||
and values.get("metricOf") != MetricOfTable.issues:
|
||||
values["metricValue"] = []
|
||||
|
||||
if values.get("metric_type") == MetricType.funnel.value and \
|
||||
|
|
@ -858,33 +935,40 @@ class TryCardSchema(CustomMetricChartPayloadSchema):
|
|||
def validator(cls, values):
|
||||
if values.get("metric_type") == MetricType.table:
|
||||
assert isinstance(values.get("view_type"), MetricTableViewType), \
|
||||
f"viewType must be of type {MetricTableViewType} for metricType:{MetricType.table.value}"
|
||||
assert isinstance(values.get("metric_of"), TableMetricOfType), \
|
||||
f"metricOf must be of type {TableMetricOfType} for metricType:{MetricType.table.value}"
|
||||
if values.get("metric_of") != TableMetricOfType.issues:
|
||||
f"viewType must be of type {MetricTableViewType} for metricType:{MetricType.table}"
|
||||
assert isinstance(values.get("metric_of"), MetricOfTable), \
|
||||
f"metricOf must be of type {MetricOfTable} for metricType:{MetricType.table}"
|
||||
if values.get("metric_of") != MetricOfTable.issues:
|
||||
assert values.get("metric_value") is None or len(values.get("metric_value")) == 0, \
|
||||
f"metricValue is only available for metricOf:{TableMetricOfType.issues.value}"
|
||||
f"metricValue is only available for metricOf:{MetricOfTable.issues}"
|
||||
elif values.get("metric_type") == MetricType.timeseries:
|
||||
assert isinstance(values.get("view_type"), MetricTimeseriesViewType), \
|
||||
f"viewType must be of type {MetricTimeseriesViewType} for metricType:{MetricType.timeseries.value}"
|
||||
assert isinstance(values.get("metric_of"), TimeseriesMetricOfType), \
|
||||
f"metricOf must be of type {TimeseriesMetricOfType} for metricType:{MetricType.timeseries.value}"
|
||||
f"viewType must be of type {MetricTimeseriesViewType} for metricType:{MetricType.timeseries}"
|
||||
assert isinstance(values.get("metric_of"), MetricOfTimeseries), \
|
||||
f"metricOf must be of type {MetricOfTimeseries} for metricType:{MetricType.timeseries}"
|
||||
else:
|
||||
if values.get("metric_type") == MetricType.errors:
|
||||
assert isinstance(values.get("metric_of"), MetricOfErrors), \
|
||||
f"metricOf must be of type {MetricOfErrors} for metricType:{MetricType.errors}"
|
||||
elif values.get("metric_type") == MetricType.performance:
|
||||
assert isinstance(values.get("metric_of"), MetricOfPerformance), \
|
||||
f"metricOf must be of type {MetricOfPerformance} for metricType:{MetricType.performance}"
|
||||
elif values.get("metric_type") == MetricType.resources:
|
||||
assert isinstance(values.get("metric_of"), MetricOfResources), \
|
||||
f"metricOf must be of type {MetricOfResources} for metricType:{MetricType.resources}"
|
||||
elif values.get("metric_type") == MetricType.web_vital:
|
||||
assert isinstance(values.get("metric_of"), MetricOfWebVitals), \
|
||||
f"metricOf must be of type {MetricOfWebVitals} for metricType:{MetricType.web_vital}"
|
||||
|
||||
assert isinstance(values.get("view_type"), MetricOtherViewType), \
|
||||
f"viewType must be 'chart' for metricOf:{values.get('metric_of')}"
|
||||
|
||||
return values
|
||||
|
||||
class Config:
|
||||
alias_generator = attribute_to_camel_case
|
||||
|
||||
|
||||
class CustomMetricsConfigSchema(BaseModel):
|
||||
col: Optional[int] = Field(...)
|
||||
row: Optional[int] = Field(default=2)
|
||||
position: Optional[int] = Field(default=0)
|
||||
|
||||
|
||||
class CreateCardSchema(TryCardSchema):
|
||||
name: str = Field(...)
|
||||
|
||||
|
||||
class CustomMetricUpdateSeriesSchema(CustomMetricCreateSeriesSchema):
|
||||
series_id: Optional[int] = Field(None)
|
||||
|
||||
|
|
@ -1025,7 +1109,7 @@ class LiveSessionSearchFilterSchema(BaseModel):
|
|||
type: LiveFilterType = Field(...)
|
||||
source: Optional[str] = Field(None)
|
||||
operator: Literal[SearchEventOperator._is.value,
|
||||
SearchEventOperator._contains.value] = Field(SearchEventOperator._contains.value)
|
||||
SearchEventOperator._contains.value] = Field(SearchEventOperator._contains.value)
|
||||
|
||||
@root_validator
|
||||
def validator(cls, values):
|
||||
|
|
|
|||
|
|
@ -20,16 +20,70 @@ CREATE TABLE IF NOT EXISTS assist_records
|
|||
|
||||
ALTER TYPE webhook_type ADD VALUE IF NOT EXISTS 'msteams';
|
||||
|
||||
DELETE
|
||||
FROM metrics
|
||||
WHERE is_predefined
|
||||
AND is_template
|
||||
AND metric_type = 'predefined';
|
||||
UPDATE metrics_clone
|
||||
SET metric_of=CASE
|
||||
WHEN metric_of = 'USEROS' THEN 'userOS'
|
||||
WHEN metric_of = 'USERBROWSER' THEN 'userBrowser'
|
||||
WHEN metric_of = 'USERDEVICE' THEN 'userDevice'
|
||||
WHEN metric_of = 'USERCOUNTRY' THEN 'userCountry'
|
||||
WHEN metric_of = 'USERID' THEN 'userId'
|
||||
WHEN metric_of = 'ISSUE' THEN 'issue'
|
||||
WHEN metric_of = 'LOCATION' THEN 'location'
|
||||
WHEN metric_of = 'SESSIONS' THEN 'sessions'
|
||||
WHEN metric_of = 'js_exception' THEN 'jsException'
|
||||
WHEN metric_of = 'sessionCount' THEN 'sessionCount'
|
||||
END
|
||||
WHERE NOT is_predefined;
|
||||
|
||||
-- TODO: transform metric_type to text
|
||||
-- TODO: drop metric_type enum
|
||||
-- TODO: drop is_pinned
|
||||
-- TODO: drop is_predefined
|
||||
-- TODO: drop is_template
|
||||
-- 1. pre transform structure
|
||||
ALTER TABLE IF EXISTS metrics_clone
|
||||
ALTER COLUMN metric_type TYPE text,
|
||||
ALTER COLUMN view_type TYPE text,
|
||||
ADD COLUMN IF NOT EXISTS o_metric_id INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS o_widget_id INTEGER;
|
||||
|
||||
-- 2. insert predefined metrics related to dashboards as custom metrics
|
||||
INSERT INTO metrics_clone(project_id, user_id, name, metric_type, view_type, metric_of, metric_value, metric_format,
|
||||
default_config, o_metric_id, o_widget_id)
|
||||
SELECT dashboards.project_id,
|
||||
dashboard_widgets.user_id,
|
||||
metrics_clone.name,
|
||||
left(category, 1) || right(replace(initcap(category), ' ', ''), -1) AS metric_type,
|
||||
'chart' AS view_type,
|
||||
left(predefined_key, 1) || right(replace(initcap(predefined_key), '_', ''), -1) AS metric_of,
|
||||
metric_value,
|
||||
metric_format,
|
||||
default_config,
|
||||
metrics_clone.metric_id,
|
||||
dashboard_widgets.widget_id
|
||||
FROM metrics_clone
|
||||
INNER JOIN dashboard_widgets USING (metric_id)
|
||||
INNER JOIN dashboards USING (dashboard_id)
|
||||
WHERE is_predefined;
|
||||
|
||||
-- 3. update widgets
|
||||
UPDATE dashboard_widgets
|
||||
SET metric_id=metrics_clone.metric_id
|
||||
FROM metrics_clone
|
||||
WHERE metrics_clone.o_widget_id IS NOT NULL
|
||||
AND dashboard_widgets.widget_id = metrics_clone.o_widget_id;
|
||||
|
||||
-- 4. delete predefined metrics
|
||||
DELETE
|
||||
FROM metrics_clone
|
||||
WHERE is_predefined;
|
||||
|
||||
ALTER TABLE IF EXISTS metrics_clone
|
||||
DROP COLUMN IF EXISTS active,
|
||||
DROP COLUMN IF EXISTS is_predefined,
|
||||
DROP COLUMN IF EXISTS is_template,
|
||||
DROP COLUMN IF EXISTS category,
|
||||
DROP COLUMN IF EXISTS o_metric_id,
|
||||
DROP COLUMN IF EXISTS o_widget_id,
|
||||
DROP CONSTRAINT IF EXISTS null_project_id_for_template_only,
|
||||
DROP CONSTRAINT IF EXISTS metrics_clone_unique_key;
|
||||
|
||||
DROP TYPE IF EXISTS metric_type;
|
||||
DROP TYPE IF EXISTS metric_view_type;
|
||||
|
||||
COMMIT;
|
||||
|
|
@ -10,4 +10,70 @@ FROM metrics
|
|||
WHERE is_predefined
|
||||
AND is_template;
|
||||
|
||||
UPDATE metrics_clone
|
||||
SET metric_of=CASE
|
||||
WHEN metric_of = 'USEROS' THEN 'userOS'
|
||||
WHEN metric_of = 'USERBROWSER' THEN 'userBrowser'
|
||||
WHEN metric_of = 'USERDEVICE' THEN 'userDevice'
|
||||
WHEN metric_of = 'USERCOUNTRY' THEN 'userCountry'
|
||||
WHEN metric_of = 'USERID' THEN 'userId'
|
||||
WHEN metric_of = 'ISSUE' THEN 'issue'
|
||||
WHEN metric_of = 'LOCATION' THEN 'location'
|
||||
WHEN metric_of = 'SESSIONS' THEN 'sessions'
|
||||
WHEN metric_of = 'js_exception' THEN 'jsException'
|
||||
WHEN metric_of = 'sessionCount' THEN 'sessionCount'
|
||||
END
|
||||
WHERE NOT is_predefined;
|
||||
|
||||
-- 1. pre transform structure
|
||||
ALTER TABLE IF EXISTS metrics_clone
|
||||
ALTER COLUMN metric_type TYPE text,
|
||||
ALTER COLUMN view_type TYPE text,
|
||||
ADD COLUMN IF NOT EXISTS o_metric_id INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS o_widget_id INTEGER;
|
||||
|
||||
-- 2. insert predefined metrics related to dashboards as custom metrics
|
||||
INSERT INTO metrics_clone(project_id, user_id, name, metric_type, view_type, metric_of, metric_value, metric_format,
|
||||
default_config, o_metric_id, o_widget_id)
|
||||
SELECT dashboards.project_id,
|
||||
dashboard_widgets.user_id,
|
||||
metrics_clone.name,
|
||||
left(category, 1) || right(replace(initcap(category), ' ', ''), -1) AS metric_type,
|
||||
'chart' AS view_type,
|
||||
left(predefined_key, 1) || right(replace(initcap(predefined_key), '_', ''), -1) AS metric_of,
|
||||
metric_value,
|
||||
metric_format,
|
||||
default_config,
|
||||
metrics_clone.metric_id,
|
||||
dashboard_widgets.widget_id
|
||||
FROM metrics_clone
|
||||
INNER JOIN dashboard_widgets USING (metric_id)
|
||||
INNER JOIN dashboards USING (dashboard_id)
|
||||
WHERE is_predefined;
|
||||
|
||||
-- 3. update widgets
|
||||
UPDATE dashboard_widgets
|
||||
SET metric_id=metrics_clone.metric_id
|
||||
FROM metrics_clone
|
||||
WHERE metrics_clone.o_widget_id IS NOT NULL
|
||||
AND dashboard_widgets.widget_id = metrics_clone.o_widget_id;
|
||||
|
||||
-- 4. delete predefined metrics
|
||||
DELETE
|
||||
FROM metrics_clone
|
||||
WHERE is_predefined;
|
||||
|
||||
ALTER TABLE IF EXISTS metrics_clone
|
||||
DROP COLUMN IF EXISTS active,
|
||||
DROP COLUMN IF EXISTS is_predefined,
|
||||
DROP COLUMN IF EXISTS is_template,
|
||||
DROP COLUMN IF EXISTS category,
|
||||
DROP COLUMN IF EXISTS o_metric_id,
|
||||
DROP COLUMN IF EXISTS o_widget_id,
|
||||
DROP CONSTRAINT IF EXISTS null_project_id_for_template_only,
|
||||
DROP CONSTRAINT IF EXISTS metrics_clone_unique_key;
|
||||
|
||||
DROP TYPE IF EXISTS metric_type;
|
||||
DROP TYPE IF EXISTS metric_view_type;
|
||||
|
||||
COMMIT;
|
||||
Loading…
Add table
Reference in a new issue