feat(api): dashboard 2/5

This commit is contained in:
Taha Yassine Kraiem 2022-03-25 20:18:31 +01:00
parent 9e3a5004f0
commit cc08060e30
8 changed files with 217 additions and 117 deletions

View file

@ -12,7 +12,7 @@ from routers import core, core_dynamic
from routers.app import v1_api from routers.app import v1_api
from routers.crons import core_crons from routers.crons import core_crons
from routers.crons import core_dynamic_crons from routers.crons import core_dynamic_crons
from routers.subs import dashboard, insights from routers.subs import dashboard, insights, metrics
app = FastAPI() app = FastAPI()
@ -54,6 +54,7 @@ app.include_router(core_dynamic.public_app)
app.include_router(core_dynamic.app) app.include_router(core_dynamic.app)
app.include_router(core_dynamic.app_apikey) app.include_router(core_dynamic.app_apikey)
app.include_router(dashboard.app) app.include_router(dashboard.app)
app.include_router(metrics.app)
app.include_router(insights.app) app.include_router(insights.app)
app.include_router(v1_api.app_apikey) app.include_router(v1_api.app_apikey)

View file

@ -1,3 +1,5 @@
import json
import schemas import schemas
from chalicelib.utils import helper from chalicelib.utils import helper
from chalicelib.utils import pg_client from chalicelib.utils import pg_client
@ -29,32 +31,59 @@ def get_dashboards(project_id, user_id):
def get_dashboard(project_id, user_id, dashboard_id): def get_dashboard(project_id, user_id, dashboard_id):
with pg_client.PostgresClient() as cur: with pg_client.PostgresClient() as cur:
pg_query = """SELECT dashboards.*, all_widgets.* pg_query = """SELECT dashboards.*, all_template_widgets.widgets AS template_widgets, all_metric_widgets.widgets AS metric_widgets
FROM dashboards FROM dashboards
LEFT JOIN LATERAL (SELECT COALESCE(ARRAY_AGG(widgets), '{}') AS widgets LEFT JOIN LATERAL (SELECT COALESCE(JSONB_AGG(templates), '[]'::jsonb) AS widgets
FROM widgets FROM templates
INNER JOIN dashboard_widgets USING (widget_id) INNER JOIN dashboard_widgets USING (template_id)
WHERE dashboard_widgets.dashboard_id = dashboards.dashboard_id WHERE dashboard_widgets.dashboard_id = dashboards.dashboard_id
AND widgets.deleted_at ISNULL ) AS all_template_widgets ON (TRUE)
AND (widgets.project_id ISNULL OR widgets.project_id = %(projectId)s) LEFT JOIN LATERAL (SELECT COALESCE(JSONB_AGG(raw_metrics), '[]') AS widgets
) AS all_widgets ON (TRUE) FROM (SELECT metrics.*, metric_series.series
FROM metrics
INNER JOIN dashboard_widgets USING (metric_id)
LEFT JOIN LATERAL (SELECT JSONB_AGG(metric_series.* ORDER BY index) AS series
FROM metric_series
WHERE metric_series.metric_id = metrics.metric_id
AND metric_series.deleted_at ISNULL
) AS metric_series ON (TRUE)
WHERE dashboard_widgets.dashboard_id = dashboards.dashboard_id
AND metrics.deleted_at ISNULL
AND metrics.project_id = %(projectId)s) AS raw_metrics
) AS all_metric_widgets ON (TRUE)
WHERE dashboards.deleted_at ISNULL WHERE dashboards.deleted_at ISNULL
AND dashboards.project_id = %(projectId)s AND dashboards.project_id = %(projectId)s
AND dashboard_id = %(dashboard_id)s AND dashboard_id = %(dashboard_id)s
AND (dashboards.user_id = %(userId)s OR is_public);""" AND (dashboards.user_id = %(userId)s OR is_public);"""
params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id} params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id}
print(cur.mogrify(pg_query, params))
cur.execute(cur.mogrify(pg_query, params))
row = cur.fetchone()
row["widgets"] = row.pop("template_widgets") + row.pop("metric_widgets")
return helper.dict_to_camel_case(row)
def add_widget(project_id, user_id, dashboard_id, data: schemas.AddWidgetToDashboardPayloadSchema):
ref_key = "metric_id"
if data.template_id is not None:
ref_key = "template_id"
with pg_client.PostgresClient() as cur:
pg_query = f"""INSERT INTO dashboard_widgets(dashboard_id, {ref_key}, user_id, configuration, name)
VALUES (%(dashboard_id)s, %({ref_key})s, %(userId)s, %(configuration)s::jsonb, %(name)s)
RETURNING *;"""
params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, **data.dict()}
params["configuration"] = json.dumps(params.get("configuration", {}))
cur.execute(cur.mogrify(pg_query, params)) cur.execute(cur.mogrify(pg_query, params))
row = cur.fetchone() row = cur.fetchone()
return helper.dict_to_camel_case(row) return helper.dict_to_camel_case(row)
# def get_widgets(project_id):
def get_widgets(project_id): # with pg_client.PostgresClient() as cur:
with pg_client.PostgresClient() as cur: # pg_query = f"""SELECT *
pg_query = f"""SELECT * # FROM widgets
FROM widgets # WHERE deleted_at ISNULL
WHERE deleted_at ISNULL # AND project_id = %(projectId)s;"""
AND project_id = %(projectId)s;""" # params = {"projectId": project_id}
params = {"projectId": project_id} # cur.execute(cur.mogrify(pg_query, params))
cur.execute(cur.mogrify(pg_query, params)) # rows = cur.fetchall()
rows = cur.fetchall() # return helper.list_to_camel_case(rows)
return helper.list_to_camel_case(rows)

View file

@ -0,0 +1,19 @@
from chalicelib.utils import helper
from chalicelib.utils import pg_client
CATEGORY_DESCRIPTION = {
'categ1': 'lorem',
}
def get_templates():
with pg_client.PostgresClient() as cur:
pg_query = f"""SELECT category, jsonb_agg(templates ORDER BY name) AS widgets
FROM templates
GROUP BY category
ORDER BY category;"""
cur.execute(pg_query)
rows = cur.fetchall()
for r in rows:
r["description"] = CATEGORY_DESCRIPTION.get(r["category"], "")
return helper.list_to_camel_case(rows)

View file

@ -8,7 +8,7 @@ jira==2.0.0
fastapi==0.74.1 fastapi==0.75.0
uvicorn[standard]==0.17.5 uvicorn[standard]==0.17.5
python-decouple==3.6 python-decouple==3.6
pydantic[email]==1.8.2 pydantic[email]==1.8.2

View file

@ -1065,78 +1065,6 @@ def change_client_password(data: schemas.EditUserPasswordSchema = Body(...),
user_id=context.user_id) user_id=context.user_id)
@app.post('/{projectId}/custom_metrics/try', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics/try', tags=["customMetrics"])
def try_custom_metric(projectId: int, data: schemas.CreateCustomMetricsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.merged_live(project_id=projectId, data=data)}
@app.post('/{projectId}/custom_metrics', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics', tags=["customMetrics"])
def add_custom_metric(projectId: int, data: schemas.CreateCustomMetricsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return custom_metrics.create(project_id=projectId, user_id=context.user_id, data=data)
@app.get('/{projectId}/custom_metrics', tags=["customMetrics"])
def get_custom_metrics(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.get_all(project_id=projectId, user_id=context.user_id)}
@app.get('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
def get_custom_metric(projectId: int, metric_id: int, context: schemas.CurrentContext = Depends(OR_context)):
data = custom_metrics.get(project_id=projectId, user_id=context.user_id, metric_id=metric_id)
if data is None:
return {"errors": ["custom metric not found"]}
return {"data": data}
@app.post('/{projectId}/custom_metrics/{metric_id}/sessions', tags=["customMetrics"])
def get_custom_metric_sessions(projectId: int, metric_id: int,
data: schemas.CustomMetricSessionsPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = custom_metrics.get_sessions(project_id=projectId, user_id=context.user_id, metric_id=metric_id, data=data)
if data is None:
return {"errors": ["custom metric not found"]}
return {"data": data}
@app.post('/{projectId}/custom_metrics/{metric_id}/chart', tags=["customMetrics"])
def get_custom_metric_chart(projectId: int, metric_id: int, data: schemas.CustomMetricChartPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = custom_metrics.make_chart(project_id=projectId, user_id=context.user_id, metric_id=metric_id,
data=data)
if data is None:
return {"errors": ["custom metric not found"]}
return {"data": data}
@app.post('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
def update_custom_metric(projectId: int, metric_id: int, data: schemas.UpdateCustomMetricsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = custom_metrics.update(project_id=projectId, user_id=context.user_id, metric_id=metric_id, data=data)
if data is None:
return {"errors": ["custom metric not found"]}
return {"data": data}
@app.post('/{projectId}/custom_metrics/{metric_id}/status', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics/{metric_id}/status', tags=["customMetrics"])
def update_custom_metric_state(projectId: int, metric_id: int,
data: schemas.UpdateCustomMetricsStatusSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {
"data": custom_metrics.change_state(project_id=projectId, user_id=context.user_id, metric_id=metric_id,
status=data.active)}
@app.delete('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
def delete_custom_metric(projectId: int, metric_id: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.delete(project_id=projectId, user_id=context.user_id, metric_id=metric_id)}
@app.post('/{projectId}/saved_search', tags=["savedSearch"]) @app.post('/{projectId}/saved_search', tags=["savedSearch"])
@app.put('/{projectId}/saved_search', tags=["savedSearch"]) @app.put('/{projectId}/saved_search', tags=["savedSearch"])
def add_saved_search(projectId: int, data: schemas.SavedSearchSchema = Body(...), def add_saved_search(projectId: int, data: schemas.SavedSearchSchema = Body(...),

View file

@ -1,10 +1,9 @@
from fastapi import Body, Depends from fastapi import Body
import schemas import schemas
from chalicelib.core import dashboard, dashboards2 from chalicelib.core import dashboard
from chalicelib.core import metadata from chalicelib.core import metadata
from chalicelib.utils import helper from chalicelib.utils import helper
from or_dependencies import OR_context
from routers.base import get_routers from routers.base import get_routers
public_app, app, app_apikey = get_routers() public_app, app, app_apikey = get_routers()
@ -345,25 +344,3 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body
*helper.explode_widget(dashboard.get_avg_cpu(project_id=projectId, **data.dict())), *helper.explode_widget(dashboard.get_avg_cpu(project_id=projectId, **data.dict())),
*helper.explode_widget(dashboard.get_avg_fps(project_id=projectId, **data.dict())), *helper.explode_widget(dashboard.get_avg_fps(project_id=projectId, **data.dict())),
]} ]}
@app.post('/{projectId}/dashboards', tags=["dashboard", "metrics"])
@app.put('/{projectId}/dashboards', tags=["dashboard", "metrics"])
def create_dashboards(projectId: int, data: schemas.CreateDashboardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.create_dashboard(project_id=projectId, user_id=context.user_id, data=data)}
@app.get('/{projectId}/dashboards', tags=["dashboard", "metrics"])
def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.get_dashboards(project_id=projectId, user_id=context.user_id)}
@app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard", "metrics"])
def get_dashboards(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)}
@app.get('/{projectId}/widgets', tags=["dashboard", "metrics"])
def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.get_widgets(project_id=projectId)}

129
api/routers/subs/metrics.py Normal file
View file

@ -0,0 +1,129 @@
from fastapi import Body, Depends
import schemas
from chalicelib.core import dashboards2, templates, custom_metrics
from or_dependencies import OR_context
from routers.base import get_routers
public_app, app, app_apikey = get_routers()
@app.post('/{projectId}/dashboards', tags=["dashboard", "metrics"])
@app.put('/{projectId}/dashboards', tags=["dashboard", "metrics"])
def create_dashboards(projectId: int, data: schemas.CreateDashboardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.create_dashboard(project_id=projectId, user_id=context.user_id, data=data)}
@app.get('/{projectId}/dashboards', tags=["dashboard", "metrics"])
def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.get_dashboards(project_id=projectId, user_id=context.user_id)}
@app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard", "metrics"])
def get_dashboards(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)}
@app.post('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard", "metrics"])
@app.put('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard", "metrics"])
def add_widget_to_dashboards(projectId: int, dashboardId: int,
data: schemas.AddWidgetToDashboardPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId,
data=data)}
# @app.get('/{projectId}/widgets', tags=["dashboard", "metrics"])
# def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
# return {"data": dashboards2.get_widgets(project_id=projectId)}
@app.get('/{projectId}/metrics/templates', tags=["dashboard", "metrics"])
def get_templates(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": templates.get_templates()}
@app.post('/{projectId}/metrics/try', tags=["dashboard"])
@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.CreateCustomMetricsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.merged_live(project_id=projectId, data=data)}
@app.post('/{projectId}/metrics', tags=["dashboard"])
@app.put('/{projectId}/metrics', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics', tags=["customMetrics"])
def add_custom_metric(projectId: int, data: schemas.CreateCustomMetricsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return custom_metrics.create(project_id=projectId, user_id=context.user_id, data=data)
@app.get('/{projectId}/metrics', tags=["dashboard"])
@app.get('/{projectId}/custom_metrics', tags=["customMetrics"])
def get_custom_metrics(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.get_all(project_id=projectId, user_id=context.user_id)}
@app.get('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
@app.get('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
def get_custom_metric(projectId: int, metric_id: int, context: schemas.CurrentContext = Depends(OR_context)):
data = custom_metrics.get(project_id=projectId, user_id=context.user_id, metric_id=metric_id)
if data is None:
return {"errors": ["custom metric not found"]}
return {"data": data}
@app.post('/{projectId}/metrics/{metric_id}/sessions', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/sessions', tags=["customMetrics"])
def get_custom_metric_sessions(projectId: int, metric_id: int,
data: schemas.CustomMetricSessionsPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = custom_metrics.get_sessions(project_id=projectId, user_id=context.user_id, metric_id=metric_id, data=data)
if data is None:
return {"errors": ["custom metric not found"]}
return {"data": data}
@app.post('/{projectId}/metrics/{metric_id}/chart', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/chart', tags=["customMetrics"])
def get_custom_metric_chart(projectId: int, metric_id: int, data: schemas.CustomMetricChartPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = custom_metrics.make_chart(project_id=projectId, user_id=context.user_id, metric_id=metric_id,
data=data)
if data is None:
return {"errors": ["custom metric not found"]}
return {"data": data}
@app.post('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
@app.put('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
def update_custom_metric(projectId: int, metric_id: int, data: schemas.UpdateCustomMetricsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = custom_metrics.update(project_id=projectId, user_id=context.user_id, metric_id=metric_id, data=data)
if data is None:
return {"errors": ["custom metric not found"]}
return {"data": data}
@app.post('/{projectId}/metrics/{metric_id}/status', tags=["dashboard"])
@app.put('/{projectId}/metrics/{metric_id}/status', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/status', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics/{metric_id}/status', tags=["customMetrics"])
def update_custom_metric_state(projectId: int, metric_id: int,
data: schemas.UpdateCustomMetricsStatusSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {
"data": custom_metrics.change_state(project_id=projectId, user_id=context.user_id, metric_id=metric_id,
status=data.active)}
@app.delete('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
@app.delete('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
def delete_custom_metric(projectId: int, metric_id: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.delete(project_id=projectId, user_id=context.user_id, metric_id=metric_id)}

View file

@ -884,3 +884,20 @@ class CreateDashboardSchema(BaseModel):
class Config: class Config:
alias_generator = attribute_to_camel_case alias_generator = attribute_to_camel_case
class AddWidgetToDashboardPayloadSchema(BaseModel):
template_id: Optional[int] = Field(default=None)
metric_id: Optional[int] = Field(default=None)
name: Optional[str] = Field(default=None)
configuration: dict = Field(default={})
@root_validator
def validator(cls, values):
assert bool(values.get("template_id") is not None) != bool(values.get("metric_id") is not None), \
f"templateId or metricId should be provided, but not both at the same time"
return values
class Config:
alias_generator = attribute_to_camel_case