# Create app for demo-production-planning algos.py
import pandas as pd
import numpy as np
from pulp import *

# This code is used for config.py

###############################################################################
# Functions
###############################################################################

def create_model(demand: pd.DataFrame, fixed_variables: dict):
    """This function creates the model. It will creates all the variables and contraints of the problem.
    It will also create the objective function.

    Args:
        demand (pd.DataFrame): demand dataframe
        fixed_variables (dict): fixed variables dictionary

    Returns:
        dict: model_info (with the model created)
    """
    print("Creating the model...")

    monthly_demand_FPA = demand["Demand_A"]
    monthly_demand_FPB = demand["Demand_B"]

    nb_periods = len(monthly_demand_FPA)

    # creation of the model
    prob = LpProblem("Production_Planning", LpMinimize)

    # creation of the variables
    # for product A
    monthly_production_FPA = [
        LpVariable(f"Monthly_Production_FPA_{m}", 0) for m in range(nb_periods)
    ]
    monthly_stock_FPA = [
        LpVariable(f"Monthly_Stock_FPA_{m}", 0) for m in range(nb_periods)
    ]

    monthly_back_order_FPA = [
        LpVariable(f"Monthly_Back_Order_FPA_{m}", 0) for m in range(nb_periods)
    ]

    # for product B
    monthly_production_FPB = [
        LpVariable(f"Monthly_Production_FPB_{m}", 0) for m in range(nb_periods)
    ]
    monthly_stock_FPB = [
        LpVariable(f"Monthly_Stock_FPB_{m}", 0) for m in range(nb_periods)
    ]
    monthly_back_order_FPB = [
        LpVariable(f"Monthly_Back_Order_FPB_{m}", 0) for m in range(nb_periods)
    ]

    # for product 1
    monthly_purchase_RPone = [
        LpVariable(f"Monthly_Purchase_RPone_{m}", 0) for m in range(nb_periods)
    ]
    monthly_stock_RPone = [
        LpVariable(f"Monthly_Stock_RPone_{m}", 0) for m in range(nb_periods)
    ]
    monthly_stock_not_used_RPone = [
        LpVariable(f"Monthly_Stock_not_used_RPone{m}", 0) for m in range(nb_periods)
    ]
    monthly_stock_RPone_for_FPA = [
        LpVariable(f"Monthly_Stock_RPone_for_FPA{m}", 0) for m in range(nb_periods)
    ]
    monthly_stock_RPone_for_FPB = [
        LpVariable(f"Monthly_Stock_RPone_for_FPB{m}", 0) for m in range(nb_periods)
    ]

    # for product 2
    monthly_purchase_RPtwo = [
        LpVariable(f"Monthly_Purchase_RPtwo{m}", 0) for m in range(nb_periods)
    ]
    monthly_stock_RPtwo = [
        LpVariable(f"Monthly_Stock_RP{m}two", 0) for m in range(nb_periods)
    ]
    monthly_stock_not_used_RPtwo = [
        LpVariable(f"Monthly_Stock_not_used_RPtwo{m}", 0) for m in range(nb_periods)
    ]
    monthly_stock_RPtwo_for_FPA = [
        LpVariable(f"Monthly_Stock_RPtwo_for_FPA{m}", 0) for m in range(nb_periods)
    ]
    monthly_stock_RPtwo_for_FPB = [
        LpVariable(f"Monthly_Stock_RPtwo_for_FPB{m}", 0) for m in range(nb_periods)
    ]

    # creation of the constraints

    # Kirchoff's law for product A
    for m in range(1, nb_periods):
        prob += (
            monthly_production_FPA[m]
            - monthly_back_order_FPA[m - 1]
            + monthly_stock_FPA[m - 1]
            == monthly_demand_FPA[m] + monthly_stock_FPA[m] - monthly_back_order_FPA[m]
        )
    # Kirchoff's law for product B
    for m in range(1, nb_periods):
        prob += (
            monthly_production_FPB[m]
            - monthly_back_order_FPB[m - 1]
            + monthly_stock_FPB[m - 1]
            == monthly_demand_FPB[m] + monthly_stock_FPB[m] - monthly_back_order_FPB[m]
        )

    # Kirchoff's law for product 1
    for m in range(1, nb_periods):
        prob += (
            monthly_purchase_RPone[m - 1] + monthly_stock_not_used_RPone[m - 1]
            == monthly_stock_RPone[m]
        )
    # MS Fix for None issue
    prob += monthly_purchase_RPone[nb_periods - 1] == 0

    for m in range(1, nb_periods):
        prob += (
            monthly_purchase_RPtwo[m - 1] + monthly_stock_not_used_RPtwo[m - 1]
            == monthly_stock_RPtwo[m]
        )
    # MS Fix for None issue
    prob += monthly_purchase_RPtwo[nb_periods - 1] == 0

    for m in range(nb_periods):
        prob += monthly_production_FPA[m] <= fixed_variables["Max_Capacity_FPA"]

    prob += monthly_production_FPA[0] == fixed_variables["Initial_Production_FPA"]

    prob += monthly_back_order_FPA[0] == fixed_variables["Initial_Back_Order_FPA"]

    prob += monthly_stock_FPA[0] == fixed_variables["Initial_Stock_FPA"]

    # constraints on bill of materials for product A

    for m in range(1, nb_periods):
        prob += (
            monthly_production_FPA[m]
            == fixed_variables["number_RPone_to_produce_FPA"]
            * monthly_stock_RPone_for_FPA[m - 1]
            + fixed_variables["number_RPtwo_to_produce_FPA"]
            * monthly_stock_RPtwo_for_FPA[m - 1]
        )

    for m in range(nb_periods):
        prob += (
            fixed_variables["number_RPone_to_produce_FPA"]
            * monthly_stock_RPone_for_FPA[m]
            == fixed_variables["number_RPtwo_to_produce_FPA"]
            * monthly_stock_RPtwo_for_FPA[m]
        )

    # constraints on the variables : max and initial value for product A
    for m in range(nb_periods):
        prob += monthly_production_FPB[m] <= fixed_variables["Max_Capacity_FPB"]
    prob += monthly_production_FPB[0] == fixed_variables["Initial_Production_FPB"]

    prob += monthly_back_order_FPB[0] == fixed_variables["Initial_Back_Order_FPB"]
    prob += monthly_stock_FPB[0] == fixed_variables["Initial_Stock_FPB"]

    # constraints on bill of materials for product B

    for m in range(1, nb_periods):
        prob += (
            monthly_production_FPB[m]
            == fixed_variables["number_RPone_to_produce_FPB"]
            * monthly_stock_RPone_for_FPB[m - 1]
            + fixed_variables["number_RPtwo_to_produce_FPB"]
            * monthly_stock_RPtwo_for_FPB[m - 1]
        )

    for m in range(nb_periods):
        prob += (
            fixed_variables["number_RPone_to_produce_FPB"]
            * monthly_stock_RPone_for_FPB[m]
            == fixed_variables["number_RPtwo_to_produce_FPB"]
            * monthly_stock_RPtwo_for_FPB[m]
        )

    for m in range(nb_periods):
        prob += monthly_stock_RPone[m] <= fixed_variables["Max_Stock_RPone"]

    prob += monthly_stock_RPone[0] == fixed_variables["Initial_Stock_RPone"]

    prob += monthly_purchase_RPone[0] == fixed_variables["Initial_Purchase_RPone"]

    for m in range(nb_periods):
        prob += monthly_stock_RPone[m] == (
            monthly_stock_not_used_RPone[m]
            + monthly_stock_RPone_for_FPA[m]
            + monthly_stock_RPone_for_FPB[m]
        )
    # constraints on the variables : max and initial value for product 1

    for m in range(nb_periods):
        prob += monthly_stock_RPtwo[m] <= fixed_variables["Max_Stock_RPtwo"]

    prob += monthly_stock_RPtwo[0] == fixed_variables["Initial_Stock_RPtwo"]

    prob += monthly_purchase_RPtwo[0] == fixed_variables["Initial_Purchase_RPtwo"]
    # constraints that define what is a stock for product 1

    for m in range(nb_periods):
        prob += monthly_stock_RPtwo[m] == (
            monthly_stock_not_used_RPtwo[m]
            + monthly_stock_RPtwo_for_FPA[m]
            + monthly_stock_RPtwo_for_FPB[m]
        )
    # constraints on the variables : max value for product A and B (cumulative)

    for m in range(nb_periods):
        prob += (
            monthly_production_FPA[m] + monthly_demand_FPB[m]
            <= fixed_variables["Max_Capacity_of_FPA_and_FPB"]
        )

    # setting the objective function
    prob += lpSum(
        fixed_variables["Weight_of_Back_Order"]
        / 100
        * (
            fixed_variables["cost_FPA_Back_Order"] * monthly_back_order_FPA[m]
            + fixed_variables["cost_FPB_Back_Order"] * monthly_back_order_FPB[m]
        )
        + fixed_variables["Weight_of_Stock"]
        / 100
        * (
            fixed_variables["cost_FPA_Stock"] * monthly_stock_FPA[m]
            + fixed_variables["cost_FPB_Stock"] * monthly_stock_FPB[m]
            + fixed_variables["cost_RPone_Stock"] * monthly_stock_RPone[m]
            + fixed_variables["cost_RPtwo_Stock"] * monthly_stock_RPtwo[m]
        )
        for m in range(nb_periods)
    )

    # putting all the needed information in a dictionary
    model_info = {
        "model_created": prob,
        "model_solved": None,
        "Monthly_Production_FPA": monthly_production_FPA,
        "Monthly_Stock_FPA": monthly_stock_FPA,
        "Monthly_Back_Order_FPA": monthly_back_order_FPA,
        "Monthly_Production_FPB": monthly_production_FPB,
        "Monthly_Stock_FPB": monthly_stock_FPB,
        "Monthly_Back_Order_FPB": monthly_back_order_FPB,
        "Monthly_Stock_RPone": monthly_stock_RPone,
        "Monthly_Stock_RPtwo": monthly_stock_RPtwo,
        "Monthly_Purchase_RPone": monthly_purchase_RPone,
        "Monthly_Purchase_RPtwo": monthly_purchase_RPtwo,
    }

    print("Model created")
    return model_info

def solve_model(model_info: dict, solver_name):
    """This function solves the model and returns all the solutions in a dictionary.

    Args:
        model_info (dict): the model_info passed by the create_model function

    Returns:
        dict: the model solved and and the solutions
    """
    print("Solving the model...")
    prob = model_info["model_created"]

    nb_periods = len(model_info["Monthly_Production_FPA"])

    if solver_name != "Default":
        solver = getSolver(solver_name)
        # solving the model
        m_solved = prob.solve(solver)
    else:
        m_solved = prob.solve()

    # getting the solution in the right variables
    # for product A
    prod_sol_FPA = [
        value(model_info["Monthly_Production_FPA"][p]) for p in range(nb_periods)
    ]
    stock_sol_FPA = [
        value(model_info["Monthly_Stock_FPA"][p]) for p in range(nb_periods)
    ]
    bos_sol_FPA = [
        value(model_info["Monthly_Back_Order_FPA"][p]) for p in range(nb_periods)
    ]

    # for product B
    prod_sol_FPB = [
        value(model_info["Monthly_Production_FPB"][p]) for p in range(nb_periods)
    ]
    stock_sol_FPB = [
        value(model_info["Monthly_Stock_FPB"][p]) for p in range(nb_periods)
    ]
    bos_sol_FPB = [
        value(model_info["Monthly_Back_Order_FPB"][p]) for p in range(nb_periods)
    ]

    # for product 1
    stock_RPone_sol = [
        value(model_info["Monthly_Stock_RPone"][p]) for p in range(nb_periods)
    ]
    stock_RPtwo_sol = [
        value(model_info["Monthly_Stock_RPtwo"][p]) for p in range(nb_periods)
    ]

    # for product 2
    purchase_RPone_sol = [
        value(model_info["Monthly_Purchase_RPone"][p]) for p in range(nb_periods)
    ]
    purchase_RPtwo_sol = [
        value(model_info["Monthly_Purchase_RPtwo"][p]) for p in range(nb_periods)
    ]

    # put it in a dictionary
    model_info = {
        "model_created": prob,
        "model_solved": m_solved,
        "Monthly_Production_FPA": prod_sol_FPA,
        "Monthly_Stock_FPA": stock_sol_FPA,
        "Monthly_Back_Order_FPA": bos_sol_FPA,
        "Monthly_Production_FPB": prod_sol_FPB,
        "Monthly_Stock_FPB": stock_sol_FPB,
        "Monthly_Back_Order_FPB": bos_sol_FPB,
        "Monthly_Stock_RPone": stock_RPone_sol,
        "Monthly_Purchase_RPone": purchase_RPone_sol,
        "Monthly_Stock_RPtwo": stock_RPtwo_sol,
        "Monthly_Purchase_RPtwo": purchase_RPtwo_sol,
    }
    print("Model solved")
    return model_info

