Fix charts
Signed-off-by: András Schmelczer <andras@schmelczer.dev>
This commit is contained in:
parent
4b92a75841
commit
5b92d22f75
5 changed files with 51 additions and 26 deletions
|
|
@ -1,9 +1,11 @@
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from fastapi import FastAPI, status
|
from fastapi import FastAPI, status
|
||||||
from fastapi.middleware.wsgi import WSGIMiddleware
|
from fastapi.middleware.wsgi import WSGIMiddleware
|
||||||
from fastapi.openapi.docs import get_swagger_ui_html
|
from fastapi.openapi.docs import get_swagger_ui_html
|
||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
from starlette.responses import HTMLResponse
|
from starlette.responses import HTMLResponse
|
||||||
|
|
||||||
from ..context import get_context
|
from ..context import get_context
|
||||||
|
|
@ -11,6 +13,8 @@ from ..helper import snake_case_to_text
|
||||||
from ..metrics import create_dash_app
|
from ..metrics import create_dash_app
|
||||||
from ..views import HealthCheckResponse, Query
|
from ..views import HealthCheckResponse, Query
|
||||||
|
|
||||||
|
PATH = Path(__file__).parent.resolve()
|
||||||
|
|
||||||
|
|
||||||
def create_fastapi_app(
|
def create_fastapi_app(
|
||||||
function_name: str, disable_docs: bool, disable_metrics: bool
|
function_name: str, disable_docs: bool, disable_metrics: bool
|
||||||
|
|
@ -40,6 +44,10 @@ def create_fastapi_app(
|
||||||
def redirect_to_entrypoint() -> RedirectResponse:
|
def redirect_to_entrypoint() -> RedirectResponse:
|
||||||
return RedirectResponse("/metrics")
|
return RedirectResponse("/metrics")
|
||||||
|
|
||||||
|
app.mount(
|
||||||
|
"/assets", StaticFiles(directory=PATH / "../metrics/assets"), name="static"
|
||||||
|
)
|
||||||
|
|
||||||
@app.post("/query", status_code=status.HTTP_200_OK)
|
@app.post("/query", status_code=status.HTTP_200_OK)
|
||||||
def query_metrics(query: Query) -> List[Dict[str, Any]]:
|
def query_metrics(query: Query) -> List[Dict[str, Any]]:
|
||||||
return get_context().persistence.query(
|
return get_context().persistence.query(
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ from typing import Any, Callable
|
||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI, status
|
from fastapi import FastAPI, status
|
||||||
|
from uvicorn.config import LOGGING_CONFIG
|
||||||
|
|
||||||
from ..tracing import TracingContext
|
from ..tracing import TracingContext
|
||||||
from ..views import Trace
|
from ..views import Trace
|
||||||
|
|
@ -32,8 +33,7 @@ def serve(
|
||||||
host="0.0.0.0",
|
host="0.0.0.0",
|
||||||
port=5050,
|
port=5050,
|
||||||
log_config={
|
log_config={
|
||||||
"version": 1,
|
**LOGGING_CONFIG,
|
||||||
"disable_existing_loggers": False,
|
|
||||||
"formatters": {
|
"formatters": {
|
||||||
"default": {
|
"default": {
|
||||||
"()": "good_ai.logger.CustomFormatter",
|
"()": "good_ai.logger.CustomFormatter",
|
||||||
|
|
@ -44,26 +44,5 @@ def serve(
|
||||||
"fmt": "%(asctime)s | %(levelname)8s | %(message)s", # noqa: E501
|
"fmt": "%(asctime)s | %(levelname)8s | %(message)s", # noqa: E501
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"handlers": {
|
|
||||||
"default": {
|
|
||||||
"formatter": "default",
|
|
||||||
"class": "logging.StreamHandler",
|
|
||||||
"stream": "ext://sys.stderr",
|
|
||||||
},
|
|
||||||
"access": {
|
|
||||||
"formatter": "access",
|
|
||||||
"class": "logging.StreamHandler",
|
|
||||||
"stream": "ext://sys.stdout",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"loggers": {
|
|
||||||
"uvicorn": {"handlers": ["default"], "level": "INFO"},
|
|
||||||
"uvicorn.error": {"level": "INFO"},
|
|
||||||
"uvicorn.access": {
|
|
||||||
"handlers": ["access"],
|
|
||||||
"level": "INFO",
|
|
||||||
"propagate": False,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
3
good_ai/src/good_ai/good_ai/metrics/assets/index.css
Normal file
3
good_ai/src/good_ai/good_ai/metrics/assets/index.css
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
body {
|
||||||
|
background-color: hotpink;
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,8 @@ from dash import Dash, dash_table, dcc, html
|
||||||
from dash.dependencies import Input, Output
|
from dash.dependencies import Input, Output
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
|
||||||
|
from good_ai.utilities.unique import unique
|
||||||
|
|
||||||
from ..context import get_context
|
from ..context import get_context
|
||||||
from ..helper import snake_case_to_text
|
from ..helper import snake_case_to_text
|
||||||
from ..views import SortBy
|
from ..views import SortBy
|
||||||
|
|
@ -15,10 +17,16 @@ from .get_filter_from_datatable import get_filter_from_datatable
|
||||||
|
|
||||||
|
|
||||||
def create_dash_app(function_name: str) -> Flask:
|
def create_dash_app(function_name: str) -> Flask:
|
||||||
|
flask_app = Flask(__name__)
|
||||||
app = Dash(
|
app = Dash(
|
||||||
function_name,
|
function_name,
|
||||||
requests_pathname_prefix=get_context().metrics_path + "/",
|
requests_pathname_prefix=get_context().metrics_path + "/",
|
||||||
|
server=flask_app,
|
||||||
title=snake_case_to_text(function_name),
|
title=snake_case_to_text(function_name),
|
||||||
|
update_title=None,
|
||||||
|
external_stylesheets=[
|
||||||
|
"/assets/index.css",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
documents = get_context().persistence.get_documents()
|
documents = get_context().persistence.get_documents()
|
||||||
|
|
@ -117,8 +125,34 @@ def create_dash_app(function_name: str) -> Flask:
|
||||||
)
|
)
|
||||||
|
|
||||||
df = pd.DataFrame(rows)
|
df = pd.DataFrame(rows)
|
||||||
return px.parallel_coordinates(
|
return go.Figure(
|
||||||
df, labels={c: snake_case_to_text(c) for c in df.columns}
|
go.Parcoords(
|
||||||
|
dimensions=[
|
||||||
|
get_dimension_descriptor(df, c)
|
||||||
|
for c in df.columns
|
||||||
|
if not c.startswith("arg:") and c not in {"id", "created"}
|
||||||
|
]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return app.server
|
return flask_app
|
||||||
|
|
||||||
|
|
||||||
|
def get_dimension_descriptor(df: pd.DataFrame, column: str) -> Dict[str, Any]:
|
||||||
|
dimension: Dict[str, Any] = {
|
||||||
|
"label": snake_case_to_text(column),
|
||||||
|
}
|
||||||
|
|
||||||
|
values = df[column]
|
||||||
|
|
||||||
|
try:
|
||||||
|
dimension["values"] = [float(v) for v in values]
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
unique_values = unique(values)
|
||||||
|
value_mapping = {str(v): i for i, v in enumerate(unique_values)}
|
||||||
|
|
||||||
|
dimension["values"] = [value_mapping[str(v)] for v in values]
|
||||||
|
dimension["tickvals"] = list(value_mapping.values())
|
||||||
|
dimension["ticktext"] = list(value_mapping.keys())
|
||||||
|
|
||||||
|
return dimension
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
from typing_extensions import TypedDict
|
from typing_extensions import TypedDict
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue