Skip to content

feat: landing screen for projects #534

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cd672d8
feat: landing screen for projects
cabreraalex Dec 7, 2023
f3fcd19
go to explore if no metrics or models
Sparkier Dec 8, 2023
92275f9
add element database functions
Sparkier Dec 8, 2023
19ab8cd
functionality to add default project elements if they are not set
Sparkier Dec 8, 2023
10b292c
charts
cabreraalex Dec 8, 2023
804c069
updates
cabreraalex Dec 9, 2023
00932dc
updates
Sparkier Dec 11, 2023
e0f4be5
prevent delete charts
Sparkier Dec 11, 2023
25291f5
tests
Sparkier Dec 12, 2023
a4d8ca1
small changes
cabreraalex Dec 14, 2023
b25c0d9
styles
cabreraalex Dec 14, 2023
ae8c16c
prettier
cabreraalex Dec 14, 2023
db686ce
back to %
Sparkier Dec 14, 2023
e4b3c8e
min-height
Sparkier Dec 14, 2023
98c6375
radar sizing
Sparkier Dec 14, 2023
9d70937
test
Sparkier Dec 14, 2023
86bb570
sort instances
Sparkier Dec 14, 2023
6248966
fix radar legend
Sparkier Dec 14, 2023
10cf1d6
tests
Sparkier Dec 14, 2023
621cbf2
Merge remote-tracking branch 'origin/main' into ac-project-home
Sparkier Dec 20, 2023
851b459
fix table issue
Sparkier Dec 20, 2023
c94670e
update download button position and behavior
Sparkier Dec 20, 2023
3a9c9e4
Merge remote-tracking branch 'origin/main' into ac-project-home
cabreraalex Dec 22, 2023
2716d39
change to x and y
cabreraalex Dec 22, 2023
1f39943
Merge remote-tracking branch 'origin/ac-project-home' into ac-project…
Sparkier Jan 11, 2024
fa04305
Merge remote-tracking branch 'origin/main' into ac-project-home
Sparkier Jan 11, 2024
e766b7a
prettier
Sparkier Jan 11, 2024
159ed48
check
Sparkier Jan 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions backend/zeno_backend/classes/project.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Types for Zeno projects."""
from enum import Enum

from zeno_backend.classes.base import CamelModel, ZenoColumn
from zeno_backend.classes.folder import Folder
Expand Down Expand Up @@ -38,6 +39,39 @@ class Project(CamelModel):
updated_at: str = ""


class ProjectHomeElementType(Enum):
"""Enumeration of possible elements on a project's home in Zeno.

Attributes:
CHART: chart element for a report.
"""

CHART = "CHART"
LIST = "LIST"


class ProjectHomeElement(CamelModel):
"""Representation of an element on a project's home in Zeno.

Attributes:
id (int): ID of the project home element.
type (ReportElementType): what type of element this represents.
data (str | None): any data that the element holds.
x (int): x position of the element.
y (int): y position of the element.
width (int): width of the element.
height (int): height of the element.
"""

id: int | None = None
type: ProjectHomeElementType
data: str | None = None
x: int
y: int
width: int
height: int


class ProjectStats(CamelModel):
"""Statistics for projects.

Expand Down
2 changes: 2 additions & 0 deletions backend/zeno_backend/classes/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ class ReportElement(CamelModel):
"""Representation of an element of a Zeno report.

Attributes:
id (int): ID of the report element.
type (ReportElementType): what type of element this represents.
position (int): position of the element in the report.
data (str | None): any data that the element holds.
"""

Expand Down
33 changes: 32 additions & 1 deletion backend/zeno_backend/database/insert.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
Operation,
PredicatesEncoder,
)
from zeno_backend.classes.project import Project
from zeno_backend.classes.project import Project, ProjectHomeElement
from zeno_backend.classes.report import ReportElement
from zeno_backend.classes.slice import Slice
from zeno_backend.classes.tag import Tag
Expand Down Expand Up @@ -121,6 +121,37 @@ async def project(project_config: Project, owner_id: int):
await conn.commit()