def create_results(model_info: dict, fixed_variables: dict, demand: pd.DataFrame):
    """This function creates the results of the model. The results dataframe is a concatenation of all the useful information.

    Args:
        model_info (dict): the dictionary created by the solve_model function
        fixed_variables (dict): the fixed variables of the problem
        demand (pd.DataFrame): the demand for A and B

    Returns:
        pd.DataFrame: dataframe that gathers all the useful information about the solution
    """
    print("Creating the results...")

    # getting the demand for A and B
    demand_series_FPA = demand["Demand_A"]
    demand_series_FPB = demand["Demand_B"]

    nb_periods = len(demand_series_FPA)

    # calculate the different costs
    cost_FPBO_FPA = fixed_variables["cost_FPA_Back_Order"] * np.array(
        model_info["Monthly_Back_Order_FPA"]
    )
    cost_stock_FPA = fixed_variables["cost_FPA_Stock"] * np.array(
        model_info["Monthly_Stock_FPA"]
    )
    cost_FPBO_FPB = fixed_variables["cost_FPB_Back_Order"] * np.array(
        model_info["Monthly_Back_Order_FPB"]
    )
    cost_stock_FPB = fixed_variables["cost_FPB_Stock"] * np.array(
        model_info["Monthly_Stock_FPB"]
    )
    cost_stock_RPone = fixed_variables["cost_RPone_Stock"] * np.array(
        model_info["Monthly_Stock_RPone"]
    )
    cost_stock_RPtwo = fixed_variables["cost_RPtwo_Stock"] * np.array(
        model_info["Monthly_Stock_RPtwo"]
    )
    cost_product_RPone = fixed_variables["cost_RPone_Purchase"] * np.array(
        model_info["Monthly_Purchase_RPone"]
    )
    cost_product_RPtwo = fixed_variables["cost_RPtwo_Purchase"] * np.array(
        model_info["Monthly_Purchase_RPtwo"]
    )

    # the total cost (sum of the costs)
    total_cost = (
        cost_FPBO_FPA
        + cost_stock_FPA
        + cost_FPBO_FPB
        + cost_stock_FPB
        + cost_stock_RPone
        + cost_product_RPone
        + cost_product_RPtwo
        + cost_stock_RPtwo
    )

    # creation of the dictionary that will be used to create the dataframe
    dict_for_dataframe = {
        "Monthly Production FPA": model_info["Monthly_Production_FPA"],
        "Monthly Stock FPA": model_info["Monthly_Stock_FPA"],
        "Monthly BO FPA": model_info["Monthly_Back_Order_FPA"],
        "Max Capacity FPA": [fixed_variables["Max_Capacity_FPA"]] * nb_periods,
        "Monthly Production FPB": model_info["Monthly_Production_FPB"],
        "Monthly Stock FPB": model_info["Monthly_Stock_FPB"],
        "Monthly BO FPB": model_info["Monthly_Back_Order_FPB"],
        "Max Capacity FPB": [fixed_variables["Max_Capacity_FPB"]] * nb_periods,
        "Monthly Stock RP1": model_info["Monthly_Stock_RPone"],
        "Monthly Stock RP2": model_info["Monthly_Stock_RPtwo"],
        "Monthly Purchase RP1": model_info["Monthly_Purchase_RPone"],
        "Monthly Purchase RP2": model_info["Monthly_Purchase_RPtwo"],
        "Demand FPA": demand_series_FPA,
        "Demand FPB": demand_series_FPB,
        "Stock FPA Cost": cost_stock_FPA,
        "Stock FPB Cost": cost_stock_FPB,
        "Stock RP1 Cost": cost_stock_RPone,
        "Stock RP2 Cost": cost_stock_RPtwo,
        "Purchase RP1 Cost": cost_product_RPone,
        "Purchase RP2 Cost": cost_product_RPtwo,
        "BO FPA Cost": cost_FPBO_FPA,
        "BO FPB Cost": cost_FPBO_FPB,
        "Total Cost": total_cost,
        "index": range(nb_periods),
    }

    results = pd.DataFrame(dict_for_dataframe).round()
    print("Results created")

    # we erase the last two observations because of how the model is created,
    # their values don't have a meaning
    return results[:-2]


# Create app for demo-production-planning create_data.py
import numpy as np
import pandas as pd

# this code is used to create the csv file for the demand, it is the source data for the problem

