diff --git a/dashboard/__init__.py b/dashboard/__init__.py
deleted file mode 100644
index 95f0a75b..00000000
--- a/dashboard/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from __future__ import annotations
-
-from typing import * # type: ignore
-
-import rio
-
-from . import components as comps
-from . import pages
-
-app = rio.App(
- name="Rio",
- icon=rio.common.RIO_LOGO_ASSET_PATH,
- pages=[
- rio.Page(
- page_url="",
- build=pages.Dashboard,
- ),
- ],
- theme=rio.Theme.from_color(),
-)
diff --git a/dashboard/components/__init__.py b/dashboard/components/__init__.py
deleted file mode 100644
index c4290bf7..00000000
--- a/dashboard/components/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from .history import History
-from .sidebar import Sidebar
-from .stat_card import StatCard
-from .task_list import ProcessList
diff --git a/dashboard/components/history.py b/dashboard/components/history.py
deleted file mode 100644
index 40db415b..00000000
--- a/dashboard/components/history.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from __future__ import annotations
-
-import time
-from dataclasses import KW_ONLY, field
-from typing import * # type: ignore
-
-import plotly.graph_objects as go
-import psutil
-
-import rio
-
-from .. import components as comps
-
-
-class History(rio.Component):
- timestamps: List[float] = field(default_factory=list)
- values: List[float] = field(default_factory=list)
-
- @rio.event.periodic(0.5)
- async def on_populate(self) -> None:
- # Add the current time and CPU usage to the lists
- now = time.time()
- self.timestamps.append(now)
- self.values.append(psutil.cpu_percent())
-
- # Remove any old ones
- while self.timestamps[0] < now - 60:
- del self.timestamps[0]
- del self.values[0]
-
- await self.force_refresh()
-
- def build(self) -> rio.Component:
- figure = go.Figure(
- go.Scatter(
- x=self.timestamps,
- y=self.values,
- line={
- "color": self.session.theme.secondary_color.as_plotly,
- "width": 4,
- },
- fill="tozeroy",
- fillcolor=self.session.theme.secondary_color.replace(
- opacity=0.2
- ).as_plotly,
- ),
- )
-
- figure.update_layout(
- yaxis_range=[0, 100],
- xaxis_showticklabels=False,
- # margin={"t": 0, "r": 0, "b": 0, "l": 0},
- )
-
- return rio.Plot(
- figure,
- corner_radius=self.session.theme.corner_radius_medium,
- )
diff --git a/dashboard/components/sidebar.py b/dashboard/components/sidebar.py
deleted file mode 100644
index c95426fe..00000000
--- a/dashboard/components/sidebar.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from __future__ import annotations
-
-from typing import * # type: ignore
-
-import rio
-
-from .. import components as comps
-
-
-class Sidebar(rio.Component):
- def build(self) -> rio.Component:
- return rio.Card(
- rio.SwitcherBar(
- icons=[
- "material/castle",
- "material/archive",
- "material/settings",
- ],
- names=[
- "Dash",
- "Project",
- "Settings",
- ],
- values=[
- "dashboard",
- "projects",
- "settings",
- ],
- orientation="vertical",
- align_y=0,
- margin=0.4,
- ),
- corner_radius=0,
- )
diff --git a/dashboard/components/stat_card.py b/dashboard/components/stat_card.py
deleted file mode 100644
index d42376cc..00000000
--- a/dashboard/components/stat_card.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from __future__ import annotations
-
-from typing import * # type: ignore
-
-import rio
-
-from .. import components as comps
-
-
-class StatCard(rio.Component):
- icon: str
- color: rio.Color
-
- title: str
- current: str
- maximum: str | None = None
-
- on_press: rio.EventHandler[[]] = None
-
- @rio.event.periodic(1)
- async def on_populate(self) -> None:
- await self.force_refresh()
-
- def _on_press(self) -> None:
- pass
-
- def build(self) -> rio.Component:
- return rio.Card(
- rio.Column(
- rio.Row(
- rio.Icon(
- self.icon,
- width=3,
- height=3,
- align_y=0,
- ),
- rio.Column(
- rio.Text(
- self.title,
- style="heading3",
- align_x=1,
- margin_bottom=1,
- ),
- rio.Spacer(),
- rio.Text(
- self.current,
- align_x=1,
- ),
- (
- rio.Spacer(height=0)
- if self.maximum is None
- else rio.Text(
- f"of {self.maximum}",
- style="dim",
- align_x=1,
- )
- ),
- width="grow",
- ),
- height="grow",
- ),
- margin=1.5,
- ),
- color=self.color,
- on_press=self._on_press,
- )
diff --git a/dashboard/components/task_list.py b/dashboard/components/task_list.py
deleted file mode 100644
index 0942cd96..00000000
--- a/dashboard/components/task_list.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from __future__ import annotations
-
-from typing import * # type: ignore
-
-import psutil
-
-import rio
-
-from .. import components as comps
-
-
-class ProcessList(rio.Component):
- example_state: str = "For demonstration purposes"
-
- def build(self) -> rio.Component:
- entries = []
-
- # entries.append(
- # rio.SimpleListItem(
- # text="Task 1",
- # secondary_text="This is a description of task 1",
- # key="task1",
- # ),
- # )
-
- # entries.append(
- # rio.SimpleListItem(
- # text="Task 2",
- # secondary_text="This is a description of task 2",
- # key="task2",
- # ),
- # )
- processes = list(psutil.process_iter())
- processes.sort(key=lambda proc: proc.name())
- processes = processes[:10]
-
- for proc in processes:
- entries.append(
- rio.SimpleListItem(
- text=proc.name(),
- secondary_text=f"PID: {proc.pid}",
- key=str(proc.pid),
- ),
- )
-
- return rio.Card(
- rio.Column(
- rio.Text(
- "Tasks",
- style="heading2",
- margin=1,
- ),
- rio.ListView(
- *entries,
- ),
- ),
- # color="primary",
- )
diff --git a/dashboard/pages/__init__.py b/dashboard/pages/__init__.py
deleted file mode 100644
index 632302cb..00000000
--- a/dashboard/pages/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from .dashboard import Dashboard
diff --git a/dashboard/pages/dashboard.py b/dashboard/pages/dashboard.py
deleted file mode 100644
index 3f8d7efc..00000000
--- a/dashboard/pages/dashboard.py
+++ /dev/null
@@ -1,113 +0,0 @@
-from __future__ import annotations
-
-from typing import * # type: ignore
-
-import psutil
-
-import rio
-
-from .. import components as comps
-
-
-class Dashboard(rio.Component):
- banner_message: str | None = None
-
- def build(self) -> rio.Component:
- # Grids need more information about their children than most components.
- # It's often easier to create them first, then add children afterwards.
- grid = rio.Grid(
- width="grow",
- height="grow",
- row_spacing=2,
- column_spacing=2,
- margin=2,
- )
-
- # The banner will display a message at the top of the page.
- grid.add(
- rio.Banner(
- self.banner_message,
- style="info",
- ),
- row=0,
- column=0,
- width=4,
- )
-
- # CPU
- grid.add(
- comps.StatCard(
- icon="material/memory",
- # color=rio.Color.RED,
- color=self.session.theme.primary_color,
- title="Processor",
- current=f"{psutil.cpu_percent()}%",
- ),
- row=1,
- column=0,
- )
-
- # Memory
- mem = psutil.virtual_memory()
- grid.add(
- comps.StatCard(
- icon="material/apps",
- # color=rio.Color.GREEN,
- color=self.session.theme.primary_color,
- title="Memory",
- current=f"{mem.used / 2**30:.0f}GiB",
- maximum=f"{mem.total / 2 ** 30:.0f}GiB",
- ),
- row=1,
- column=1,
- )
-
- # Network
- net = psutil.net_io_counters()
- grid.add(
- comps.StatCard(
- icon="material/wifi",
- # color=rio.Color.BLUE,
- color=self.session.theme.primary_color,
- title="Network",
- current=f"{net.bytes_sent / 2**30:.0f}GiB",
- ),
- row=1,
- column=2,
- )
-
- # Storage
- disk = psutil.disk_usage("/")
- grid.add(
- comps.StatCard(
- icon="material/storage",
- # color=rio.Color.ORANGE,
- color=self.session.theme.primary_color,
- title="Storage",
- current=f"{disk.used / 2**30:.0f}GiB",
- maximum=f"{disk.total / 2**30:.0f}GiB",
- ),
- row=1,
- column=3,
- )
-
- grid.add(
- comps.History(
- height="grow",
- ),
- row=2,
- column=0,
- width=3,
- )
- grid.add(
- comps.ProcessList(
- align_y=0,
- ),
- row=2,
- column=3,
- )
-
- return rio.Row(
- comps.Sidebar(),
- grid,
- )
diff --git a/rio/snippets/snippet-files/project-template-Simple Dashboard/assets/smartphone-sales-clean.csv b/rio/snippets/snippet-files/project-template-Simple Dashboard/assets/smartphones.csv
similarity index 100%
rename from rio/snippets/snippet-files/project-template-Simple Dashboard/assets/smartphone-sales-clean.csv
rename to rio/snippets/snippet-files/project-template-Simple Dashboard/assets/smartphones.csv
diff --git a/rio/snippets/snippet-files/project-template-Simple Dashboard/meta.json b/rio/snippets/snippet-files/project-template-Simple Dashboard/meta.json
index dbafd294..9a6b59d4 100644
--- a/rio/snippets/snippet-files/project-template-Simple Dashboard/meta.json
+++ b/rio/snippets/snippet-files/project-template-Simple Dashboard/meta.json
@@ -1,4 +1,8 @@
{
"level": "beginner",
- "summary": "A simple dashboard, featuring an interactive graph, controls and CSV download"
+ "summary": "A simple dashboard, featuring an interactive graph, controls and CSV download",
+ "dependencies": {
+ "plotly": ">=5.20.0",
+ "pandas": ">=2.2.1"
+ }
}
\ No newline at end of file
diff --git a/rio/snippets/snippet-files/project-template-Simple Dashboard/pages/interactive_plot.py b/rio/snippets/snippet-files/project-template-Simple Dashboard/pages/interactive_plot.py
index 3e5b2b70..5934e9bb 100644
--- a/rio/snippets/snippet-files/project-template-Simple Dashboard/pages/interactive_plot.py
+++ b/rio/snippets/snippet-files/project-template-Simple Dashboard/pages/interactive_plot.py
@@ -1,33 +1,40 @@
-import urllib.request
-from pathlib import Path
-
-import pandas as pd
-import plotly.graph_objs as go
+from dataclasses import field
import rio
-# Load the dataset
-CSV_PATH = Path(__file__).parent / "smartphone-sales.csv"
-CSV_URL = "https://TODO-real-url.com"
+#
+import pandas as pd
+import plotly.graph_objs as go
-if not CSV_PATH.exists():
- with urllib.request.urlopen(CSV_URL) as response:
- CSV_PATH.write_bytes(response.read())
-
-raw_df = pd.read_csv(CSV_PATH)
-raw_df = raw_df.head(1000)
+#
#
class InteractivePlot(rio.Component):
- x_axis: str = raw_df.columns[0]
- y_axis: str = raw_df.columns[1]
+ # The full dataset, containing all smartphone data. This will be loaded when
+ # the component is initialized. See the `load_data` method for details.
+ dataset: pd.DataFrame = field(default_factory=pd.DataFrame)
+ # The currently selected columns to display in the scatterplot. These values
+ # will be initializes when the dataset is loaded.
+ x_axis: str = ""
+ y_axis: str = ""
+
+ # If this is `True`, we'll take care to remove unlikely data by keeping only
+ # the tenth to ninetieth percentile of the dataset.
remove_outliers: bool = False
+ @rio.event.on_populate
+ def load_data(self) -> None:
+ self.dataset = pd.read_csv(
+ self.session.assets / "smartphones.csv",
+ )
+ self.x_axis = self.dataset.columns[0]
+ self.y_axis = self.dataset.columns[1]
+
async def on_download_csv(self) -> None:
# Build a CSV file from the selected columns
- selected_df = raw_df[[self.x_axis, self.y_axis]]
+ selected_df = self.dataset[[self.x_axis, self.y_axis]]
csv = selected_df.to_csv(index=False)
# Save the file
@@ -41,7 +48,7 @@ class InteractivePlot(rio.Component):
Remove outliers from the dataset, by keeping only the tenth to ninetieth
percentile.
"""
- result = raw_df
+ result = self.dataset
# Remove outliers in the x-axis
series = result[self.x_axis]
@@ -68,7 +75,7 @@ class InteractivePlot(rio.Component):
if self.remove_outliers:
df = self.filter_data()
else:
- df = raw_df
+ df = self.dataset
# Build the plot
return rio.Row(