reflex-dev / reflex

🕸️ Web apps in pure Python 🐍
https://reflex.dev
Apache License 2.0
19.83k stars 1.14k forks source link

Computed vars at runtime #751

Closed BogdanCatana28 closed 4 months ago

BogdanCatana28 commented 1 year ago

Describe the bug raise TypeError( TypeError: State vars must be primitive Python types, or subclasses of pc.Base. Got var of type <class 'pydantic.main.ModelMetaclass'>. I think there is an issue with the processing of computed vars at runtime when you want them passed as inputs for other components(methods)

To Reproduce Steps to reproduce the behavior:

import pynecone as pc
import plotly.express as px
import pynecone as pc
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

class State(pc.State):
    """The base state for the app."""

    username: str
    logged_in: bool = False

class Dream(pc.Model, table=True):
    username: str
    energy_level: str
    duration: str
    stress_level: str

class DurationChart:

    def get_data(self) -> pd.DataFrame:
        with pc.session() as session:
            data = [dream.duration for dream in
                    session.query(Dream.duration).filter(Dream.username.contains(""))]
            data_len = len(data)
        return pd.DataFrame({'Duration': data, 'Index': range(data_len)})

class EnergyChart(State):

    def get_data(self) -> pd.DataFrame:
        with pc.session() as session:
            data = [dream.energy_level for dream in
                    session.query(Dream.energy_level).filter(Dream.username.contains(self.username))]
            data_len = len(data)
        return pd.DataFrame({'Energy level': data, 'Index': range(data_len)})

class StressChart(State):

    def get_data(self) -> pd.DataFrame:
        with pc.session() as session:
            data = [dream.stress_level for dream in
                    session.query(Dream.stress_level).filter(Dream.username.contains(self.username))]
            data_len = len(data)
        return pd.DataFrame({'Stress level': data, 'Index': range(data_len)})

class ChartsState(State):
    """The app state."""

    category: str = "by Duration"

    @pc.var
    def factory_chart(self):

        categories = {
            "by Duration": DurationChart,
            "by Energy level": EnergyChart,
            "by Stress level": StressChart,
        }

        return categories[self.category]

    @pc.var
    def chart_gen(self) -> go.Figure:
        """The chart figure."""

        if self.category and self.category == "by Duration":
            chart = self.factory_chart
            data = chart.get_data.fn(self)
            return px.line(data, x='Index', y='Duration', title='Your sleep duration chart')

        if self.category and self.category == "by Energy level":
            chart = self.factory_chart("by Energy level")
            data = chart.get_data.fn(self)
            return px.line(data, x='Index', y='Energy level', title='Your sleep energy level chart')

        if self.category and self.category == "by Stress level":
            chart = self.factory_chart("by Stress level")
            data = chart.get_data.fn(self)
            return px.line(data, x='Index', y='Stress level', title='Your sleep stress level chart')

def index():
    return pc.center(
        pc.vstack(
            pc.heading("Please select what chart do you want to view:", font_size="3em"),
            pc.vstack(
                pc.hstack(
                    pc.select(
                        ["by Duration", "by Energy level", "by Stress level"],
                        placeholder="Select a category.",
                        on_change=ChartsState.set_category,
                    ),
                ),
                pc.plotly(data=ChartsState.chart_gen, layout={"width": "1000", "height": "600"}),
                bg="white",
                padding="2em",
                shadow="lg",
                border_radius="lg",
            ),
        ),
        width="100%",
        height="100vh",
        background="radial-gradient(circle at 22% 11%,rgba(62, 180, 137,.20),hsla(0,0%,100%,0) 19%),radial-gradient(circle at 82% 25%,rgba(33,150,243,.18),hsla(0,0%,100%,0) 35%),radial-gradient(circle at 25% 61%,rgba(250, 128, 114, .28),hsla(0,0%,100%,0) 55%)",
    )

# Add state and page to the app.
app = pc.App(state=State)
app.add_page(index)
app.compile()

Expected behavior I am trying to implement the factory method design pattern so to do this, i am writing the 3 classes that return me the dataFrame objects which i will use in the chart_gen method in my ChartsState class where i just want to create an object based on those 3 classes using the factory_chart method and then call the method from the specific class to pass the data it is returning to the plotly to display a chart

Specifics (please complete the following information):

Additional context Add any other context about the problem here.

Alek99 commented 1 year ago

Ok thanks for pointing this out looking into it

joeysumi commented 1 year ago

I've found that this happens when I try to run:

 pc.chart(
      pc.bar(
          data=pc.data(
              "bar",
              x=state.chart_months,
              y=state.chart_values,
          ),
      ),

It doesn't process the variables that I've set for state.chart_months and state.chart_values unless I make them static values (i.e. x=['Jan'], y=[13]

picklelo commented 4 months ago

Our graphing structure has changed now - closing this issue for now.