diff --git a/docs/tutorials/articles/sales_dashboard/images/final_app.png b/docs/tutorials/articles/sales_dashboard/images/final_app.png new file mode 100644 index 000000000..536c586a6 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/images/final_app.png differ diff --git a/docs/tutorials/articles/sales_dashboard/images/thumbnail.png b/docs/tutorials/articles/sales_dashboard/images/thumbnail.png new file mode 100644 index 000000000..d6ca7502e Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/images/thumbnail.png differ diff --git a/docs/tutorials/articles/sales_dashboard/images/yt-thumbnail.png b/docs/tutorials/articles/sales_dashboard/images/yt-thumbnail.png new file mode 100644 index 000000000..e17c0778a Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/images/yt-thumbnail.png differ diff --git a/docs/tutorials/articles/sales_dashboard/index.md b/docs/tutorials/articles/sales_dashboard/index.md new file mode 100644 index 000000000..a86f4834b --- /dev/null +++ b/docs/tutorials/articles/sales_dashboard/index.md @@ -0,0 +1,47 @@ +--- +title: Creating a Sales Dashboard +category: fundamentals +data-keywords: gui vizelement chart navbar table layout part menu state multi-page callback +short-description: Understand basic knowledge of Taipy by creating a multi-page sales dashboard. +order: 1.5 +img: sales_dashboard/images/thumbnail.png +--- + +!!! note "Supported Python versions" + Taipy requires **Python 3.9** or newer. + +This tutorial focuses on creating a simple sales dashboard application. You'll learn about visual elements, +interaction, styling, and multi-page applications. + +![Final Application](images/final_app.png){width=90% .tp-image-border} + +This tutorial is also available in video format: + +

+ + Youtube Tutorial + +