async def project_home_element(project_uuid: str, element: ProjectHomeElement):
"""Add a project home element to the database.

Args:
project_uuid (str): the uuid of the project to add the element to.
element (ProjectHomeElement): the element to add to the project.

Returns:
int | None: the id of the newly created report element.
"""
async with db_pool.connection() as conn:
async with conn.cursor() as cur:
await cur.execute(
"INSERT INTO project_home_elements (project_uuid, type, data, x, "
"y, width, height)"
" VALUES (%s,%s,%s,%s,%s,%s,%s) RETURNING id;",
[
project_uuid,
element.type,
element.data,
element.x,
element.y,
element.width,
element.height,
],
)
id = await cur.fetchall()
if len(id) > 0:
return id[0][0]


async def dataset_schema(
project_uuid: str,
id_column: str,
Expand Down
72 changes: 68 additions & 4 deletions backend/zeno_backend/database/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from zeno_backend.classes.metric import Metric
from zeno_backend.classes.project import (
Project,
ProjectHomeElement,
ProjectHomeElementType,
ProjectState,
ProjectStats,
)
Expand Down Expand Up @@ -859,6 +861,29 @@ async def charts_for_projects(project_uuids: list[str]) -> list[Chart]:
)


async def project_home_chart_ids(project_uuid: str) -> list[int]:
"""Get all chart IDs for a project's home view.

Args:
project_uuid (str): the UUID of the project.

Returns:
list[int]: the list of chart IDs.
"""
async with db_pool.connection() as conn:
async with conn.cursor() as cur:
await cur.execute(
"SELECT data FROM project_home_elements WHERE project_uuid = %s "
"AND type = %s;",
[project_uuid, ProjectHomeElementType.CHART],
)
chart_results = await cur.fetchall()
if len(chart_results) == 0:
return []

return [r[0] for r in chart_results]


async def tags_for_projects(project_uuids: list[str]) -> list[Tag]:
"""Get all tags for a list of projects.

Expand Down Expand Up @@ -986,6 +1011,38 @@ async def project_from_uuid(project_uuid: str) -> Project:
)


async def project_home_elements(project_uuid: str) -> list[ProjectHomeElement]:
"""Get the project data given a UUID.

Args:
project_uuid (str): the uuid of the project for which to fetch home elements.

Returns:
list[ProjectHomeElement]: Elements of the project's home view.
"""
async with db_pool.connection() as conn:
async with conn.cursor() as cur:
await cur.execute(
"SELECT id, type, data, x, y, width, height "
"FROM project_home_elements WHERE project_uuid = %s;",
[project_uuid],
)
project_home_elements_result = await cur.fetchall()

return [
ProjectHomeElement(
id=element[0],
type=element[1],
data=element[2],
x=element[3],
y=element[4],
width=element[5],
height=element[6],
)
for element in project_home_elements_result
]


async def report_from_id(report_id: int) -> Report:
"""Get the report data given a ID.