def create_time_series(nb_months=12,mean_A=840,mean_B=760,std_A=96,std_B=72, amplitude_A=108,amplitude_B=144):
    time_series_A = [mean_A]
    time_series_B = [mean_B]

    for i in range(1,nb_months):
        time_series_A.append(np.random.normal(mean_A + amplitude_A*np.sin(2*np.pi*i/12), std_A))
        time_series_B.append(np.random.normal(mean_B + amplitude_B*np.sin((2*np.pi*(i+6))/12), std_B))

    time_series_A = pd.Series(time_series_A)
    time_series_B = pd.Series(time_series_B)
    month = [i%12 for i in range(nb_months)]
    year = [i//12 + 2020 for i in range(nb_months)]
    df_time_series = pd.DataFrame({"Year":year,
                                   "Month":month,
                                   "Demand_A":time_series_A,
                                   "Demand_B":time_series_B})
    return df_time_series

def time_series_to_csv(nb_months=12,mean_A=840,mean_B=760,std_A=96,std_B=72, amplitude_A=108,amplitude_B=144):
    time_serie_data = create_time_series(nb_months,
                                         mean_A,
                                         mean_B,
                                         std_A,
                                         std_B,
                                         amplitude_A,
                                         amplitude_B)
    time_serie_data.to_csv('data/time_series_demand.csv')


# Create app for demo-production-planning databases_md.py
import pathlib

d_chart_csv_path = None

# this path is used to create a temporary file that will allow us to
# download a table in the Datasouces page
tempdir = pathlib.Path(".tmp")
tempdir.mkdir(exist_ok=True)
PATH_TO_TABLE = str(tempdir / "table.csv")

da_databases_md = """
<|container|
# Data**sources**{: .color-primary }

<|layout|columns=3 2 1|columns[mobile]=1|class_name=align_columns_bottom|
<layout_scenario|
<|layout|columns=1 1 3|columns[mobile]=1|class_name=align_columns_bottom|
<year|
Year

<|{sm_selected_year}|selector|lov={sm_year_selector}|dropdown|width=100%|on_change=change_sm_month_selector|>
|year>

<month|
Month

<|{sm_selected_month}|selector|lov={sm_month_selector}|dropdown|width=100%|on_change=change_scenario_selector|>
|month>

<scenario|
**Scenario**

<|{selected_scenario}|selector|lov={scenario_selector}|dropdown|adapter=adapt_scenarios|width=18rem|>
|scenario>
|>
|layout_scenario>

<|
Table

<|{sm_graph_selected}|selector|lov={sm_graph_selector}|dropdown|>
|>

<br/>
<|{d_chart_csv_path}|file_download|name=table.csv|label=Download table|>
|>

<|part|render={len(scenario_selector)>0}|class_name=mt2|
<|{chart}|table|width=100%|rebuild|>
|>
|>
"""


# Create app for demo-production-planning data_visualization_md.py
import pandas as pd
import json

with open('data/fixed_variables_default.json', "r") as f:
    fixed_variables_default = json.load(f)

# no code from Taipy Core has been executed yet, we read the csv file this way
da_initial_demand = pd.read_csv('data/time_series_demand.csv')[['Year', 'Month', 'Demand_A', 'Demand_B']]\
                                                             .astype(int)

da_initial_demand.columns = [col.replace('_', ' ') for col in da_initial_demand.columns]

da_initial_variables = pd.DataFrame({key: [fixed_variables_default[key]]
                                    for key in fixed_variables_default.keys() if 'Initial' in key})

# The code below is to correctly format the name of the columns
da_initial_variables.columns = [col.replace('_', ' ').replace('one', '1').replace('two', '2').replace('initial ', '').replace('Initial ', '')
                                for col in da_initial_variables.columns]
da_initial_variables.columns = [col[0].upper() +
                                col[1:] for col in da_initial_variables.columns]

da_data_visualisation_md = """
<|container|
# Data **Visualization**{: .color-primary }

<|Expand here to see more data|expandable|expanded=False|

    <|layout|columns=5 3 3|columns[mobile]=1|
### Initial **stock**{: .color-secondary } \
<|{da_initial_variables[[col for col in da_initial_variables.columns if 'Stock' in col]]}|table|show_all|width=100%|>

### Incoming **purchases**{: .color-secondary } \
<|{da_initial_variables[[col for col in da_initial_variables.columns if 'Purchase' in col]]}|table|show_all|width=100%|>

### Initial **production**{: .color-secondary } \
<|{da_initial_variables[[col for col in da_initial_variables.columns if 'Production' in col]]}|table|show_all|width=100%|>
    |>

## **Demand**{: .color-secondary } of the upcoming months
<|{da_initial_demand.round()}|table|width=fit-content|show_all|height=fit-content|>
|>

### **Evolution**{: .color-primary } of the demand
<|{da_initial_demand}|chart|x=Month|y[1]=Demand A|y[2]=Demand B|>
|>
"""


# Create app for demo-production-planning shared.md
<|toggle|theme|>

<|menu|label=Menu|lov={menu_lov}|on_action=menu_fct|>

<|Need any help?|button|on_action={lambda s: s.assign('dialog_help', True)}|id=help_button|>

<|{dialog_help}|dialog|title=Walkthough|on_action=validate_help|labels=Go!|id=dialog_help|width=100%|

<|container|
## Page 1: Data Visualization
Upon registering with a new account (name & password), the first page is displayed.

The primary chart depicts future demand for finished products A (FPA)
and B (FPB) over the next 11 months, with the current month marked as month 0.

Just above the chart, by clicking "Expand here," you can access an expandable
Taipy front-end containing initial production data at time 0 (current month):
stock & production levels, incoming raw material orders, and demand, all presented in a table.

## Page 2: Scenario Manager

Create, configure, and optimize production scenarios.
This is the application's main page, where users can create new scenarios,
adjust scenario parameters (on the 'Scenario Configuration' side of the page),
and re-submit scenarios for re-optimization based on modified parameters.
Initially, no scenario is available, and the Year/Month corresponds to the current month.

### Creating your first scenario

The purpose of the model is to generate a production plan (level of production
for both products) for the the next 11 months in order to:

- Meet the demand for the finished product
- Respect the Capacity Constraints
- Minimize 2 cost functions:

    - Back ordering costs: the costs of not meeting the demand on time
    - Stock costs: costs of storing raw and finished products.

It is worth noting that these 2 cost functions are kind of opposite:
if I have a lot of stock, I should easily meet the demand. Conversely,
a low inventory may put the demand in jeopardy.
When creating a first scenario, two key indicators , "Back Order Cost"
and "Stock Cost," appear above an empty main chart (no plan generated yet).

Click on "New Scenario" to launch the optimization algorithm, which
quickly finds the optimal production levels, respecting the capacity
constraints and optimizing costs.
Results can be displayed as time series or pie charts, and different
graphs can be selected by choosing the data to display (costs, productions, etc.).

### Modifying the Parameters
On the right-hand side of this panel, you can modify various parameters categorized into three sections:

- **Capacity Constraints**: Modify capacity values for different products (finished and raw).
- **Objectives Weights**: Emphasize minimizing a specific cost (stock or backordering).
- **Initial Parameters**: Modify other parameters like Initial Stock and Unit Cost.
By "Playing" with these parameters, you can create several scenarios.

## Page 3: Compare Scenarios
To Compare two scenarios, select them then click on the "compare scenario" button.
You can select different comparison metrics  such as costs, purchases, and production levels, etc.

## Page 4: Compare Cycles

This demo also introduces the concept of ‘Cycles".
In this manufacturing context, the cycle is monthly.
This implies that scenarios are created each month.
Only one of the generated scenarios will be chosen as the
‘official scenario', this scenario is referred as the "Primary" scenario.

This demo already contains many scenarios generated from the
previous months. The "Evolution of costs" bar chart displays
the performance for every single "primary' scenario generated
every month for the past few years. Compare monthly stock and
backorder costs from January 2021 to the present month using stacked bar charts.

## Page 5: Datasources

Access and display various tables associated with
a selected scenario. Conveniently download data tables in CSV format.
|>
|>


# Create app for demo-production-planning shared.py
from taipy.gui import notify, navigate, Icon
import taipy as tp
import datetime as dt

# User id
state_id = None

# Metrics for scenario manager and comparison
sum_costs = 0
sum_costs_of_stock = 0
sum_costs_of_BO = 0
sum_costs_of_BO = 0

# Navigation
page = "Data Visualization"

menu_lov = [("Data-Visualization", Icon('images/icons/visualize.svg', 'Data Visualization')),
            ("Scenario-Manager", Icon('images/icons/scenario.svg', 'Scenario Manager')),
            ("Compare-Scenarios", Icon('images/icons/compare.svg', 'Compare Scenarios')),
            ("Compare-Cycles", Icon('images/icons/cycle.svg', 'Compare Cycles')),
            ('Databases', Icon('images/icons/data_base.svg', 'Databases'))]

def menu_fct(state, var_name: str, var_value):
    """Functions that is called when there is a change in the menu control

    Args:
        state (_type_): the state object of Taipy
        var_name (str): the changed variable name
        var_value (_type_): the changed variable value
    """

    # change the value of the state.page variable in order to render the
    # correct page
    state.page = var_value['args'][0]
    navigate(state, to=state.page)

    # security on the 'All' option of sm_graph_selected that can be selected
    # only on the 'Databases' page
    if state.page != 'Databases' and state.sm_graph_selected == 'All':
        state.sm_graph_selected = 'Costs'

# Functions for scenarios
def adapt_scenarios(scenario):
    return 'Primary ' + scenario.name if scenario.is_primary else scenario.name

def create_sm_tree_dict(scenarios, sm_tree_dict: dict = None):
    """This function creates a tree dict from a list of scenarios. The levels of the tree are:
    year/month/scenario

    Args:
        scenarios (list): a list of scenarios
        sm_tree_dict (dict, optional): the tree gathering all the scenarios. Defaults to None.

    Returns:
        tree: the tree created to classify the scenarios
    """
    print("Creating tree dict...")
    if sm_tree_dict is None:
        # Initialize the tree dict if it is not already initialized
        sm_tree_dict = {}

    # Add all the scenarios that are in the list
    for scenario in scenarios:
        # Create a name for the cycle
        date = scenario.creation_date
        year = f"{date.strftime('%Y')}"
        period = f"{date.strftime('%b')}"

        # Add the cycle if it was not already added
        if year not in sm_tree_dict:
            sm_tree_dict[year] = {}
        if period not in sm_tree_dict[year]:
            sm_tree_dict[year][period] = []

        sm_tree_dict[year][period] += [scenario]

    return sm_tree_dict

def create_time_selectors():
    """This function creates the time selectors that will be displayed on the GUI and it is also creating
    the tree dict gathering all the scenarios.

    Returns:
        dict: the tree dict gathering all the scenarios
        list: the list of years
        list: the list of months
    """
    all_scenarios_ordered = sorted(tp.get_scenarios(), key=lambda x: x.creation_date.timestamp())

    sm_tree_dict = create_sm_tree_dict(all_scenarios_ordered)

    if sm_current_year not in list(sm_tree_dict.keys()):
        sm_tree_dict[sm_current_year] = {}
    if sm_current_month not in sm_tree_dict[sm_current_year]:
        sm_tree_dict[sm_current_year][sm_current_month] = []

    sm_year_selector = list(sm_tree_dict.keys())
    sm_month_selector = list(sm_tree_dict[sm_selected_year].keys())

    return sm_tree_dict, sm_year_selector, sm_month_selector

def change_sm_month_selector(state):
    """
    This function is called when the user changes the year selector. It updates the selector shown on the GUI
    for the month selector and is calling the same function for the scenario selector.

    Args:
        state (State): all the GUI variables
    """
    state.sm_month_selector = list(state.sm_tree_dict[state.sm_selected_year].keys())

    if state.sm_selected_month not in state.sm_month_selector:
        state.sm_selected_month = state.sm_month_selector[0]

    change_scenario_selector(state)

def change_scenario_selector(state):
    """
    This function is called when the user changes the month selector. It updates the selector shown on the GUI
    for the scenario selector.

    Args:
        state (State): all the GUI variables
    """
    state.scenario_selector = list(state.sm_tree_dict[state.sm_selected_year][state.sm_selected_month])
    state.scenario_selector_two = state.scenario_selector.copy()
    if len(state.scenario_selector) > 0:
        state.selected_scenario = state.scenario_selector[0]

    if (state.sm_selected_month != sm_current_month or\
            state.sm_selected_year != sm_current_year) and\
            state.sm_show_config_scenario:

        state.sm_show_config_scenario = False
        notify(state, "info", "This scenario is historical, you can't modify it")
    else:
        state.sm_show_config_scenario = True

def update_scenario_selector(state):
    """
    This function will update the scenario selectors. It will be used when
    we create a new scenario. If there is a scenario that is created, we will
    add its (id,name) in this list.

    Args:
        scenarios (list): a list of tuples (scenario,properties)
    """
    state.scenario_selector = [s for s in tp.get_scenarios() if 'user' in s.properties and\
                                                                 state.state_id == s.properties['user']]
    state.scenario_selector_two = state.scenario_selector.copy()
    sm_tree_dict[state.sm_selected_year][state.sm_selected_month] = state.scenario_selector

scenario_selector = []
selected_scenario = None

scenario_selector_two = []
selected_scenario_two = None

# Initialization of scenario tree
sm_tree_dict = {}

sm_current_month = dt.date.today().strftime('%b')
sm_current_year = dt.date.today().strftime('%Y')

sm_selected_year = sm_current_year
sm_selected_month = sm_current_month

sm_tree_dict, sm_year_selector, sm_month_selector = create_time_selectors()

# Help
dialog_help = False

def restore_state(state):
    state.cs_show_comparison = False
    update_scenario_selector(state)
    notify(state, 'info', 'Restoring your session')

def validate_help(state, action, payload):
    state.dialog_help = False


# Create app for demo-production-planning compare_cycles_md.py
from data.create_data import time_series_to_csv
from config.config import scenario_cfg

from taipy.core import taipy as tp

import datetime as dt
import pandas as pd

cc_data = pd.DataFrame(
    {
        'Date': [dt.datetime(2021, 1, 1)],
        'Cycle': [dt.date(2021, 1, 1)],
        'Cost of Back Order': [0],
        'Cost of Stock': [0]
    })

cc_show_comparison = False
cc_layout = {'barmode': 'stack'}

def cc_create_scenarios_for_cycle():
    """This function creates scenarios for multiple cycles and submit them.
    """
    date = dt.datetime.now() - dt.timedelta(days=365)
    month = date.strftime('%b')
    year = date.strftime('%Y')

    current_month = dt.date.today().strftime('%b')
    current_year = dt.date.today().strftime('%Y')

    while month != current_month or year != current_year:
        date += dt.timedelta(days=15)
        month = date.strftime('%b')
        year = date.strftime('%Y')

        if month != current_month or year != current_year:
            time_series_to_csv()

            scenario = tp.create_scenario(scenario_cfg, creation_date=date, name=date.strftime('%d-%b-%Y'))
            tp.submit(scenario)

def update_cc_data(state):
    """This function creates the evolution of the cost of back order and stock for the primary scenario of all the cycles."""

    dates = []
    cycles = []
    costs_of_back_orders = []
    costs_of_stock = []
    all_scenarios = tp.get_primary_scenarios()

    all_scenarios_ordered = sorted(
        all_scenarios,
        key=lambda x: x.creation_date.timestamp())

    for scenario in all_scenarios_ordered:
        results = scenario.results.read()

        if results is not None:
            date_ = scenario.creation_date

            # creation of sum_costs_of_stock metrics
            bool_costs_of_stock = [c for c in results.columns if 'Cost' in c and\
                                                                 'Total' not in c and\
                                                                 'Stock' in c]
            sum_costs_of_stock = int(results[bool_costs_of_stock].sum(axis=1)\
                                                                 .sum(axis=0))

            # creation of sum_costs_of_BO metrics
            bool_costs_of_BO = [c for c in results.columns if 'Cost' in c and\
                                                              'Total' not in c and\
                                                              'BO' in c]
            sum_costs_of_BO = int(results[bool_costs_of_BO].sum(axis=1)\
                                                           .sum(axis=0))

            dates.append(date_)
            cycles.append(dt.date(date_.year, date_.month, 1))
            costs_of_back_orders.append(sum_costs_of_BO)
            costs_of_stock.append(sum_costs_of_stock)

    state.cc_data = pd.DataFrame({'Date': dates,
                                  'Cycle': cycles,
                                  'Cost of Back Order': costs_of_back_orders,
                                  'Cost of Stock': costs_of_stock})

cc_compare_cycles_md = """
<|container|
# **Compare**{: .color-primary} cycles
<|Start cycles comparison|button|on_action=update_cc_data|class_name=mb2|>

<|Table|expandable|expanded=False|
<|{cc_data}|table|>
|>

## Evolution of costs
<|{cc_data}|chart|type=bar|x=Cycle|y[1]=Cost of Back Order|y[2]=Cost of Stock|layout={cc_layout}|>
|>
"""


# Create app for demo-production-planning compare_scenario_md.py
import taipy as tp
import pandas as pd

cs_compare_scenario_md = """
<|part|class_name=container|
# **Compare**{: .color-primary} scenarios

Choose two scenarios to compare.

<|layout|columns=3 3 auto|columns[mobile]=1|gap=1.5rem|class_name=align_columns_bottom|

<layout_scenario|
**Scenario 1**

<|layout|columns=1 1 3|columns[mobile]=1|
Year <|{sm_selected_year}|selector|lov={sm_year_selector}|dropdown|width=100%|on_change=change_sm_month_selector|>

Month <|{sm_selected_month}|selector|lov={sm_month_selector}|dropdown|width=100%|on_change=change_scenario_selector|>

**Scenario** <|{selected_scenario}|selector|lov={scenario_selector}|dropdown|adapter=adapt_scenarios|width=18rem|>
|>
|layout_scenario>

<layout_scenario|
**Scenario 2**

<|layout|columns=1 1 3|columns[mobile]=1|
Year <|{sm_selected_year}|selector|lov={sm_year_selector}|dropdown|width=100%|on_change=change_sm_month_selector|>

Month <|{sm_selected_month}|selector|lov={sm_month_selector}|dropdown|width=100%|on_change=change_scenario_selector|>

**Scenario** <|{selected_scenario_two}|selector|lov={scenario_selector_two}|dropdown|adapter=adapt_scenarios|>
|>
|layout_scenario>

<|Compare scenario|button|on_action=compare_scenarios|active={len(scenario_selector)>1}|>
|>

<|part|render={cs_show_comparison and len(scenario_selector)>=2}|class_name=mt2 card p2|
<|layout|columns=1 1 1|columns[mobile]=1|class_name=align_columns_bottom|
**Representation**
<|{cs_compar_graph_selected}|selector|lov={cs_compar_graph_selector}|dropdown|>

**Total cost of scenario 1:** *<|{str(int(sum_costs/1000))+' K'}|>*
{: .text-center}

**Total cost of scenario 2:** *<|{str(int(cs_sum_costs_two/1000))+' K'}|>*
{: .text-center}
|>

<|part|render={cs_compar_graph_selected=='Metrics'}|class_name=mt2|

<|layout|columns=1 1|columns[mobile]=1|
<|{cs_comparaison_metrics_df[cs_comparaison_metrics_df['Metrics']=='BO Cost']}|chart|type=bar|x=Metrics|y[1]=Scenario 1: BO Cost|y[2]=Scenario 2: BO Cost|color[2]=#2b93db|height={cs_height_bar_chart}|>

<|{cs_comparaison_metrics_df[cs_comparaison_metrics_df['Metrics']=='Stock Cost']}|chart|type=bar|x=Metrics|y[1]=Scenario 1: Stock Cost|y[2]=Scenario 2: Stock Cost|color[1]=#ff7f0e|color[2]=#ff9a41|height={cs_height_bar_chart}|>
|>
|>

<|{cs_comparaison_df}|chart|x=index|y[1]=Scenario 1 Cost|y[2]=Scenario 2 Cost|color[2]=#1f77b4|line[2]=dash|render={cs_compar_graph_selected=='Costs'}|>
<|{cs_comparaison_df}|chart|x=index|y[1]=Scenario 1 Purchase|y[2]=Scenario 2 Purchase|color[2]=#1f77b4|line[2]=dash|render={cs_compar_graph_selected=='Purchases'}|>
<|{cs_comparaison_df}|chart|x=index|y[1]=Scenario 1 Production|y[2]=Scenario 2 Production|color[2]=#1f77b4|line[2]=dash|render={cs_compar_graph_selected=='Productions'}|>
<|{cs_comparaison_df}|chart|x=index|y[1]=Scenario 1 Stock|y[2]=Scenario 2 Stock|color[2]=#1f77b4|line[2]=dash|render={cs_compar_graph_selected=='Stocks'}|>
<|{cs_comparaison_df}|chart|x=index|y[1]=Scenario 1 BO|y[2]=Scenario 2 BO|color[2]=#1f77b4|line[2]=dash|render={cs_compar_graph_selected=='Back Order'}|>
|>
|>
"""

def compare_scenarios(state):
    """This function compares two scenarios chosen by the user on different metrics and populate dataframes for the comparison graphs.

    Args:
        state (State): All the GUI variables
    """
    state.cs_show_comparison = True

    # get of the two scenarios chosen by the user
    results_1 = state.selected_scenario.results.read()
    results_2 = state.selected_scenario_two.results.read()
    state.cs_sum_costs_two = results_2['Total Cost'].sum()

    # calculate the partial costs of the two scenarios
    bool_costs_of_stock = [c for c in results_2.columns
                           if 'Cost' in c and 'Total' not in c and 'Stock' in c]

    state.cs_sum_costs_of_stock_two = int(results_2[bool_costs_of_stock].sum(axis=1)\
                                                                        .sum(axis=0))

    bool_costs_of_BO = [c for c in results_2.columns
                        if 'Cost' in c and 'Total' not in c and 'BO' in c]
    state.cs_sum_costs_of_BO_two = int(results_2[bool_costs_of_BO].sum(axis=1)\
                                                                  .sum(axis=0))

    # populate the dataframes for the comparison graphs
    new_result_1 = pd.DataFrame({"index": results_1.index})
    new_result_2 = pd.DataFrame({"index": results_2.index})

    columns_to_merge = ['Cost', 'Purchase', 'Production', 'Stock', 'BO']
    for col in columns_to_merge:
        if col == 'Cost':
            bool_col_1 = [c for c in results_1.columns
                          if col in c and 'Total' not in c]
            bool_col_2 = [c for c in results_2.columns
                          if col in c and 'Total' not in c]
        else:
            bool_col_1 = [c for c in results_1.columns
                          if col in c and 'Total' not in c and 'Cost' not in c]
            bool_col_2 = [c for c in results_2.columns
                          if col in c and 'Total' not in c and 'Cost' not in c]

        new_result_1[col] = results_1[bool_col_1].sum(axis=1)
        new_result_2[col] = results_2[bool_col_2].sum(axis=1)

    new_result_1.columns = ['Scenario 1 ' + column if column != 'index' else 'index'
                            for column in new_result_1.columns]
    new_result_2.columns = ['Scenario 2 ' + column if column !='index' else 'index'
                            for column in new_result_2.columns]

    state.cs_comparaison_metrics_df = pd.DataFrame(
        {
            "Metrics": [ "Stock Cost", "BO Cost"],
            "Scenario 1: Stock Cost": [state.sum_costs_of_stock, None],
            "Scenario 2: Stock Cost": [state.cs_sum_costs_of_stock_two, None],
            "Scenario 1: BO Cost": [None, state.sum_costs_of_BO],
            "Scenario 2: BO Cost": [None, state.cs_sum_costs_of_BO_two]
        })

    state.cs_comparaison_df = pd.merge(new_result_1, new_result_2, on="index", how="inner")
    print("Comparaison done")
    pass

cs_height_bar_chart = "80%"

cs_show_comparison = False

cs_compar_graph_selector = [
    'Metrics',
    'Costs',
    'Purchases',
    'Productions',
    'Stocks',
    'Back Order']
cs_compar_graph_selected = cs_compar_graph_selector[0]

cs_comparaison_df = pd.DataFrame({'index': [0],
                                  'Scenario 1 Cost': [0],
                                  'Scenario 1 Purchase': [0],
                                  'Scenario 1 Production': [0],
                                  'Scenario 1 Stock': [0],
                                  'Scenario 1 BO': [0],
                                  'Scenario 2 Cost': [0],
                                  'Scenario 2 Purchase': [0],
                                  'Scenario 2 Production': [0],
                                  'Scenario 2 Stock': [0],
                                  'Scenario 2 BO': [0]})

cs_comparaison_metrics_df = pd.DataFrame({"Metrics": ["Stock Cost", "BO Cost"],
                                          "Scenario 1: Stock Cost": [0, 0],
                                          "Scenario 2: Stock Cost": [0, 0],
                                          "Scenario 1: BO Cost": [0, 0],
                                          "Scenario 2: BO Cost": [0, 0]})

cs_sum_costs_of_stock_two = 0
cs_sum_costs_of_BO_two = 0
cs_sum_costs_two = 0


# Create app for demo-production-planning scenario_manager.md
<|container|
# **Scenario**{: .color-primary } Manager

<|layout|columns=8 4 auto|
    <layout_scenario|
        <|layout|columns=1 1 3|
Year <|{sm_selected_year}|selector|lov={sm_year_selector}|dropdown|on_change=change_sm_month_selector|>

Month <|{sm_selected_month}|selector|lov={sm_month_selector}|dropdown|on_change=change_scenario_selector|>

**Scenario** <|{selected_scenario}|selector|lov={scenario_selector}|dropdown|adapter=adapt_scenarios|width=18rem|class_name=success|>
        |>
    |layout_scenario>

Graph <|{sm_graph_selected}|selector|lov={sm_graph_selector}|dropdown|>

<toggle_chart|
Pie/Line chart

<|{sm_show_pie}|toggle|lov={sm_choice_chart}|value_by_id|active={not 'Product ' in sm_graph_selected}|>
|toggle_chart>
|>

---

<|layout|columns=9 3|gap=1.5rem|

<|part|render={len(scenario_selector)>0}|
<|layout|columns=1 1|gap=1rem|
    <|part|class_name=card mb1 p2 text_center|
<|{str(int(sum_costs_of_BO/1000))+' K'}|indicator|value={sum_costs_of_BO}|min=50_000|max=1_000|width=93%|>
**Back Order Cost**
    |>

    <|part|class_name=card mb1 p2 text_center|
<|{str(int(sum_costs_of_stock/1000))+' K'}|indicator|value={sum_costs_of_stock}|min=100_000|max=25_000|width=93%|>
**Stock Cost**
    |>
|>

<|{pie_results.loc[['Stock FPA Cost', 'Stock FPB Cost', 'Stock RP1 Cost', 'Stock RP2 Cost', 'Purchase RP1 Cost', 'Purchase RP2 Cost', 'BO FPA Cost', 'BO FPB Cost']]}|chart|type=pie|values=values|labels=labels|render={sm_show_pie=='pie' and sm_graph_selected=='Costs'}|>

<|{sm_results}|chart|x=index|y[1]=Stock FPA Cost|y[2]=Stock FPB Cost|y[3]=Stock RP1 Cost|y[4]=Stock RP2 Cost|y[5]=Purchase RP1 Cost|y[6]=Purchase RP2 Cost|y[7]=BO FPA Cost|y[8]=BO FPB Cost|y[9]=Total Cost|render={sm_show_pie=='chart' and sm_graph_selected=='Costs'}|>

<|{pie_results.loc[['Monthly Purchase RP1', 'Monthly Purchase RP2']]}|chart|type=pie|values=values|labels=labels|render={sm_show_pie=='pie' and sm_graph_selected=='Purchases'}|>

<|{sm_results}|chart|x=index|y[1]=Monthly Purchase RP1|y[2]=Monthly Purchase RP2|render={sm_show_pie=='chart' and sm_graph_selected=='Purchases'}|>

<|{pie_results.loc[['Monthly Production FPA', 'Max Capacity FPA', 'Monthly Production FPB', 'Max Capacity FPB']]}|chart|type=pie|values=values|labels=labels|render={sm_show_pie=='pie' and sm_graph_selected=='Productions'}|>

<|{sm_results}|chart|x=index|y[1]=Monthly Production FPA|y[2]=Max Capacity FPA|line[2]=dash|y[3]=Monthly Production FPB|y[4]=Max Capacity FPB|line[4]=dash|render={sm_show_pie=='chart' and sm_graph_selected=='Productions'}|>

<|{pie_results.loc[['Monthly Stock FPA', 'Monthly Stock FPB', 'Monthly Stock RP1', 'Monthly Stock RP2']]}|chart|type=pie|values=values|labels=labels|render={sm_show_pie=='pie' and sm_graph_selected=='Stocks'}|>

<|{sm_results}|chart|x=index|y[1]=Monthly Stock FPA|y[2]=Monthly Stock FPB|y[3]=Monthly Stock RP1|y[4]=Monthly Stock RP2|render={sm_show_pie=='chart' and sm_graph_selected=='Stocks'}|>

<|{pie_results.loc[['Monthly BO FPA', 'Monthly BO FPB']]}|chart|type=pie|values=values|labels=labels|render={sm_show_pie=='pie' and sm_graph_selected=='Back Order'}|>

<|{sm_results}|chart|x=index|y[1]=Monthly BO FPA|y[2]=Monthly BO FPB|render={sm_show_pie=='chart' and sm_graph_selected=='Back Order'}|>

<|{sm_results}|chart|x=index|y[1]=Monthly Production FPA|y[2]=Monthly Stock FPA|y[3]=Monthly BO FPA|y[4]=Max Capacity FPA|line[4]=dash|y[5]=Demand FPA|render={sm_graph_selected=='Product FPA'}|>

<|{sm_results}|chart|x=index|y[1]=Monthly Production FPB|y[2]=Monthly Stock FPB|y[3]=Monthly BO FPB|y[4]=Max Capacity FPB|line[4]=dash|y[5]=Demand FPB|render={sm_graph_selected=='Product FPB'}|>

<|{sm_results}|chart|x=index|y[1]=Monthly Stock RP1|y[2]=Monthly Purchase RP1|render={sm_graph_selected=='Product RP1'}|>

<|{sm_results}|chart|x=index|y[1]=Monthly Stock RP2|y[2]=Monthly Purchase RP2|render={sm_graph_selected=='Product RP2'}|>
|>

<no_scenario|part|render={len(scenario_selector)==0}|
#### No scenario created for the current month #### {: .mt0 .color_secondary }
|no_scenario>

<|mt2|
<|{sm_param_selected}|selector|lov={sm_param_selector}|class_name=fullwidth|>

<|part|render={sm_param_selected == 'Capacity Constraints'}|

<|{sm_product_param}|toggle|lov={sm_choice_product_param}|value_by_id|class_name=mb1 text_center|>

<|part|render={sm_product_param == 'product_FPA'}|
Max Capacity FPA : *<|{fixed_variables.Max_Capacity_FPA}|>*
<|{fixed_variables.Max_Capacity_FPA}|slider|min=332|max=1567|active={sm_show_config_scenario}|>

Max Capacity of FPA and FPB : *<|{fixed_variables.Max_Capacity_of_FPA_and_FPB}|>*
<|{fixed_variables.Max_Capacity_of_FPA_and_FPB}|slider|min=598|max=2821|active={sm_show_config_scenario}|>
|>

<|part|render={sm_product_param == 'product_FPB'}|
Max Capacity FPB : *<|{fixed_variables.Max_Capacity_FPB}|>*
<|{fixed_variables.Max_Capacity_FPB}|slider|min=332|max=1567|active={sm_show_config_scenario}|>

Max Capacity of FPA and FPB : *<|{fixed_variables.Max_Capacity_of_FPA_and_FPB}|>*
<|{fixed_variables.Max_Capacity_of_FPA_and_FPB}|slider|min=598|max=2821|active={sm_show_config_scenario}|>
|>
<|part|render={sm_product_param == 'product_RPone'}|

Max Stock RP1 : *<|{fixed_variables.Max_Stock_RPone}|>*
<|{fixed_variables.Max_Stock_RPone}|slider|min=28|max=132|active={sm_show_config_scenario}|>
|>
<|part|render={sm_product_param == 'product_RPtwo'}|

Max Stock RP2 : *<|{fixed_variables.Max_Stock_RPtwo}|>*
<|{fixed_variables.Max_Stock_RPtwo}|slider|min=21|max=99|active={sm_show_config_scenario}|>
|>
|>

<|part|render={sm_param_selected == 'Objective Weights'}|
Weight of Stock : *<|{fixed_variables.Weight_of_Stock}|>*
<|{fixed_variables.Weight_of_Stock}|slider|min=35|max=165|active={sm_show_config_scenario}|>

Weight of Back Order : *<|{fixed_variables.Weight_of_Back_Order}|>*
<|{fixed_variables.Weight_of_Back_Order}|slider|min=35|max=165|active={sm_show_config_scenario}|>
|>

<|part|render={sm_param_selected == 'Initial Parameters'}|
<|{sm_product_param}|toggle|lov={sm_choice_product_param}|value_by_id|class_name=mb1 text_center|>

<|part|render={sm_product_param == 'product_FPA'}|
Unit Cost - FPA Back Order : *<|{fixed_variables.cost_FPA_Back_Order}|>*
<|{fixed_variables.cost_FPA_Back_Order}|slider|min=70|max=330|active={sm_show_config_scenario}|>

Unit Cost - FPA Stock : *<|{fixed_variables.cost_FPA_Stock}|>*
<|{fixed_variables.cost_FPA_Stock}|slider|min=15|max=74|active={sm_show_config_scenario}|>

Initial Back Order FPA : *<|{fixed_variables.Initial_Back_Order_FPA}|>*
<|{fixed_variables.Initial_Back_Order_FPA}|slider|min=0|max=50|active={sm_show_config_scenario}|>

Initial Stock FPA : *<|{fixed_variables.Initial_Stock_FPA}|>*
<|{fixed_variables.Initial_Stock_FPA}|slider|min=10|max=49|active={sm_show_config_scenario}|>

Initial Production FPA : *<|{fixed_variables.Initial_Production_FPA}|>*
<|{fixed_variables.Initial_Production_FPA}|slider|min=297|max=1402|active={sm_show_config_scenario}|>
|>

<|part|render={sm_product_param == 'product_FPB'}|
Unit Cost - FPB Back Order : *<|{fixed_variables.cost_FPB_Back_Order}|>*
<|{fixed_variables.cost_FPB_Back_Order}|slider|min=87|max=412|active={sm_show_config_scenario}|>

Unit Cost - FPB Stock : *<|{fixed_variables.cost_FPB_Stock}|>*
<|{fixed_variables.cost_FPB_Stock}|slider|min=14|max=66|active={sm_show_config_scenario}|>

Initial Back Order FPB : *<|{fixed_variables.Initial_Back_Order_FPB}|>*
<|{fixed_variables.Initial_Back_Order_FPB}|slider|min=8|max=41|active={sm_show_config_scenario}|>

Initial Stock FPB : *<|{fixed_variables.Initial_Stock_FPB}|>*
<|{fixed_variables.Initial_Stock_FPB}|slider|min=0|max=50|active={sm_show_config_scenario}|>

Initial Production FPB : *<|{fixed_variables.Initial_Production_FPB}|>*
<|{fixed_variables.Initial_Production_FPB}|slider|min=280|max=1320|active={sm_show_config_scenario}|>
|>

<|part|render={sm_product_param == 'product_RPone'}|
Initial Stock RP1 : *<|{fixed_variables.Initial_Stock_RPone}|>*
<|{fixed_variables.Initial_Stock_RPone}|slider|min=10|max=49|active={sm_show_config_scenario}|>

Unit Cost - RP1 Stock : *<|{fixed_variables.cost_RPone_Stock}|>*
<|{fixed_variables.cost_RPone_Stock}|slider|min=10|max=49|active={sm_show_config_scenario}|>

Unit Cost - RP1 Purchase : *<|{fixed_variables.cost_RPone_Purchase}|>*
<|{fixed_variables.cost_RPone_Purchase}|slider|min=35|max=165|active={sm_show_config_scenario}|>

Initial Purchase RP1 : *<|{fixed_variables.Initial_Purchase_RPone}|>*
<|{fixed_variables.Initial_Purchase_RPone}|slider|min=12|max=57|active={sm_show_config_scenario}|>
|>

<|part|render={sm_product_param == 'product_RPtwo'}|
Initial Stock RP2 : *<|{fixed_variables.Initial_Stock_RPtwo}|>*
<|{fixed_variables.Initial_Stock_RPtwo}|slider|min=14|max=66|active={sm_show_config_scenario}|>

Unit Cost - RP2 Stock : *<|{fixed_variables.cost_RPtwo_Stock}|>*
<|{fixed_variables.cost_RPtwo_Stock}|slider|min=21|max=99|active={sm_show_config_scenario}|>

Unit Cost - RP2 Purchase : *<|{fixed_variables.cost_RPtwo_Purchase}|>*
<|{fixed_variables.cost_RPtwo_Purchase}|slider|min=52|max=247|active={sm_show_config_scenario}|>

Initial Purchase RP2 : *<|{fixed_variables.Initial_Purchase_RPtwo}|>*
<|{fixed_variables.Initial_Purchase_RPtwo}|slider|min=14|max=66|active={sm_show_config_scenario}|>
|>
|>

<|Delete|button|on_action=delete_scenario_fct|active={len(scenario_selector)>0 and sm_show_config_scenario}|class_name=fullwidth error mb_half|>
<|Make Primary|button|on_action=make_primary|active={len(scenario_selector)>0 and not selected_scenario.is_primary and sm_show_config_scenario}|class_name=fullwidth secondary mb_half|>
<|Re-optimize|button|on_action=submit_scenario|active={len(scenario_selector)>0 and sm_show_config_scenario}|class_name=fullwidth secondary mb_half|>
<|New scenario|button|on_action=create_new_scenario||active={sm_show_config_scenario}|class_name=fullwidth plain mb_half|>
|>
|>

|>


# Create app for demo-production-planning scenario_manager_md.py
from pages.shared import update_scenario_selector

from taipy.gui import notify, invoke_long_callback, Markdown
import taipy as tp
from config.config import scenario_cfg
import datetime as dt
import pandas as pd

from config.config import fixed_variables_default
from taipy.gui import Icon
import pandas as pd

# Toggle for setting charts
sm_choice_chart = [("pie", Icon("images/icons/pie_chart.svg", "pie")),
                    ("chart", Icon("images/icons/bar_chart.svg", "chart"))]
sm_show_pie = sm_choice_chart[1][0]

sm_results = pd.DataFrame({"Monthly Production FPA":[],
                          "Monthly Stock FPA": [],
                          "Monthly BO FPA": [],
                          "Max Capacity FPA": [],

                          "Monthly Production FPB": [],
                          "Monthly Stock FPB": [],
                          "Monthly BO FPB": [],
                          "Max Capacity FPB": [],

                          "Monthly Stock RP1":[],
                          "Monthly Stock RP2":[],

                          "Monthly Purchase RP1":[],
                          "Monthly Purchase RP2":[],

                          "Demand FPA": [],
                          "Demand FPB": [],

                          'Stock FPA Cost': [],
                          'Stock FPB Cost': [],

                          'Stock RP1 Cost': [],
                          'Stock RP2 Cost': [],

                          'Purchase RP1 Cost': [],
                          'Purchase RP2 Cost': [],

                          "BO FPA Cost":[],
                          "BO FPB Cost":[],

                          "Total Cost": [],
                          "index": []})

pie_results = pd.DataFrame(
        {
            "values": [1] * len(list(sm_results.columns)),
            "labels": list(sm_results.columns)
        }, index=list(sm_results.columns)
        )

chart = sm_results[['index',
                    'Purchase RP1 Cost',
                    'Stock RP1 Cost',
                    'Stock RP2 Cost',
                    'Purchase RP2 Cost',
                    'Stock FPA Cost',
                    'Stock FPB Cost',
                    'BO FPA Cost',
                    'BO FPB Cost',
                    'Total Cost']]

sm_param_selector = ['Capacity Constraints','Objective Weights','Initial Parameters']
sm_param_selected = sm_param_selector[0]

# Toggle for choosing the sliders
sm_choice_product_param = [("product_RPone", Icon("images/P1.png", "product_RPone")),
                    ("product_RPtwo", Icon("images/P2.png", "product_RPtwo")),
                    ("product_FPA", Icon("images/PA.png", "product_FPA")),
                    ("product_FPB", Icon("images/PB.png", "product_FPB"))]
sm_product_param = 'Else'

# Button for configuring scenario
sm_show_config_scenario = True

# Choose the graph to display
sm_graph_selector = [
    'Costs',
    'Purchases',
    'Productions',
    'Stocks',
    'Back Order',
    'Product RP1',
    'Product RP2',
    'Product FPA',
    'Product FPB']
sm_graph_selected = sm_graph_selector[0]

fixed_variables = fixed_variables_default

def make_primary(state):
    tp.set_primary(state.selected_scenario)
    update_scenario_selector(state)
    state.selected_scenario = state.selected_scenario

def delete_scenario_fct(state):
    if state.selected_scenario.is_primary:
        notify(
            state,
            "warning",
            "You can't delete the primary scenario of the month")
    else:
        tp.delete(state.selected_scenario.id)
        update_scenario_selector(state)

        if len(state.scenario_selector) != 0:
            state.selected_scenario = state.scenario_selector[0]

def create_new_scenario(state):
    """
    This function is used whan the 'create' button is pressed in the scenario_manager_md page.
    See the scenario_manager_md page for more information. It will configure another scenario,
    create it and submit it.

    Args:
        state (_type_): the state object of Taipy
    """

    name = f"{dt.datetime.now().strftime('%d-%b-%Y')} Nb : {len(state.scenario_selector)}"
    scenario = tp.create_scenario(scenario_cfg, name=name)
    scenario.properties['user'] = state.state_id

    # update the scenario selector
    update_scenario_selector(state)
    state.selected_scenario = scenario

    submit_scenario(state)

def catch_error_in_submit(state):
    """
    This function is used to catch the error that can occur when we submit a scenario. When an
    error is catched, a notification will appear and variables wil be changed to avoid any error.
    The errors comes from the solution of the Cplex model where infeasible or unbounded problems
    can happen if the fixed variables are wrongly set.

    Args:
        state (_type_): the state object of Taipy
    """

    # if our initial production is higher that our max capacity of production
    if state.fixed_variables["Initial_Production_FPA"] > state.fixed_variables["Max_Capacity_FPA"]:
        state.fixed_variables["Initial_Production_FPA"] = state.fixed_variables["Max_Capacity_FPA"]
        notify(
            state,
            "warning",
            "Value of initial production FPA is greater than max production A")

    # if our initial production is higher that our max capacity of production
    if state.fixed_variables["Initial_Production_FPB"] > state.fixed_variables["Max_Capacity_FPB"]:
        state.fixed_variables["Initial_Production_FPB"] = state.fixed_variables["Max_Capacity_FPB"]
        notify(
            state,
            "warning",
            "Value of initial production FPB is greater than max production B")

    # if our initial stock is higher that our max capacity of production
    if state.fixed_variables["Initial_Stock_RPone"] > state.fixed_variables["Max_Stock_RPone"]:
        state.fixed_variables["Initial_Stock_RPone"] = state.fixed_variables["Max_Stock_RPone"]
        notify(
            state,
            "warning",
            "Value of initial stock RP1 is greater than max stock 1")

    # if our initial stock is higher that our max capacity of production
    if state.fixed_variables["Initial_Stock_RPtwo"] > state.fixed_variables["Max_Stock_RPtwo"]:
        state.fixed_variables["Initial_Stock_RPtwo"] = state.fixed_variables["Max_Stock_RPtwo"]
        notify(
            state,
            "warning",
            "Value of initial stock RP2 is greater than max stock 2")

    # if our initial productions are higher that our max capacity of
    # productions
    if state.fixed_variables["Initial_Production_FPA"] + \
            state.fixed_variables["Initial_Production_FPB"] > state.fixed_variables["Max_Capacity_of_FPA_and_FPB"]:

        state.fixed_variables["Initial_Production_FPA"] = int(state.fixed_variables["Max_Capacity_of_FPA_and_FPB"] / 2)
        state.fixed_variables["Initial_Production_FPB"] = int(state.fixed_variables["Max_Capacity_of_FPA_and_FPB"] / 2)

        notify(
            state,
            "warning",
            "Value of initial productions is greater than the max capacities")

def submit_heavy(scenario):
    tp.submit(scenario)

def submit_status(state, status):
    update_variables(state)

def submit_scenario(state):
    """
    This function will submit the scenario that is selected. It will be used when the 'submit' button is pressed
    or when we create a new scenario. It checks if there is any errors then it will change the parameters of the
    problem and submit the scenario. At the end, we update all the variables that we want to update.

    Args:
        state (_type_): the state object of Taipy

    Returns:
        _type_: _description_
    """

    # see if there are errors in the parameters that will be given to the
    # scenario
    catch_error_in_submit(state)

    # setting the scenario with the right parameters
    old_fixed_variables = state.selected_scenario.fixed_variables.read()
    if old_fixed_variables != state.fixed_variables._dict:
        state.selected_scenario.fixed_variables.write(state.fixed_variables._dict)

    # running the scenario in a long callback and update variables
    invoke_long_callback(state, submit_heavy, [state.selected_scenario], submit_status)

def update_variables(state):
    """This function is only used in the submit_scenario or when the selected_scenario changes. It will update all the useful variables that we want to update.

    Args:
        state (_type_): the state object of Taipy
    """
    # it will set the sliders to the right values when a scenario is changed
    state.fixed_variables = state.selected_scenario.fixed_variables.read()

    # read the result
    state.sm_results = state.selected_scenario.results.read()
    state.pie_results = pd.DataFrame(
        {
            "values": state.sm_results.sum(axis=0),
            "labels": list(state.sm_results.columns)
        })

    state.sum_costs = state.sm_results['Total Cost'].sum()

    bool_costs_of_stock = [c for c in state.sm_results.columns if 'Cost' in c and\
                                                                  'Total' not in c and\
                                                                  'Stock' in c]
    state.sum_costs_of_stock = int(state.sm_results[bool_costs_of_stock].sum(axis=1)\
                                                                        .sum(axis=0))

    bool_costs_of_BO = [c for c in state.sm_results.columns if 'Cost' in c and\
                                                                'Total' not in c and\
                                                                'BO' in c]
    state.sum_costs_of_BO = int(state.sm_results[bool_costs_of_BO].sum(axis=1)\
                                                                  .sum(axis=0))

sm_scenario_manager_md = Markdown('pages/scenario_manager/scenario_manager.md')


# Create app for demo-movie-recommendation ReadMe.md
# Demo Movie Recommendation

## Usage
- [Usage](#usage)
- [Demo Movie Recommendation](#what-is-demo-movie-recommendation)
- [Directory Structure](#directory-structure)
- [License](#license)
- [Installation](#installation)
- [Contributing](#contributing)
- [Code of conduct](#code-of-conduct)

## What is Demo Movie Recommendation

Taipy is a Python library for creating Business Applications. More information on our
[website](https://www.taipy.io).

[Demo Movie Recommendation](https://github.com/Avaiga/demo-movie-recommendation) is a
full application showing how Taipy Core and Taipy Gui can work together to build a simple
but powerful application. This demo shows the basics of search a and recommendation
algorithms. The goal is to be able to search for films and recommend related/similar films.
These recommendations will use the user profile by tracking their session.

Get data [here](https://files.grouplens.org/datasets/movielens/ml-25m.zip).

### Demo Type
- **Level**: Advanced
- **Topic**: Taipy-GUI, Taipy-Core
- **Components/Controls**:
  - Taipy GUI: input, selector, chart, expandable, table
  - Taipy Core: datanode, pipeline, scenario

## How to run

This demo works with a Python version superior to 3.8. Install the dependencies of the
*requirements.txt* and run the *main.py*.

Get data [here](https://files.grouplens.org/datasets/movielens/ml-25m.zip).

## Introduction

A user has a userID generated when the user opens the app. Two pages are created to search and recommend films.

### Search page

- Be able to search for films
- List of films appears after search (selector of movies)
- Clicking on a movie will display a description of said movies, image, ratings, casting, date, ...
    - Possibility to use Imdb api in real time to provide these information
- Recommendation on searched films (not based on syntax but on association with
  this film/similar films Avengers ~ Batman)
- Recommendation depending on liked films of user

### User page

- Possibility to create Data Nodes for tracking/profiling:
    - Selected films
    - Viewed films
    - Liked films
- Possibility to recommand a list of films based on selected/viewed/liked films (constraints on genres)
    - Example: `np.mean([find_similar_movies(movie_id) for movie_id in liked_movies_id])`
- Deduce user profile (favourite genres, favourite period, ...)
 - when a film is selected, add to the set of the user films (a datanode?)

## Directory Structure

- `src/`: Contains the demo source code.
  - `src/algos`: Contains the functions to be executed as tasks by Taipy.
  - `src/config`: Contains the configuration files.
  - `src/data`: Contains the application data files.
  - `src/pages`: Contains the page definition files.
- `CODE_OF_CONDUCT.md`: Code of conduct for members and contributors of _demo-movie-recommendation_.
- `CONTRIBUTING.md`: Instructions to contribute to _demo-movie-recommendation_.
- `INSTALLATION.md`: Instructions to install _demo-movie-recommendation_.
- `LICENSE`: The Apache 2.0 License.
- `Pipfile`: File used by the Pipenv virtual environment to manage project dependencies.
- `README.md`: Current file.

## License
Copyright 2022 Avaiga Private Limited

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
[http://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt)

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

## Installation

Want to install _Demo Movie Recommendation_? Check out our
[`INSTALLATION.md`](INSTALLATION.md) file.

## Contributing

Want to help build _Demo Movie Recommendation_? Check out our
[`CONTRIBUTING.md`](CONTRIBUTING.md) file.

## Code of conduct

Want to be part of the _Demo Movie Recommendation_ community? Check out our
[`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) file.


# Create app for demo-movie-recommendation main.py
from taipy.gui import Gui, Markdown, notify
from pages.search import page_search
from pages.user import page_user

pages = {"/":"<|navbar|>",
         "search":page_search,
         "user":page_user}

if __name__ == "__main__":
    gui = Gui(pages=pages)
    gui.run(port=5006)


# Create app for demo-movie-recommendation algos.py
import pandas as pd
import re
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

def clean_title(title):
    title = re.sub("[^a-zA-Z0-9 ]", "", title)
    return title

def search(title):
    title = clean_title(title)
    query_vec = vectorizer.transform([title])
    similarity = cosine_similarity(query_vec, tfidf).flatten()
    indices = np.argpartition(similarity, -5)[-5:]
    results = movies.iloc[indices].iloc[::-1]

    return results

def find_similar_movies(movie_id):
    similar_users = ratings[(ratings["movieId"] == movie_id) & (ratings["rating"] > 4)]["userId"].unique()
    similar_user_recs = ratings[(ratings["userId"].isin(similar_users)) & (ratings["rating"] > 4)]["movieId"]
    similar_user_recs = similar_user_recs.value_counts() / len(similar_users)

    similar_user_recs = similar_user_recs[similar_user_recs > .10]
    all_users = ratings[(ratings["movieId"].isin(similar_user_recs.index)) & (ratings["rating"] > 4)]
    all_user_recs = all_users["movieId"].value_counts() / len(all_users["userId"].unique())
    rec_percentages = pd.concat([similar_user_recs, all_user_recs], axis=1)
    rec_percentages.columns = ["similar", "all"]

    rec_percentages["score"] = rec_percentages["similar"] / rec_percentages["all"]
    rec_percentages = rec_percentages.sort_values("score", ascending=False)
    return rec_percentages.head(10).merge(movies, left_index=True, right_on="movieId")[["score", "title", "genres"]]

vectorizer = TfidfVectorizer(ngram_range=(1,2))
#movie_id = 89745

ratings = pd.read_csv('data/ratings.csv')

movies = pd.read_csv("data/movies.csv")
movies["clean_title"] = movies["title"].apply(clean_title)
tfidf = vectorizer.fit_transform(movies["clean_title"])

#print(search('Avengers'))
#start=time.time()
#print(find_similar_movies(movie_id))
#print(time.time()-start)


# Create app for demo-movie-recommendation user.py
from taipy.gui import Gui, notify, Markdown

page_user = Markdown("""

""")


# Create app for demo-movie-recommendation search.py
from taipy.gui import Gui, notify, Markdown

from algos.algos import clean_title, search

import pandas as pd

searched_film = ""
selected_film = ""
film_selector = [('','')]

page_search = Markdown("""
""")


# Create app for demo-sales-dashboard main.py
import pandas as pd
from taipy.gui import Gui, notify

# ---- READ EXCEL ----

df = pd.read_excel(
    io="data/supermarkt_sales.xlsx",
    engine="openpyxl",
    sheet_name="Sales",
    skiprows=3,
    usecols="B:R",
    nrows=1000,
)
# Add 'Hour' column to dataframe
df["Hour"] = pd.to_datetime(df["Time"], format="%H:%M:%S").dt.hour

city = cities = list(df["City"].unique())
customer_type = types = list(df["Customer_type"].unique())
gender = genders = list(df["Gender"].unique())

layout = {"margin": {"l": 220}}

page = """
<|toggle|theme|>

<|25 75|layout|gap=30px|
<|sidebar|
## Please **filter**{: .color-primary} here:

<|{city}|selector|lov={cities}|multiple|label=Select the City|dropdown|on_change=on_filter|class_name=fullwidth|>

<|{customer_type}|selector|lov={types}|multiple|label=Select the Customer Type|dropdown|on_change=on_filter|class_name=fullwidth|>

<|{gender}|selector|lov={genders}|multiple|label=Select the Gender|dropdown|on_change=on_filter|class_name=fullwidth|>
|>

<main_page|
# 📊 Sales **Dashboard**{: .color-primary}

<|1 1 1|layout|
<total_sales|
## **Total**{: .color-primary} sales:
US $ <|{int(df_selection["Total"].sum())}|>
|total_sales>

<average_rating|
## Average **Rating**{: .color-primary}:
<|{round(df_selection["Rating"].mean(), 1)}|> <|{"⭐" * int(round(round(df_selection["Rating"].mean(), 1), 0))}|>
|average_rating>

<average_sale|
## Average **Sales**{: .color-primary}:
US $ <|{round(df_selection["Total"].mean(), 2)}|>
|average_sale>
|>

<br/>

<|Sales Table|expandable|not expanded|
<|{df_selection}|table|page_size=5|>
|>

<|card p2|
<|{sales_by_hour}|chart|x=Hour|y=Total|type=bar|title=Sales by Hour|>

<|{sales_by_product_line}|chart|x=Total|y=Product line|type=bar|orientation=h|title=Sales by Product|layout={layout}|>
|>

Get the Taipy Code [here](https://github.com/Avaiga/demo-sales-dashboard) and the original code [here](https://github.com/Sven-Bo/streamlit-sales-dashboard)
|main_page>
|>

"""

def filter(city, customer_type, gender):
    df_selection = df[
        df["City"].isin(city)
        & df["Customer_type"].isin(customer_type)
        & df["Gender"].isin(gender)
    ]

    # SALES BY PRODUCT LINE [BAR CHART]
    sales_by_product_line = (
        df_selection[["Product line", "Total"]]
        .groupby(by=["Product line"])
        .sum()[["Total"]]
        .sort_values(by="Total")
    )
    sales_by_product_line["Product line"] = sales_by_product_line.index

    # SALES BY HOUR [BAR CHART]
    sales_by_hour = (
        df_selection[["Hour", "Total"]].groupby(by=["Hour"]).sum()[["Total"]]
    )
    sales_by_hour["Hour"] = sales_by_hour.index
    return df_selection, sales_by_product_line, sales_by_hour

def on_filter(state):
    if len(state.city) == 0 or len(state.customer_type) == 0 or len(state.gender) == 0:
        notify(state, "Error", "No results found. Check the filters.")
        return

    state.df_selection, state.sales_by_product_line, state.sales_by_hour = filter(
        state.city, state.customer_type, state.gender
    )

if __name__ == "__main__":
    df_selection, sales_by_product_line, sales_by_hour = filter(
        city, customer_type, gender
    )
    Gui(page).run(margin="0em", title="Sales Dashboard")


# Create app for demo-sentiment-analysis main.py
""" Creates a sentiment analysis App using Taipy"""
from transformers import AutoTokenizer
from transformers import AutoModelForSequenceClassification
from scipy.special import softmax

import numpy as np
import pandas as pd
from taipy.gui import Gui, notify

text = "Original text"

page = """
# Getting started with **Taipy**{: .color-primary} **GUI**{: .color-primary}

<|layout|columns=1 1|
<|
**My text:** <|{text}|>

**Enter a word:**
<|{text}|input|>
<|Analyze|button|on_action=local_callback|>
|>

<|Table|expandable|
<|{dataframe}|table|width=100%|number_format=%.2f|>
|>
|>

<|layout|columns=1 1 1|
## Positive <|{np.mean(dataframe['Score Pos'])}|text|format=%.2f|raw|>

## Neutral <|{np.mean(dataframe['Score Neu'])}|text|format=%.2f|raw|>

## Negative <|{np.mean(dataframe['Score Neg'])}|text|format=%.2f|raw|>
|>

<|{dataframe}|chart|type=bar|x=Text|y[1]=Score Pos|y[2]=Score Neu|y[3]=Score Neg|y[4]=Overall|color[1]=green|color[2]=grey|color[3]=red|type[4]=line|>
"""

MODEL = "sbcBI/sentiment_analysis_model"
tokenizer = AutoTokenizer.from_pretrained(MODEL)
model = AutoModelForSequenceClassification.from_pretrained(MODEL)

dataframe = pd.DataFrame(
    {
        "Text": [""],
        "Score Pos": [0.33],
        "Score Neu": [0.33],
        "Score Neg": [0.33],
        "Overall": [0],
    }
)

dataframe2 = dataframe.copy()

def analyze_text(input_text: str) -> dict:
    """
    Runs the sentiment analysis model on the text

    Args:
        - text (str): text to be analyzed

    Returns:
        - dict: dictionary with the scores
    """
    encoded_text = tokenizer(input_text, return_tensors="pt")
    output = model(**encoded_text)
    scores = output[0][0].detach().numpy()
    scores = softmax(scores)

    return {
        "Text": input_text[:50],
        "Score Pos": scores[2],
        "Score Neu": scores[1],
        "Score Neg": scores[0],
        "Overall": scores[2] - scores[0],
    }

def local_callback(state) -> None:
    """
    Analyze the text and updates the dataframe

    Args:
        - state: state of the Taipy App
    """
    notify(state, "Info", f"The text is: {state.text}", True)
    temp = state.dataframe.copy()
    scores = analyze_text(state.text)
    state.dataframe = temp.append(scores, ignore_index=True)
    state.text = ""

path = ""
treatment = 0

page_file = """
<|{path}|file_selector|extensions=.txt|label=Upload .txt file|on_action=analyze_file|> <|{f'Downloading {treatment}%...'}|>

<|Table|expandable|
<|{dataframe2}|table|width=100%|number_format=%.2f|>
|>

<|{dataframe2}|chart|type=bar|x=Text|y[1]=Score Pos|y[2]=Score Neu|y[3]=Score Neg|y[4]=Overall|color[1]=green|color[2]=grey|color[3]=red|type[4]=line|height=600px|>

"""

def analyze_file(state) -> None:
    """
    Analyse the lines in a text file

    Args:
        - state: state of the Taipy App
    """
    state.dataframe2 = dataframe2
    state.treatment = 0
    with open(state.path, "r", encoding="utf-8") as f:
        data = f.read()
        print(data)

        file_list = list(data.split("\n"))

    for i, input_text in enumerate(file_list):
        state.treatment = int((i + 1) * 100 / len(file_list))
        temp = state.dataframe2.copy()
        scores = analyze_text(input_text)
        print(scores)
        state.dataframe2 = temp.append(scores, ignore_index=True)

    state.path = None

pages = {
    "/": "<|toggle|theme|>\n\n<|navbar|>\n",
    "line": page,
    "text": page_file,
}

Gui(pages=pages).run(title="Sentiment Analysis")


# Create app for demo-template test_main.py


# Create app for demo-template test_config.py
# Please insert your unit tests of config code here.


# Create app for demo-template test_algo1.py
# Please insert your unit tests of algos code here.
#
# Here is an example:
#
#
# from .algos.algo1 import algo1_first_function
#
# def test_algo1():
#     assert algo1_first_function() == 10


# Create app for demo-template main.py
# Please insert your main code here.


# Create app for demo-template config.py
from taipy import Config

# Please insert your configuration here.
#
# Here is an example:
#
# conf_path = Path('my', 'config', 'path', 'taipy-config.toml')
# Config.load(str(conf_path))
#
# first_data_node_config = Config.configure_data_node(...)
# second_data_node_config = Config.configure_data_node(...)
#
# task_config = Config.configure_task(...)
#
# scenario_config = Config.configure_scenario(...)


# Create app for demo-template algo2.py
# Please insert your algo as python functions to be translated into taipy tasks/pipelines here.
#
# Here is an example:
#
# def algo2_(params):
#     print("this is my algo 2 function")
#     #


# Create app for demo-template algo1.py
# Please insert your python functions to be translated into taipy tasks here.
#
# Here is an example:
#
# def algo1_first_function(params):
#     print("this is my first function")
#     if __sub_function(params):
#         return 10
#     else:
#         return 20
#
#
# def __sub_function(params):
#     print("this is an internal sub_function")
#     return True
#
#
# def algo1_second_function(other_param: int):
#     print("this is my second function")
#     return other_param + 1


# Create app for demo-template page1.py
# Please insert your code dedicated to pages here.
#


# Create app for demo-image-classification demo-image_classifcation-taipy-cloud.py
import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.utils import to_categorical
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image

class_names =  ['AIRPLANE', 'AUTOMOBILE', 'BIRD', 'CAT', 'DEER', 'DOG', 'FROG', 'HORSE', 'SHIP', 'TRUCK']

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()

x_train = x_train / 255.0
y_train = to_categorical(y_train, len(class_names))

x_test = x_test / 255.0
y_test = to_categorical(y_test, len(class_names))

#########################################################################################################
def create_model():
    model = models.Sequential()
    model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
    model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))
    model.add(layers.MaxPool2D((2,2)))

    model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same',))
    model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same',))
    model.add(layers.MaxPool2D((2,2)))

    model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same',))
    model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same',))
    model.add(layers.MaxPool2D((2,2)))

    model.add(layers.Flatten())
    model.add(layers.Dense(128, activation='relu'))
    model.add(layers.Dense(10, activation='softmax'))

    model.compile(optimizer='adam',
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])

    return model

df = pd.read_csv("saved_models/df.csv")
df["N_Epochs"] = range(1,len(df)+1)

#STATE VARIABLES

model  = None

# Parameters for models & training
epochs = 1
input_model_name = "model"

# Parameters for trained model
trained_model_path = ""

# Parameters for CIFAR dataset
cifar_image_index  = 10
cifar_image_path = "images/sample/taipy.jpg"
cifar_predicted_label = 'NA'
cifar_true_label = 'NA'

# Parameters for online image
online_image_url  = "URL"
online_image_path = "images/sample/airplane.jpg"
online_image_count = 0
online_image_predicted_label  = 'NA' # predicted label for the online image

#P1
from taipy import Gui
from taipy.gui import invoke_long_callback, notify
import urllib

p1 = """

Image Classification CNN

<|layout|columns=1 3|
<|
## PARAMETERS
Enter chosen optimal numper of epochs:
<|{epochs}|input|>

Register model name:
<|{input_model_name}|input|>

Train the model with the Training + Validation sets:
<|START TRAINING|button|on_action=train_button|>

### Upload Trained Model
<|{trained_model_path}|file_selector|label=Upload trained model|on_action=load_trained_model|extensions=.h5|>
|>

<|

 Val_loss and Accuracy

<|{df}|chart|x=N_Epochs|y[1]=accuracy|y[2]=val_accuracy|>
|>
|>
___
"""

def merged_train(model,number_of_epochs,name):
    # merge the training and validation sets
    #x_all = np.concatenate((x_train, x_test))
    #y_all = np.concatenate((y_train, y_test))

    # train with the merged dataset
    #history = model.fit(
    #    datagen.flow(x_all, y_all, batch_size=64),
    #    epochs=number_of_epochs)

    #model.save("saved_models/{}.h5".format(name),save_format='h5')
    print("TRAINING & SAVING COMPLETED!")

def train_button(state):
    notify(state, "info", "Started training model with {} epochs".format(state.epochs), True, 1000)
    #model = create_model()
    invoke_long_callback(state,merged_train,[model, int(state.epochs), state.input_model_name])

def load_trained_model(state):
    loaded_model = tf.keras.models.load_model(state.trained_model_path)
    state.model = loaded_model

#Second half of the applications
p2 = """
<|layout|columns=1 3|
<|
### CIFAR10 Images Prediction
Enter CIFAR10 image index:  |

<|{cifar_image_index}|input|>
<|PREDICT CIFAR IMAGE|button|on_action=predict_cifar_image|>

<|{cifar_image_path}|image|height=100px|width=100px|>

##Predicted label: <|{cifar_predicted_label}|>
##True label: <|{cifar_true_label}|>

|>

<|
###Paste an online image link here for prediction:

<|{online_image_url}|input|on_action=load_online_image|>

 <|{online_image_path}|image|height=300px|width=300px|>

<|PREDICT ONLINE IMAGE|button|on_action=predict_online_image|>

## Predicted label: <|{online_image_predicted_label }|>
|>
|>
"""

def predict_cifar_image(state):
    #Retrieve the cifar image at the specified index and save as PIL Image obj
    cifar_img_idx = int(state.cifar_image_index )
    cifar_img_data = x_test[cifar_img_idx]
    cifar_img = Image.fromarray(np.uint8(cifar_img_data*255))
    cifar_img.save("images/cifar10_saved/{}.jpg".format(cifar_img_idx))

    #Predict the label of the CIFAR image
    img_for_pred = np.expand_dims(x_test[cifar_img_idx], axis=0)
    cifar_img_pred_label = np.argmax(state.model.predict(img_for_pred))
    cifar_img_true_label = y_test[cifar_img_idx].argmax()

    #Update the GUI
    state.cifar_image_path = "images/cifar10_saved/{}.jpg".format(cifar_img_idx)
    state.cifar_predicted_label = str(class_names[cifar_img_pred_label])
    state.cifar_true_label = str(class_names[cifar_img_true_label])

def load_online_image(state):
    urllib.request.urlretrieve(state.online_image_url, "images/online_image.jpg")
    state.online_image_path = "images/online_image.jpg"

def predict_online_image(state):
    #Retrieve & save online image in order to show on the image box
    urllib.request.urlretrieve(state.online_image_url , "images/saved_images/{}.jpg".format(state.online_image_count))
    state.online_image_path = "images/saved_images/{}.jpg".format(state.online_image_count)

    #Predict the label of the online image
    img_array = tf.keras.utils.load_img(state.online_image_path, target_size=(32, 32))
    image = tf.keras.utils.img_to_array(img_array)  # (height, width, channels)
    image = np.expand_dims(image, axis=0) / 255.    # (1, height, width, channels) + normalize

    #Update the GUI
    state.online_image_predicted_label  = class_names[np.argmax(state.model.predict(image))]
    state.online_image_count += 1

Gui(page=p1+p2).run(dark_mode=False)


# Create app for demo-image-classification main.py
import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.utils import to_categorical
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image

class_names =  ['AIRPLANE', 'AUTOMOBILE', 'BIRD', 'CAT', 'DEER', 'DOG', 'FROG', 'HORSE', 'SHIP', 'TRUCK']

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()

x_train = x_train / 255.0
y_train = to_categorical(y_train, len(class_names))

x_test = x_test / 255.0
y_test = to_categorical(y_test, len(class_names))

#########################################################################################################
def create_model():
    model = models.Sequential()
    model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
    model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))
    model.add(layers.MaxPool2D((2,2)))

    model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same',))
    model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same',))
    model.add(layers.MaxPool2D((2,2)))

    model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same',))
    model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same',))
    model.add(layers.MaxPool2D((2,2)))

    model.add(layers.Flatten())
    model.add(layers.Dense(128, activation='relu'))
    model.add(layers.Dense(10, activation='softmax'))

    model.compile(optimizer='adam',
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])

    return model

df = pd.read_csv("saved_models/df.csv")
df["N_Epochs"] = range(1,len(df)+1)

#STATE VARIABLES

model  = None

# Parameters for models & training
epochs = 1
input_model_name = "model"

# Parameters for trained model
trained_model_path = ""

# Parameters for CIFAR dataset
cifar_image_index  = 10
cifar_image_path = "images/sample/taipy.jpg"
cifar_predicted_label = 'NA'
cifar_true_label = 'NA'

# Parameters for online image
online_image_url  = "URL"
online_image_path = "images/sample/airplane.jpg"
online_image_count = 0
online_image_predicted_label  = 'NA' # predicted label for the online image

#P1
from taipy import Gui
from taipy.gui import invoke_long_callback, notify
import urllib

p1 = """

Image Classification CNN

<|layout|columns=1 3|
<|
## PARAMETERS
Enter chosen optimal numper of epochs:
<|{epochs}|input|>

Register model name:
<|{input_model_name}|input|>

Train the model with the Training + Validation sets:
<|START TRAINING|button|on_action=train_button|>

### Upload Trained Model
<|{trained_model_path}|file_selector|label=Upload trained model|on_action=load_trained_model|extensions=.h5|>
|>

<|

 Val_loss and Accuracy

<|{df}|chart|x=N_Epochs|y[1]=accuracy|y[2]=val_accuracy|>
|>
|>
___
"""

def merged_train(model,number_of_epochs,name):
    # merge the training and validation sets
    #x_all = np.concatenate((x_train, x_test))
    #y_all = np.concatenate((y_train, y_test))

    # train with the merged dataset
    #history = model.fit(
    #    datagen.flow(x_all, y_all, batch_size=64),
    #    epochs=number_of_epochs)

    #model.save("saved_models/{}.h5".format(name),save_format='h5')
    print("TRAINING & SAVING COMPLETED!")

def train_button(state):
    notify(state, "info", "Started training model with {} epochs".format(state.epochs), True, 1000)
    #model = create_model()
    invoke_long_callback(state,merged_train,[model, int(state.epochs), state.input_model_name])

def load_trained_model(state):
    loaded_model = tf.keras.models.load_model(state.trained_model_path)
    state.model = loaded_model

#Second half of the applications
p2 = """
<|layout|columns=1 3|
<|
### CIFAR10 Images Prediction
Enter CIFAR10 image index:  |

<|{cifar_image_index}|input|>
<|PREDICT CIFAR IMAGE|button|on_action=predict_cifar_image|>

<|{cifar_image_path}|image|height=100px|width=100px|>

##Predicted label: <|{cifar_predicted_label}|>
##True label: <|{cifar_true_label}|>

|>

<|
###Paste an online image link here for prediction:

<|{online_image_url}|input|on_action=load_online_image|>

 <|{online_image_path}|image|height=300px|width=300px|>

<|PREDICT ONLINE IMAGE|button|on_action=predict_online_image|>

## Predicted label: <|{online_image_predicted_label }|>
|>
|>
"""

def predict_cifar_image(state):
    #Retrieve the cifar image at the specified index and save as PIL Image obj
    cifar_img_idx = int(state.cifar_image_index )
    cifar_img_data = x_test[cifar_img_idx]
    cifar_img = Image.fromarray(np.uint8(cifar_img_data*255))
    cifar_img.save("images/cifar10_saved/{}.jpg".format(cifar_img_idx))

    #Predict the label of the CIFAR image
    img_for_pred = np.expand_dims(x_test[cifar_img_idx], axis=0)
    cifar_img_pred_label = np.argmax(state.model.predict(img_for_pred))
    cifar_img_true_label = y_test[cifar_img_idx].argmax()

    #Update the GUI
    state.cifar_image_path = "images/cifar10_saved/{}.jpg".format(cifar_img_idx)
    state.cifar_predicted_label = str(class_names[cifar_img_pred_label])
    state.cifar_true_label = str(class_names[cifar_img_true_label])

def load_online_image(state):
    urllib.request.urlretrieve(state.online_image_url, "images/online_image.jpg")
    state.online_image_path = "images/online_image.jpg"

def predict_online_image(state):
    #Retrieve & save online image in order to show on the image box
    urllib.request.urlretrieve(state.online_image_url , "images/saved_images/{}.jpg".format(state.online_image_count))
    state.online_image_path = "images/saved_images/{}.jpg".format(state.online_image_count)

    #Predict the label of the online image
    img_array = tf.keras.utils.load_img(state.online_image_path, target_size=(32, 32))
    image = tf.keras.utils.img_to_array(img_array)  # (height, width, channels)
    image = np.expand_dims(image, axis=0) / 255.    # (1, height, width, channels) + normalize

    #Update the GUI
    state.online_image_predicted_label  = class_names[np.argmax(state.model.predict(image))]
    state.online_image_count += 1

Gui(page=p1+p2).run(dark_mode=False)


# Create app for demo-drift-detection main.py
import taipy as tp
from taipy.gui import Gui
import pandas as pd

from configuration.config import scenario_cfg
from pages import *
from pages.Drift.Drift import merge_data

if __name__ == "__main__":
    ref_data = pd.read_csv("data/data_ref.csv")

    tp.Core().run()
    scenario = tp.create_scenario(scenario_cfg)

    ref_selected = "data_ref"
    compare_selected = "data_noisy"

    ref_data = pd.read_csv("data/" + ref_selected + ".csv")
    scenario.reference_data.write(ref_data)

    compare_data = pd.read_csv("data/" + compare_selected + ".csv")
    scenario.compare_data.write(compare_data)

    bp_data, sex_data = merge_data(ref_data, compare_data)

    gui = Gui(page=Drift)
    gui.run(title="Drift Detection")


# Create app for demo-drift-detection config.py
"""
Contain the application's configuration including the scenario configurations.

The configuration is run by the Core service.
"""

from algorithms.algorithms import *

from taipy import Config

reference_data_cfg = Config.configure_data_node("reference_data", "csv")
compare_data_cfg = Config.configure_data_node("compare_data", "csv")
num_cols_cfg = Config.configure_data_node("num_cols")
cat_cols_cfg = Config.configure_data_node("cat_cols")
num_results_cfg = Config.configure_data_node("num_results")
cat_results_cfg = Config.configure_data_node("cat_results")
drift_results_cfg = Config.configure_data_node("drift_results")

detect_numerical_cfg = Config.configure_task(
    id="detect_numerical",
    function=detect_numerical,
    input=[reference_data_cfg],
    output=num_cols_cfg,
)
detect_categorical_cfg = Config.configure_task(
    id="detect_categorical",
    function=detect_categorical,
    input=[reference_data_cfg],
    output=cat_cols_cfg,
)
kolmogorov_cfg = Config.configure_task(
    id="kolmogorov",
    function=kolmogorov,
    input=[compare_data_cfg, reference_data_cfg, num_cols_cfg],
    output=num_results_cfg,
)
chi_squared_cfg = Config.configure_task(
    id="chi_squared",
    function=chi_squared,
    input=[compare_data_cfg, reference_data_cfg, cat_cols_cfg],
    output=cat_results_cfg,
)
collect_results_cfg = Config.configure_task(
    id="collect_results",
    function=collect_results,
    input=[num_results_cfg, cat_results_cfg],
    output=drift_results_cfg,
)

scenario_cfg = Config.configure_scenario(
    id="drift_detection",
    task_configs=[
        detect_numerical_cfg,
        detect_categorical_cfg,
        kolmogorov_cfg,
        chi_squared_cfg,
        collect_results_cfg,
    ],
)


# Create app for demo-drift-detection __init__.py
from .config import *


# Create app for demo-drift-detection algorithms.py
"""
This file is designed to contain the various Python functions used to configure tasks.

The functions will be imported by the __init__.py file in this folder.
"""

import pandas as pd
import scipy.stats as stats

def detect_categorical(dataset: pd.DataFrame) -> list:
    """
    Detect the names of categorical columns in a dataframe.

    Args:
        dataset: The dataframe to detect categorical columns from.

    Returns:
        A list of categorical column names.
    """
    categorical = []
    for col in dataset.columns:
        if dataset[col].dtype == "object":
            categorical.append(col)
    return categorical

def detect_numerical(dataset: pd.DataFrame) -> list:
    """
    Detect the names of numerical columns in a dataframe.

    Args:
        dataset: The dataframe to detect numerical columns from.

    Returns:
        A list of numerical column names.
    """
    numerical = []
    for col in dataset.columns:
        if dataset[col].dtype != "object":
            numerical.append(col)
    return numerical

def ks_2samp(series_1: pd.Series, series_2: pd.Series) -> float:
    """
    Runs the two-sample Kolmogorov-Smirnov test on two series.

    Args:
        series_1: The first series.
        series_2: The second series.

    Returns:
        The p-value of the test.
    """
    analysis = stats.ks_2samp(series_1, series_2)
    return int(analysis[1] * 100) / 100

def kolmogorov(
    dataset: pd.DataFrame, ref_dataset: pd.DataFrame, num_cols: list
) -> dict:
    """
    Runs the two-sample Kolmogorov-Smirnov test on all numerical columns in a dataframe.

    Args:
        dataset: The dataframe to run the test on.
        ref_dataset: The reference dataframe to compare against.
        num_cols: The list of numerical column names.

    Returns:
        A dictionary of test statistics.
    """
    ks_dict = {}
    for col in num_cols:
        ks_dict[col] = ks_2samp(dataset[col], ref_dataset[col])
    return ks_dict

def chi_squared_2samp(series_1: pd.Series, series_2: pd.Series) -> float:
    """
    Runs the two-sample chi-squared test on two series.

    Args:
        series_1: The first series.
        series_2: The second series.

    Returns:
        The p-value of the test.
    """
    # Get the unique values
    series_1_unique = series_1.unique()
    series_2_unique = series_2.unique()
    # Get the frequencies
    series_1_freq = series_1.value_counts()
    series_2_freq = series_2.value_counts()
    # Get the expected frequencies
    series_1_exp_freq = []
    series_2_exp_freq = []
    for i, _ in enumerate(series_1_unique):
        series_1_exp_freq.append(
            series_1_freq[series_1_unique[i]] * len(series_2) / len(series_1)
        )
    for i, _ in enumerate(series_2_unique):
        series_2_exp_freq.append(
            series_2_freq[series_2_unique[i]] * len(series_1) / len(series_2)
        )
    analysis = stats.chisquare(series_1_exp_freq, series_2_exp_freq)
    return int(analysis[1] * 100) / 100

def chi_squared(
    dataset: pd.DataFrame, ref_dataset: pd.DataFrame, cat_cols: list
) -> dict:
    """
    Runs the chi-squared test on all categorical columns in a dataframe.

    Args:
        dataset: The dataframe to run the test on.
        ref_dataset: The reference dataframe to compare against.
        cat_cols: The list of categorical column names.

    Returns:
        A dictionary of test statistics.
    """
    chi_dict = {}
    for col in cat_cols:
        chi_dict[col] = chi_squared_2samp(dataset[col], ref_dataset[col])
    return chi_dict

def collect_results(num_results: dict, cat_results: dict) -> pd.DataFrame:
    """
    Collects the results of the two tests into a single dictionary.

    Args:
        num_results: The dictionary of numerical test results.
        cat_results: The dictionary of categorical test results.

    Returns:
        A dataframe of the results.
    """
    columns = []
    tests = []
    values = []
    detected = []
    for col in cat_results:
        columns.append(col)
        tests.append("Chi-Squared")
        values.append(cat_results[col])
        if cat_results[col] < 0.05:
            detected.append(True)
        else:
            detected.append(False)
    for col in num_results:
        columns.append(col)
        tests.append("Kolmogorov-Smirnov")
        values.append(num_results[col])
        if num_results[col] < 0.05:
            detected.append(True)
        else:
            detected.append(False)
    results = pd.DataFrame(
        {"Column": columns, "Test": tests, "p-value": values, "Drift": detected}
    )
    return results

def merge_data(ref_data: pd.DataFrame, compare_data: pd.DataFrame):
    """
    Merges the reference and comparison data into a single dataframe.
    The Dataframe is prepared for plotting.

    Args:
        ref_data: The reference data.
        compare_data: The comparison data.

    Returns:
        plot_data: The dataset for other columns.
        sex_data: The dataset for sex distribution.
    """
    bp_data = [
        {"Blood Pressure": list(ref_data["blood_pressure"])},
        {"Blood Pressure": list(compare_data["blood_pressure"])},
    ]
    # Count the Male and Female rows in ref and compare
    male_ref = ref_data[ref_data["sex"] == "Male"].shape[0]
    male_compare = compare_data[compare_data["sex"] == "Male"].shape[0]
    female_ref = ref_data[ref_data["sex"] == "Female"].shape[0]
    female_compare = compare_data[compare_data["sex"] == "Female"].shape[0]
    sex_data = pd.DataFrame(
        {
            "Dataset": ["Ref", "Compare"],
            "Male": [male_ref, male_compare],
            "Female": [female_ref, female_compare],
        }
    )
    return bp_data, sex_data


# Create app for demo-drift-detection __init__.py
from algorithms import *


# Create app for demo-drift-detection root.md
<center>
<|navbar|>
</center>


# Create app for demo-drift-detection __init__.py
from .root import root_page
from .Drift.Drift import Drift


# Create app for demo-drift-detection root.py
"""
The rootpage of the application.
Page content is imported from the root.md file.

Please refer to https://docs.taipy.io/en/latest/manuals/gui/pages for more details.
"""

from taipy.gui import Markdown

root_page = Markdown("pages/root.md")