diff --git a/apparun/gui/modules.py b/apparun/gui/modules.py index 74f1130..c340904 100644 --- a/apparun/gui/modules.py +++ b/apparun/gui/modules.py @@ -1,15 +1,25 @@ from __future__ import annotations +import io +import re import subprocess -from typing import Annotated, Callable, List, Optional, TypeVar, Union +from typing import Annotated, Any, Callable, Dict, List, Optional, TypeVar, Union import pandas as pd +import requests import streamlit as st -from pydantic import BaseModel, Field +from PIL import Image +from pydantic import BaseModel, Field, field_validator -from apparun.gui.panels.base import InputScenarioFormPanel -from apparun.gui.panels.output_dynamic import ScenarioComparisonDynamicOutputPanel -from apparun.gui.panels.output_static import Markdown +from apparun.gui.panels.base import * +from apparun.gui.panels.base import ( + InputPanel, + OutputPanel, + get_input_panel, + get_output_panel, +) +from apparun.gui.panels.output_dynamic import * +from apparun.gui.panels.output_static import * from apparun.impact_model import ImpactModel PandasDataFrame = TypeVar("pd.core.frame.DataFrame") @@ -19,19 +29,29 @@ class Module(BaseModel): name: Optional[str] = None lca_data_path: Optional[str] = None impact_model_path: Optional[str] = None - input_panel: Optional[ - Annotated[InputScenarioFormPanel, Field(discriminator="type")] - ] = None - output_panels: List[ - Annotated[ - Union[ScenarioComparisonDynamicOutputPanel, Markdown], - Field(discriminator="type"), - ] - ] + input_panel: Optional[InputPanel] = None + output_panels: List[OutputPanel] cols: Optional[Callable] = None lca_data: Optional[PandasDataFrame] = None impact_model: Optional[ImpactModel] = None + @field_validator("input_panel", mode="before") + @classmethod + def build_input_panel( + cls, panel: Optional[Dict[Any]] = None + ) -> Optional[InputPanel]: + if panel is None: + return None + return get_input_panel(panel["type"])(**panel) + + @field_validator("output_panels", mode="before") + @classmethod + def build_output_panels(cls, panels: List[Dict[Any]]) -> List[OutputPanel]: + return [ + get_output_panel(output_panel["type"])(**output_panel) + for output_panel in panels + ] + def __init__(self, **args): super().__init__(**args) self.lca_data = ( @@ -77,14 +97,26 @@ def run(self): else: with self.output_col: for output_panel in self.output_panels: - output_panel.run() + output_panel.run( + impact_model=self.impact_model, lca_data=self.lca_data + ) class GUI(BaseModel): name: Optional[str] = None + favicon_path: Optional[str] = None modules: List[Module] def setup_layout(self): + favicon = None + if self.favicon_path is not None: + if re.match(r"https?://.*", self.favicon_path): + favicon = Image.open( + io.BytesIO(requests.get(self.favicon_path, stream=True).content) + ) + else: + favicon = Image.open(self.favicon_path) + st.set_page_config(page_title=self.name, page_icon=favicon) st.html( """