Avaiga / taipy

Turns Data and AI algorithms into production-ready web applications in no time.
https://www.taipy.io
Apache License 2.0
17.05k stars 1.86k forks source link

[🐛 BUG] 4.0.1.dev1, reading a data node changes the display of a table #2217

Open AlexandreSajus opened 3 weeks ago

AlexandreSajus commented 3 weeks ago

What went wrong? 🤔

When running https://github.com/Avaiga/demo-sap with 4.0.1.dev1, creating a new scenario will change the display of the input data node table from this: image To this: image

The only way to fix this is by refreshing the page.

Can someone help me investigate? I tried isolating the issue, but I did not figure out a way.

Steps to Reproduce Issue

  1. A code fragment
  2. And/or configuration files or code
  3. And/or Taipy GUI Markdown or HTML files

Screenshots

DESCRIPTION

Browsers

Chrome

OS

Windows

Version of Taipy

4.0.1.dev1

Acceptance Criteria

Code of Conduct

AlexandreSajus commented 3 weeks ago

Probably linked to #2082

jrobinAV commented 2 weeks ago

It should be tested on 4.0.1. and develop.

@AlexandreSajus did you test it?

AlexandreSajus commented 2 weeks ago

I'll test it

AlexandreSajus commented 2 weeks ago

Yes, this still happens both in 4.0.1 and develop

jrobinAV commented 2 weeks ago

@AlexandreSajus Thank you.

FredLL-Avaiga commented 16 hours ago

@AlexandreSajus Can you create a small reproduction example ?

FredLL-Avaiga commented 13 hours ago

Reading your code @AlexandreSajus , it looks like the table is not a datanode_viewer data rab but a simple Gui table.

FredLL-Avaiga commented 13 hours ago

I tried to reproduce your issue with a simplified version of your code, but I don't see the behavior you're reporting

import typing as t

import pandas as pd

import taipy as tp
import taipy.gui.builder as tgb
from taipy.gui import Gui, State

item_codes = ["A00001", "A00003", "B10000", "P10003"]

item_descriptions = {
    "A00001": "J.B. Officeprint 1420",
    "A00002": "J.B. Officeprint 1111",
    "A00003": "J.B. Officeprint 1186",
    "B10000": "Printer Label",
    "P10003": "PC Set 1",
}

# Standard scenario inputs
inputs = pd.DataFrame(
    {
        "Item Code": item_codes,
        "Item Description": [item_descriptions[item] for item in item_codes],
        "Expectation Factor": [1, 1.2, 1, 1.5],
        "Production Time (days)": [5, 2, 3, 4],
        "Arrival Time (days)": [1, 2, 1, 1],
        "Minimal Stock": [100, 200, 150, 200],
    }
)

default_inputs = pd.DataFrame(
    {
        "Item Code": item_codes,
        "Item Description": [item_descriptions[item] for item in item_codes],
        "Expectation Factor": [1, 1, 1, 1],
        "Production Time (days)": [1, 1, 1, 1],
        "Arrival Time (days)": [1, 1, 1, 1],
        "Minimal Stock": [0, 0, 0, 0],
    }
)

inputs_cfg = tp.Config.configure_data_node("inputs")
recommendation_df_cfg = tp.Config.configure_data_node("recommendation_df")

def recommendation(inputs: pd.DataFrame) -> t.List[t.Union[dict, dict, pd.DataFrame]]:
    return [inputs]

recommendation_task_cfg = tp.Config.configure_task(
    "recommendation",
    function=recommendation,
    input=[inputs_cfg],
    output=[
        recommendation_df_cfg,
    ],
)

scenario_cfg = tp.Config.configure_scenario(
    id="scenario", task_configs=[recommendation_task_cfg]
)

def update_outputs(state: State) -> None:
    state.recommendation_df = state.scenario.recommendation_df.read()

def scenario_submitted(state: State, submittable: str, details: dict) -> None:
    """
    Apply changes to the app when a scenario is submitted.

    Args:
        state: State of the Taipy app.
    """
    if details["submission_status"] == "COMPLETED":
        update_outputs(state)

def change_scenario(state: State) -> None:
    """
    Update the app when the user selects a scenario.

    Args:
        state: State of the Taipy app.
    """
    if state.scenario.inputs.read() is None:
        state.scenario.inputs.write(default_inputs)
    state.inputs = state.scenario.inputs.read()
    update_outputs(state)

with tgb.Page() as page:
    tgb.text("# *Inventory Management* - Forecast **Scenarios**", mode="md")
    tgb.html("hr")

    with tgb.layout("20 80", columns__mobile="1"):
        with tgb.part("sidebar"):
            tgb.text("**Create** and select scenarios", mode="md")
            tgb.scenario_selector("{scenario}", on_change=change_scenario)

        with tgb.part("main"):
            tgb.text(
                """Change **parameters** to create different scenarios:
- **Expectation Factor**: how much more demand is expected compared to last month,
- **Production Time**: how many days it takes to produce the order,
- **Arrival Time**: how many days it takes to receive the order,
- **Minimal Stock**: the minimum stock level to maintain.""",
                mode="md",
            )
            tgb.html("br")
            tgb.table(
                data="{inputs}",
                editable=True,
                show_all=True,
                on_add=False,
                on_delete=False,
            )
            tgb.scenario(
                "{scenario}",
                show_sequences=False,
                expanded=False,
                on_submission_change=scenario_submitted,
            )

if __name__ == "__main__":
    tp.Orchestrator().run()

    scenario = tp.create_scenario(scenario_cfg)
    t.cast(tp.DataNode, scenario.inputs).write(inputs)
    input_dn = scenario.inputs
    tp.submit(scenario)

    Gui(page=page).run(title="2217 Datanode table change layout on scenario submit")
FredLL-Avaiga commented 13 hours ago

I don't see, in your code, something that I would have removed and that could cause a table refresh... Any hint @AlexandreSajus ?

AlexandreSajus commented 7 hours ago

Reading your code @AlexandreSajus , it looks like the table is not a datanode_viewer data rab but a simple Gui table.

Yes this is intended

AlexandreSajus commented 7 hours ago

I don't see, in your code, something that I would have removed and that could cause a table refresh... Any hint @AlexandreSajus ?

I don't know either. I spent a few hours trying to create a small reproducible code example, but it seemed to depend on multiple parts of the code, and I could not reproduce the issue in a smaller example. What is the protocol here? Should we close this one and reopen this if we get a similar issue later?