Expand Down Expand Up @@ -1894,10 +1951,17 @@ async def table_data_paginated(
if sort == "":
sort = "diff"

order_sql = sql.SQL("ORDER BY {} {}").format(
sql.Identifier(sort),
sql.SQL("DESC" if req.sort[1] else "ASC"),
)
if req.sort[0].data_type == MetadataType.NOMINAL:
order_sql = sql.SQL("ORDER BY {} COLLATE numeric {}").format(
sql.Identifier(sort),
sql.SQL("ASC" if req.sort[1] else "DESC"),
)
else:
order_sql = sql.SQL("ORDER BY {} {}").format(
sql.Identifier(sort),
sql.SQL("ASC" if req.sort[1] else "DESC"),
)

else:
await cur.execute(
sql.SQL("SELECT column_id FROM {} WHERE type = 'ID';").format(
Expand Down
185 changes: 185 additions & 0 deletions backend/zeno_backend/processing/project_home.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
"""Functions around a project's home elements."""
import math

from fastapi import HTTPException, status

import zeno_backend.database.insert as insert
import zeno_backend.database.select as select
from zeno_backend.classes.chart import (
Chart,
ChartType,
RadarParameters,
SlicesMetricsOrModels,
SlicesOrModels,
TableParameters,
XCParameters,
)
from zeno_backend.classes.metric import Metric
from zeno_backend.classes.project import ProjectHomeElement, ProjectHomeElementType


async def create_project_home(project_uuid) -> list[ProjectHomeElement]:
"""Create the data for a project's home page.

Args:
project_uuid (str): the uuid of the project for which to create home elements.

Returns:
list[ProjectHomeElement]: list of home elements of the project.
"""
metrics = await select.metrics(project_uuid)
models = await select.models(project_uuid)

if len(metrics) == 0 or len(models) == 0:
return []

await create_overview_table(project_uuid)
await create_overview_charts(project_uuid, metrics)
await insert.project_home_element(
project_uuid,
ProjectHomeElement(
id=-1,
type=ProjectHomeElementType.LIST,
x=30,
y=0,
width=70,
height=100,
),
)

elements = await select.project_home_elements(project_uuid)

if elements is None:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Could not load project home elements.",
)

return elements


async def create_overview_table(project_uuid: str):
"""Create an overview table of all models for all metrics.

Args:
project_uuid (str): the uuid of the project to create the table for.
"""
table_id = await insert.chart(
project_uuid,
Chart(
id=-1,
name="Model Performance (Table)",
project_uuid=project_uuid,
type=ChartType.TABLE,
parameters=TableParameters(
metrics=[-2],
slices=[-1],
models=[""],
y_channel=SlicesOrModels.MODELS,
x_channel=SlicesMetricsOrModels.METRICS,
fixed_channel=SlicesMetricsOrModels.SLICES,
),
),
)

if table_id is None:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Could not add overview table to the project.",
)

await insert.project_home_element(
project_uuid,
ProjectHomeElement(
id=-1,
type=ProjectHomeElementType.CHART,
data=str(table_id),
x=0,
y=0,
width=30,
height=50,
),
)


async def create_overview_charts(project_uuid: str, metrics: list[Metric]):
"""Create an overview chart of all models for all metrics.

Args:
project_uuid (str): the uuid of the project to create the chart for.
metrics (list[Metric]): the metrics that can be used in the chart.
"""
if len(metrics) > 2:
chart_id = await insert.chart(
project_uuid,
Chart(
id=-1,
name="Model Performance (Radar)",
project_uuid=project_uuid,
type=ChartType.RADAR,
parameters=RadarParameters(
metrics=[-2],
slices=[-1],
models=[""],
axis_channel=SlicesMetricsOrModels.METRICS,
layer_channel=SlicesOrModels.MODELS,
fixed_channel=SlicesMetricsOrModels.SLICES,
),
),
)

if chart_id is None:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Could not add overview table to the project.",
)

await insert.project_home_element(
project_uuid,
ProjectHomeElement(
id=-1,
type=ProjectHomeElementType.CHART,
data=str(chart_id),
x=0,
y=50,
width=30,
height=50,
),
)
else:
for metric_index, metric in enumerate(metrics):
chart_id = await insert.chart(
project_uuid,
Chart(
id=-1,
name=metric.name,
project_uuid=project_uuid,
type=ChartType.BAR,
parameters=XCParameters(
slices=[-1],
metric=metric.id,
models=[""],
color_channel=SlicesOrModels.MODELS,
x_channel=SlicesOrModels.SLICES,
),
),
)

if chart_id is None:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Could not add overview table to the project.",
)

await insert.project_home_element(
project_uuid,
ProjectHomeElement(
id=-1,
type=ProjectHomeElementType.CHART,
data=str(chart_id),
x=0,
y=50 + (metric_index * math.ceil(50 / len(metrics))),
width=30,
height=math.floor(50 / len(metrics)),
),
)
Loading