+ +### Installation + +Ensure you have Python 3.9 or newer, then install Taipy and Plotly: + +```bash +pip install taipy plotly +``` + +!!! info + Use `pip install taipy` for the latest stable version. Need help with pip? Check out + the [installation guide](http://docs.python-guide.org/en/latest/starting/installation/). + +The dataset used in this tutorial is the +[SuperStore Sales dataset](https://www.kaggle.com/datasets/rohitsahoo/sales-forecasting) +available [here](https://github.com/AlexandreSajus/taipy-course/blob/main/data.csv). + +## Tutorial Steps + +1. [Visual Elements](step_01/step_01.md) +2. [Styling](step_02/step_02.md) +3. [Charts](step_03/step_03.md) +4. [Multipage](step_04/step_04.md) diff --git a/docs/tutorials/articles/sales_dashboard/step_01/images/simple_app.png b/docs/tutorials/articles/sales_dashboard/step_01/images/simple_app.png new file mode 100644 index 000000000..fac2d939b Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_01/images/simple_app.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_01/step_01.md b/docs/tutorials/articles/sales_dashboard/step_01/step_01.md new file mode 100644 index 000000000..65dd1f6f7 --- /dev/null +++ b/docs/tutorials/articles/sales_dashboard/step_01/step_01.md @@ -0,0 +1,115 @@ +--- +hide: + - toc +--- + +The full code for this step is available +[here](https://github.com/AlexandreSajus/taipy-course/blob/main/2_visual_elements/main.py) + +Let's start by creating a simple page with 3 components: a selector to select a category of items, +a bar chart which displays the sales of the top 10 countries for this category and +a table which displays data for the selected category + +![Step 1 Application](images/simple_app.png){ width=90% : .tp-image-border } + +Let's start by importing the necessary libraries: + +```python +from taipy.gui import Gui +import taipy.gui.builder as tgb +import pandas as pd +``` + +We can now start creating the page. We will first add a [selector](../../../../refmans/gui/viselements/generic/selector.md). + +```python +with tgb.Page() as page: + tgb.selector(value="{selected_category}", lov="{categories}", on_change=change_category) +``` + +Taipy [visual elements](../../../../refmans/gui/viselements/index.md) take many properties. +Note that dynamic properties use a quote and brackets syntax. We use `value="{selected_category}"` +to signal to Taipy that `selected_category` should change when the user uses the selector. +Likewise, if `categories` changes, the selector will get updated with the new values. + +Here, selector needs an associated string variable which will change when a user selects a value, +a list of values (lov) to choose from, and a callback function to call when the value changes. +We can define them above: + +```python +data = pd.read_csv("data.csv") +selected_category = "Furniture" +categories = list(data["Category"].unique()) + +def change_category(state): + # Do nothing for now, we will implement this later + return None +``` + +We can now add a chart to display the sales of the top 10 countries for the selected category. + +```python + tgb.chart( + data="{chart_data}", + x="State", + y="Sales", + type="bar", + layout="{layout}", + ) +``` + +Taipy charts have a specific syntax described [here](../../../../refmans/gui/viselements/generic/chart.md). You +can also directly embed Plotly charts using the `figure` property as we will do in [Step 3](../step_03/step_03.md). + +Here we need to provide a Pandas Dataframe with the data to display, the x and y columns to use, the type of chart, +and a layout dictionary with additional properties. + +```python +chart_data = ( + data.groupby("State")["Sales"] + .sum() + .sort_values(ascending=False) + .head(10) + .reset_index() +) + +layout = {"yaxis": {"title": "Revenue (USD)"}, "title": "Sales by State"} +``` + +Lastly, we can add a table to display the data for the selected category. + +```python + tgb.table(data="{data}") +``` + +We can now run the application using: + +```python +Gui(page=page).run(title="Sales", dark_mode=False, debug=True) +``` + +`debug=True` will display a stack trace of the errors if any occur. +You can also set `use_reloader=True` to automatically reload the page +when you save changes to the code and `port=XXXX` to change the server port. + +The application runs but has no interaction. We need to code the callback function +to update the chart and table when the user selects a category. + +```python +def change_category(state): + state.data = data[data["Category"] == state.selected_category] + state.chart_data = ( + state.data.groupby("State")["Sales"] + .sum() + .sort_values(ascending=False) + .head(10) + .reset_index() + ) + state.layout = { + "yaxis": {"title": "Revenue (USD)"}, + "title": f"Sales by State for {state.selected_category}", + } +``` + +Taipy uses a `state` object to store the variables per client. +The syntax to update a variable will always be `state.variable = new_value`. \ No newline at end of file diff --git a/docs/tutorials/articles/sales_dashboard/step_02/images/filters.png b/docs/tutorials/articles/sales_dashboard/step_02/images/filters.png new file mode 100644 index 000000000..1b19ffe00 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_02/images/filters.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_02/images/layout.png b/docs/tutorials/articles/sales_dashboard/step_02/images/layout.png new file mode 100644 index 000000000..6d2397039 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_02/images/layout.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_02/images/styling_app.png b/docs/tutorials/articles/sales_dashboard/step_02/images/styling_app.png new file mode 100644 index 000000000..b6f32eee7 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_02/images/styling_app.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_02/step_02.md b/docs/tutorials/articles/sales_dashboard/step_02/step_02.md new file mode 100644 index 000000000..3d72f708b --- /dev/null +++ b/docs/tutorials/articles/sales_dashboard/step_02/step_02.md @@ -0,0 +1,198 @@ +--- +hide: + - toc +--- + +The full code for this step is available +[here](https://github.com/AlexandreSajus/taipy-course/tree/main/3_styling) + +This step will be about styling the application. We will add more filters, layout the visual element and +regroup them in parts. + +![Styling Application](images/styling_app.png){ width=90% : .tp-image-border } + +We can still use the same code as the previous step, but let's recreate the page from scratch: + +```python +with tgb.Page() as page: + with tgb.part(class_name="container"): + tgb.text("# Sales by **State**", mode="md") +``` + +[Part](../../../../refmans/gui/viselements/generic/part.md) allows you to group and style visual elements together. +Here, the [container](../../../../userman/gui/styling/stylekit.md) class is a predefined style +that will limit the width of visual elements contained in the part. + +[Text](../../../../refmans/gui/viselements/generic/text.md) is a simple text visual element. Here we use the +Markdown (md) mode to display a title and make the word "State". We can also color bold text in orange +by using CSS. Create a new CSS file with the same name as the Python file and add the following code: + +```css +strong, +b { + font-weight: bold; + color: var(--color-primary); +} +``` + +Let's now add a new container for the filters: + +```python + with tgb.part(class_name="card"): + with tgb.layout(columns="1 2 1"): + with tgb.part(): + tgb.text("Filter **From**", mode="md") + with tgb.part(): + tgb.text("Filter Product **Category**", mode="md") + with tgb.part(class_name="text-center"): + tgb.button( + "Apply", + ) +``` + +[Card](../../../../userman/gui/styling/stylekit.md) is a predefined style that will regroup visual elements in +a white box. [layout](../../../../refmans/gui/viselements/generic/layout.md) allows you to create columns +for visual elements. Here we create 3 columns with a ratio of 1:2:1, the second column will be twice as wide as the +first and last columns. The contents of each column then needs to be separated in parts. + +![Layout](images/layout.png){ width=90% : .tp-image-border } + +We can now add [date selectors](../../../../refmans/gui/viselements/generic/date.md), +[selectors](../../../../refmans/gui/viselements/generic/selector.md) and a +[button](../../../../refmans/gui/viselements/generic/button.md) to apply the filters: + +```python + with tgb.part(class_name="card"): + with tgb.layout(columns="1 2 1"): + with tgb.part(): + tgb.text("Filter **From**", mode="md") + tgb.date("{start_date}") + tgb.text("To") + tgb.date("{end_date}") + with tgb.part(): + tgb.text("Filter Product **Category**", mode="md") + tgb.selector( + value="{selected_category}", + lov="{categories}", + on_change=change_category, + dropdown=True, + ) + tgb.text("Filter Product **Subcategory**", mode="md") + tgb.selector( + value="{selected_subcategory}", + lov="{subcategories}", + dropdown=True, + ) + with tgb.part(class_name="text-center"): + tgb.button( + "Apply", + class_name="plain apply_button", + on_action=apply_changes, + ) +``` + +You'll notice we converted our selectors to dropdowns by setting the `dropdown` property to `True`. +We also applied styling to the button: `plain` is a predefined style that colors the button in orange. +Predefined styles are available in visual elements documentation. +Check out the styling part of [button](../../../../refmans/gui/viselements/generic/button.md/). +`apply_button` is a custom style that we can add using our CSS file: + +```css +.apply_button { + margin-top: 158px; +} +``` + +This will add a margin to the top of the button to align it with the filters. +We can also add properties to all Taipy buttons by applying properties to the `taipy-button` class +(You can find these class names by inspecting the page on a visual element) + +```css +.taipy-button { + width: 60% +} +``` + +![Filters](images/filters.png){ width=90% : .tp-image-border } + +We can now add the chart and the table: + +```python + tgb.html("br") + tgb.chart( + data="{chart_data}", + x="State", + y="Sales", + type="bar", + layout="{layout}", + ) + tgb.html("br") + tgb.table(data="{data}") + +Gui(page=page).run(title="Sales", dark_mode=False, debug=True) + +``` + +We use `tgb.html("br")` to add a line break and create space between elements. + +The last thing we need is to initialize the new variables and create the callback +function to apply the filter: + +```python +data = pd.read_csv("data.csv") +chart_data = ( + data.groupby("State")["Sales"] + .sum() + .sort_values(ascending=False) + .head(10) + .reset_index() +) + +start_date = "2015-01-01" +start_date = pd.to_datetime(start_date) +end_date = "2018-12-31" +end_date = pd.to_datetime(end_date) + +categories = list(data["Category"].unique()) +selected_category = "Furniture" + +selected_subcategory = "Bookcases" +subcategories = list( + data[data["Category"] == selected_category]["Sub-Category"].unique() +) + +layout = {"yaxis": {"title": "Revenue (USD)"}, "title": "Sales by State"} + + +def change_category(state): + state.subcategories = list( + data[data["Category"] == state.selected_category]["Sub-Category"].unique() + ) + state.selected_subcategory = state.subcategories[0] + + +def apply_changes(state): + state.data = data[ + ( + pd.to_datetime(data["Order Date"], format="%d/%m/%Y") + >= pd.to_datetime(state.start_date) + ) + & ( + pd.to_datetime(data["Order Date"], format="%d/%m/%Y") + <= pd.to_datetime(state.end_date) + ) + ] + state.data = state.data[state.data["Category"] == state.selected_category] + state.data = state.data[state.data["Sub-Category"] == state.selected_subcategory] + state.chart_data = ( + state.data.groupby("State")["Sales"] + .sum() + .sort_values(ascending=False) + .head(10) + .reset_index() + ) + state.layout = { + "yaxis": {"title": "Revenue (USD)"}, + "title": f"Sales by State for {state.selected_category} - {state.selected_subcategory}", + } +``` \ No newline at end of file diff --git a/docs/tutorials/articles/sales_dashboard/step_03/images/map.png b/docs/tutorials/articles/sales_dashboard/step_03/images/map.png new file mode 100644 index 000000000..bed0ac8a4 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_03/images/map.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_03/images/plotly_map.png b/docs/tutorials/articles/sales_dashboard/step_03/images/plotly_map.png new file mode 100644 index 000000000..cae76d514 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_03/images/plotly_map.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_03/step_03.md b/docs/tutorials/articles/sales_dashboard/step_03/step_03.md new file mode 100644 index 000000000..8bb1b8aad --- /dev/null +++ b/docs/tutorials/articles/sales_dashboard/step_03/step_03.md @@ -0,0 +1,73 @@ +--- +hide: + - toc +--- + +The full code for this step is available +[here](https://github.com/AlexandreSajus/taipy-course/tree/main/4_charts) + +In this part we will embed a Plotly map figure in our application. + +![Map embedded in application](images/map.png){ width=90% : .tp-image-border } + +For this purpose, we will use the `generate_map` function defined +[here](https://github.com/AlexandreSajus/taipy-course/blob/main/4_charts/chart.py) +to return a Plotly map figure. + +![Plotly Map](images/plotly_map.png){ width=50% : .tp-image-border } + +Using the same code as the previous steps, we can import the function and initialize the chart: + +```python +from chart import generate_map + +data = pd.read_csv("data.csv") +map_fig = generate_map(data) +``` + +We can now add the map to the page by replacing our previous chart and table with: + +```python + tgb.html("br") + with tgb.layout(columns="2 3"): + tgb.chart( + data="{chart_data}", + x="State", + y="Sales", + type="bar", + layout="{layout}", + ) + tgb.chart(figure="{map_fig}") + tgb.html("br") + tgb.table(data="{data}") +``` + +We should now update the callback function to refresh the map when filters are applied: + +```python +def apply_changes(state): + state.data = data[ + ( + pd.to_datetime(data["Order Date"], format="%d/%m/%Y") + >= pd.to_datetime(state.start_date) + ) + & ( + pd.to_datetime(data["Order Date"], format="%d/%m/%Y") + <= pd.to_datetime(state.end_date) + ) + ] + state.data = state.data[state.data["Category"] == state.selected_category] + state.data = state.data[state.data["Sub-Category"] == state.selected_subcategory] + state.chart_data = ( + state.data.groupby("State")["Sales"] + .sum() + .sort_values(ascending=False) + .head(10) + .reset_index() + ) + state.layout = { + "yaxis": {"title": "Revenue (USD)"}, + "title": f"Sales by State for {state.selected_category} - {state.selected_subcategory}", + } + state.map_fig = generate_map(state.data) +``` \ No newline at end of file diff --git a/docs/tutorials/articles/sales_dashboard/step_04/images/menu.png b/docs/tutorials/articles/sales_dashboard/step_04/images/menu.png new file mode 100644 index 000000000..898aa1624 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_04/images/menu.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_04/step_04.md b/docs/tutorials/articles/sales_dashboard/step_04/step_04.md new file mode 100644 index 000000000..f4737c26e --- /dev/null +++ b/docs/tutorials/articles/sales_dashboard/step_04/step_04.md @@ -0,0 +1,78 @@ +--- +hide: + - toc +--- + +The full code for this step is available +[here](https://github.com/AlexandreSajus/taipy-course/tree/main/5_multipage) + +In this part we will add a second page to our application and a sidebar menu to navigate between pages. + +![Sidebar Menu](images/menu.png){ width=90% : .tp-image-border } + +Using the same code as the previous steps, let's add a root page which will contain the sidebar menu +and appear on all pages: + +```python +from taipy.gui import Gui, Icon, navigate + +with tgb.Page() as root_page: + tgb.menu( + label="Menu", + lov=[ + ("page1", Icon("images/map.png", "Sales")), + ("page2", Icon("images/person.png", "Account")), + ], + on_action=menu_option_selected, + ) +``` + +Here we use the [menu](../../../../refmans/gui/viselements/generic/menu.md) visual element to create a sidebar menu. +`menu` take a label which will be displayed at the top of the menu, a list of values (lov) which need the following format: +`(page_url, Icon(icon_image_path, page_name))`, and a callback function to call when an option is selected. + +The images used are available [here](https://github.com/AlexandreSajus/taipy-course/tree/main/5_multipage/images) + +Let's add a second page: + +```python +with tgb.Page() as page_2: + tgb.text("# Account **Management**", mode="md") + tgb.button("Logout", class_name="plain") +``` + +And run the application: + +```python +pages = {"/": root_page, "page1": page, "page2": page_2} + +Gui(pages=pages).run(title="Sales", dark_mode=False, debug=True) +``` + +For multipage applications, we use `pages` instead of `page` as the argument of the `Gui` class. + +`pages` is a dictionary where the key is the page URL and the value is the page object. + +We now need to create a callback function to navigate between pages: + +```python +def menu_option_selected(state, action, info): + page = info["args"][0] + navigate(state, to=page) +``` + +The callback signature for `menu` visual element is described [here](../../../../refmans/gui/viselements/generic/menu.md). +Here we extract the page URL from the `info` dictionary and use the `navigate` function to change the page. + +Lastly, we can add some CSS to resize the images and make the login button smaller: + +```css +.login-button { + width: 20% !important; +} + +.MuiAvatar-img { + width: 70%; + height: 70%; +} +``` \ No newline at end of file diff --git a/docs/tutorials/index.md_template b/docs/tutorials/index.md_template index 190b9e2ea..4de4fa557 100644 --- a/docs/tutorials/index.md_template +++ b/docs/tutorials/index.md_template @@ -94,6 +94,19 @@ Learn how to build Taipy applications through tutorials! +
  • + +
    + +
    +
    +

    Creating a Sales Dashboard

    +

    + Understand basic knowledge of Taipy by creating of a multi-page sales dashboard. +

    +
    +
    +
  • diff --git a/mkdocs.yml_template b/mkdocs.yml_template index e27b82ec2..231dd08ae 100644 --- a/mkdocs.yml_template +++ b/mkdocs.yml_template @@ -30,6 +30,12 @@ nav: - "5 - Python expressions in properties": tutorials/articles/understanding_gui/step_05/step_05.md - "6 - Page layouts": tutorials/articles/understanding_gui/step_06/step_06.md - "7 - Multi-pages, navbars, and menus": tutorials/articles/understanding_gui/step_07/step_07.md + - "Creating a Sales Dashboard": + - "Creating a Sales Dashboard": tutorials/articles/sales_dashboard/index.md + - "1 - Visual Elements": tutorials/articles/sales_dashboard/step_01/step_01.md + - "2 - Styling": tutorials/articles/sales_dashboard/step_02/step_02.md + - "3 - Charts": tutorials/articles/sales_dashboard/step_03/step_03.md + - "4 - Multipage": tutorials/articles/sales_dashboard/step_04/step_04.md - "Scenario management overview": tutorials/articles/scenario_management_overview/index.md - "First Realistic application": - "First Realistic application": tutorials/articles/complete_application/index.md