From 56a159025642c290c4e09e9f1b111b668cb6d5d3 Mon Sep 17 00:00:00 2001 From: trgiangdo Date: Tue, 25 Jun 2024 11:29:11 +0700 Subject: [PATCH 01/10] fix: support pandas series signature in _convert_data_to_dataframe() --- taipy/core/data/parquet.py | 8 ++++---- taipy/core/data/sql_table.py | 12 +++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/taipy/core/data/parquet.py b/taipy/core/data/parquet.py index 6e968c5966..9c80856cbd 100644 --- a/taipy/core/data/parquet.py +++ b/taipy/core/data/parquet.py @@ -214,10 +214,10 @@ def write_with_kwargs(self, data: Any, job_id: Optional[JobId] = None, **write_k } kwargs.update(self.properties[self.__WRITE_KWARGS_PROPERTY]) kwargs.update(write_kwargs) - if isinstance(data, pd.Series): - df = pd.DataFrame(data) - else: - df = self._convert_data_to_dataframe(self.properties[self._EXPOSED_TYPE_PROPERTY], data) + + df = self._convert_data_to_dataframe(self.properties[self._EXPOSED_TYPE_PROPERTY], data) + if isinstance(df, pd.Series): + df = pd.DataFrame(df) # Ensure that the columns are strings, otherwise writing will fail with pandas 1.3.5 df.columns = df.columns.astype(str) diff --git a/taipy/core/data/sql_table.py b/taipy/core/data/sql_table.py index 52fcad95f9..e1326e0627 100644 --- a/taipy/core/data/sql_table.py +++ b/taipy/core/data/sql_table.py @@ -10,7 +10,7 @@ # specific language governing permissions and limitations under the License. from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional, Set +from typing import Any, Dict, List, Optional, Set, Union import pandas as pd from sqlalchemy import MetaData, Table @@ -146,8 +146,14 @@ def __insert_dicts(cls, data: List[Dict], table: Any, connection: Any, delete_ta connection.execute(table.insert(), data) @classmethod - def __insert_dataframe(cls, df: pd.DataFrame, table: Any, connection: Any, delete_table: bool) -> None: - cls.__insert_dicts(df.to_dict(orient="records"), table, connection, delete_table) + def __insert_dataframe( + cls, df: Union[pd.DataFrame, pd.Series], table: Any, connection: Any, delete_table: bool + ) -> None: + if isinstance(df, pd.Series): + data = [df.to_dict()] + elif isinstance(df, pd.DataFrame): + data = df.to_dict(orient="records") + cls.__insert_dicts(data, table, connection, delete_table) @classmethod def __delete_all_rows(cls, table: Any, connection: Any, delete_table: bool) -> None: From d3e83f5b75ca2a2361da3b738d3971a79207f294 Mon Sep 17 00:00:00 2001 From: trgiangdo Date: Tue, 25 Jun 2024 11:29:39 +0700 Subject: [PATCH 02/10] fix: header should be provided with a bool value --- taipy/core/data/csv.py | 4 ++-- taipy/core/data/excel.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/taipy/core/data/csv.py b/taipy/core/data/csv.py index e5d5435c88..472f77b4dc 100644 --- a/taipy/core/data/csv.py +++ b/taipy/core/data/csv.py @@ -188,7 +188,7 @@ def _write(self, data: Any): ) else: self._convert_data_to_dataframe(exposed_type, data).to_csv( - self._path, index=False, encoding=self.properties[self.__ENCODING_KEY], header=None + self._path, index=False, encoding=self.properties[self.__ENCODING_KEY], header=False ) def write_with_column_names(self, data: Any, columns: Optional[List[str]] = None, job_id: Optional[JobId] = None): @@ -201,6 +201,6 @@ def write_with_column_names(self, data: Any, columns: Optional[List[str]] = None """ df = self._convert_data_to_dataframe(self.properties[self._EXPOSED_TYPE_PROPERTY], data) if columns and isinstance(df, pd.DataFrame): - df.columns = columns + df.columns = pd.Index(columns, dtype="object") df.to_csv(self._path, index=False, encoding=self.properties[self.__ENCODING_KEY]) self.track_edit(timestamp=datetime.now(), job_id=job_id) diff --git a/taipy/core/data/excel.py b/taipy/core/data/excel.py index 92f201df6a..d9ee54fa31 100644 --- a/taipy/core/data/excel.py +++ b/taipy/core/data/excel.py @@ -301,7 +301,7 @@ def __write_excel_with_multiple_sheets(self, data: Any, columns: List[str] = Non if columns: data[key].columns = columns - df.to_excel(writer, key, index=False, header=self.properties[self._HAS_HEADER_PROPERTY] or None) + df.to_excel(writer, key, index=False, header=self.properties[self._HAS_HEADER_PROPERTY] or False) def _write(self, data: Any): if isinstance(data, Dict): From 013faf93ec7ed3988cfa29291545e1038621f0dd Mon Sep 17 00:00:00 2001 From: Dinh Long Nguyen Date: Tue, 25 Jun 2024 19:21:44 +0700 Subject: [PATCH 03/10] root page decouple (#1443) * root page decouple * fix resource_handler id --- frontend/taipy-gui/base/src/app.ts | 2 +- taipy/gui/custom/_page.py | 4 ++-- taipy/gui/gui.py | 9 +++++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/frontend/taipy-gui/base/src/app.ts b/frontend/taipy-gui/base/src/app.ts index 3368757a95..aa35787577 100644 --- a/frontend/taipy-gui/base/src/app.ts +++ b/frontend/taipy-gui/base/src/app.ts @@ -162,7 +162,7 @@ export class TaipyApp { if (!path || path === "") { path = window.location.pathname.slice(1); } - sendWsMessage(this.socket, "GMC", "get_module_context", { path: path }, this.clientId); + sendWsMessage(this.socket, "GMC", "get_module_context", { path: path || "/" }, this.clientId); } trigger(actionName: string, triggerId: string, payload: Record = {}) { diff --git a/taipy/gui/custom/_page.py b/taipy/gui/custom/_page.py index 438dfd7138..f23837e53c 100644 --- a/taipy/gui/custom/_page.py +++ b/taipy/gui/custom/_page.py @@ -46,13 +46,13 @@ class ResourceHandler(ABC): User can implement this class to provide custom resources for the custom pages """ - id: str = "" + rh_id: str = "" def __init__(self) -> None: _ExternalResourceHandlerManager().register(self) def get_id(self) -> str: - return self.id if id != "" else str(id(self)) + return self.rh_id if self.rh_id != "" else str(id(self)) @abstractmethod def get_resources(self, path: str, taipy_resource_path: str) -> t.Any: diff --git a/taipy/gui/gui.py b/taipy/gui/gui.py index 0aa8c52f55..7616d50465 100644 --- a/taipy/gui/gui.py +++ b/taipy/gui/gui.py @@ -1111,10 +1111,13 @@ def __request_var_update(self, payload: t.Any): def __handle_ws_get_module_context(self, payload: t.Any): if isinstance(payload, dict): + page_path = str(payload.get("path")) + if page_path in {"/", ""}: + page_path = Gui.__root_page_name # Get Module Context - if mc := self._get_page_context(str(payload.get("path"))): + if mc := self._get_page_context(page_path): self._bind_custom_page_variables( - self._get_page(str(payload.get("path")))._renderer, self._get_client_id() + self._get_page(page_path)._renderer, self._get_client_id() ) self.__send_ws( { @@ -2149,6 +2152,8 @@ def _get_page(self, page_name: str): def _bind_custom_page_variables(self, page: CustomPage, client_id: t.Optional[str]): """Handle the bindings of custom page variables""" + if not isinstance(page, CustomPage): + return with self.get_flask_app().app_context() if has_app_context() else contextlib.nullcontext(): # type: ignore[attr-defined] self.__set_client_id_in_context(client_id) with self._set_locals_context(page._get_module_name()): From 0e9e9bf76f5a61df9e7868416ec7c22cbd5a1c30 Mon Sep 17 00:00:00 2001 From: Dinh Long Nguyen Date: Tue, 25 Jun 2024 20:08:41 +0700 Subject: [PATCH 04/10] fix decouple metadata (#1446) * fix decouple metadata * per Fred + refactor * per Fred --- frontend/taipy-gui/base/src/app.ts | 4 +++- frontend/taipy-gui/base/src/index.ts | 1 - frontend/taipy-gui/base/src/wsAdapter.ts | 12 +++++++++--- frontend/taipy-gui/src/components/Taipy/Navigate.tsx | 6 +----- taipy/gui/gui.py | 11 ++++++++--- taipy/gui/server.py | 1 - 6 files changed, 21 insertions(+), 14 deletions(-) diff --git a/frontend/taipy-gui/base/src/app.ts b/frontend/taipy-gui/base/src/app.ts index aa35787577..9a9447960b 100644 --- a/frontend/taipy-gui/base/src/app.ts +++ b/frontend/taipy-gui/base/src/app.ts @@ -24,6 +24,7 @@ export class TaipyApp { appId: string; clientId: string; context: string; + metadata: Record; path: string | undefined; routes: Route[] | undefined; wsAdapters: WsAdapter[]; @@ -41,6 +42,7 @@ export class TaipyApp { this.functionData = undefined; this.clientId = ""; this.context = ""; + this.metadata = {}; this.appId = ""; this.routes = undefined; this.path = path; @@ -175,7 +177,7 @@ export class TaipyApp { } getPageMetadata() { - return JSON.parse(localStorage.getItem("tp_cp_meta") || "{}"); + return this.metadata; } } diff --git a/frontend/taipy-gui/base/src/index.ts b/frontend/taipy-gui/base/src/index.ts index 6e814f0693..a0c111a622 100644 --- a/frontend/taipy-gui/base/src/index.ts +++ b/frontend/taipy-gui/base/src/index.ts @@ -7,5 +7,4 @@ export type { OnChangeHandler, OnInitHandler, ModuleData }; window.addEventListener("beforeunload", () => { document.cookie = "tprh=;path=/;Max-Age=-99999999;"; - localStorage.removeItem("tp_cp_meta"); }); diff --git a/frontend/taipy-gui/base/src/wsAdapter.ts b/frontend/taipy-gui/base/src/wsAdapter.ts index cad2ca718a..09b446c525 100644 --- a/frontend/taipy-gui/base/src/wsAdapter.ts +++ b/frontend/taipy-gui/base/src/wsAdapter.ts @@ -43,9 +43,15 @@ export class TaipyWsAdapter extends WsAdapter { taipyApp.clientId = id; taipyApp.updateContext(taipyApp.path); } else if (message.type === "GMC") { - const mc = (message.payload as Record).data as string; - window.localStorage.setItem("ModuleContext", mc); - taipyApp.context = mc; + const payload = message.payload as Record; + taipyApp.context = payload.context as string; + if (payload?.metadata) { + try { + taipyApp.metadata = JSON.parse((payload.metadata as string) || "{}"); + } catch (e) { + console.error("Error parsing metadata from Taipy Designer", e); + } + } } else if (message.type === "GDT") { const payload = message.payload as Record; const variableData = payload.variable; diff --git a/frontend/taipy-gui/src/components/Taipy/Navigate.tsx b/frontend/taipy-gui/src/components/Taipy/Navigate.tsx index ff5a2e6cdc..dc8a946365 100644 --- a/frontend/taipy-gui/src/components/Taipy/Navigate.tsx +++ b/frontend/taipy-gui/src/components/Taipy/Navigate.tsx @@ -27,7 +27,7 @@ const Navigate = ({ to, params, tab, force }: NavigateProps) => { const { dispatch, state } = useContext(TaipyContext); const navigate = useNavigate(); const location = useLocation(); - const SPECIAL_PARAMS = ["tp_reload_all", "tp_reload_same_route_only", "tprh", "tp_cp_meta"]; + const SPECIAL_PARAMS = ["tp_reload_all", "tp_reload_same_route_only", "tprh"]; useEffect(() => { if (to) { @@ -65,10 +65,6 @@ const Navigate = ({ to, params, tab, force }: NavigateProps) => { if (tprh !== undefined) { // Add a session cookie for the resource handler id document.cookie = `tprh=${tprh};path=/;`; - const meta = params?.tp_cp_meta; - if (meta !== undefined) { - localStorage.setItem("tp_cp_meta", meta); - } navigate(0); } } diff --git a/taipy/gui/gui.py b/taipy/gui/gui.py index 7616d50465..5015fe2435 100644 --- a/taipy/gui/gui.py +++ b/taipy/gui/gui.py @@ -1116,13 +1116,19 @@ def __handle_ws_get_module_context(self, payload: t.Any): page_path = Gui.__root_page_name # Get Module Context if mc := self._get_page_context(page_path): + page_renderer = self._get_page(page_path)._renderer self._bind_custom_page_variables( - self._get_page(page_path)._renderer, self._get_client_id() + page_renderer, self._get_client_id() ) + # get metadata if there is one + metadata: t.Dict[str, t.Any] = {} + if hasattr(page_renderer, "_metadata"): + metadata = getattr(page_renderer, "_metadata", {}) + meta_return = json.dumps(metadata, cls=_TaipyJsonEncoder) if metadata else None self.__send_ws( { "type": _WsType.GET_MODULE_CONTEXT.value, - "payload": {"data": mc}, + "payload": {"context": mc, "metadata": meta_return}, } ) @@ -2183,7 +2189,6 @@ def __render_page(self, page_name: str) -> t.Any: to=page_name, params={ _Server._RESOURCE_HANDLER_ARG: pr._resource_handler.get_id(), - _Server._CUSTOM_PAGE_META_ARG: json.dumps(pr._metadata, cls=_TaipyJsonEncoder), }, ): # Proactively handle the bindings of custom page variables diff --git a/taipy/gui/server.py b/taipy/gui/server.py index 6e7bfcca00..e22235d7f6 100644 --- a/taipy/gui/server.py +++ b/taipy/gui/server.py @@ -48,7 +48,6 @@ class _Server: __OPENING_CURLY = r"\1{" __CLOSING_CURLY = r"}\2" _RESOURCE_HANDLER_ARG = "tprh" - _CUSTOM_PAGE_META_ARG = "tp_cp_meta" def __init__( self, From 918916ed96d7d949f29667f326412723a70f51ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Andr=C3=A9?= <88906996+joaoandre-avaiga@users.noreply.github.com> Date: Tue, 25 Jun 2024 23:32:42 -0300 Subject: [PATCH 05/10] feat(migrate_cli): ensure connection is closed --- taipy/core/_entity/_migrate/_migrate_sql.py | 85 +++++++++++---------- tests/core/_entity/test_migrate_cli.py | 1 - 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/taipy/core/_entity/_migrate/_migrate_sql.py b/taipy/core/_entity/_migrate/_migrate_sql.py index fc23d78788..5cf56091f0 100644 --- a/taipy/core/_entity/_migrate/_migrate_sql.py +++ b/taipy/core/_entity/_migrate/_migrate_sql.py @@ -9,6 +9,7 @@ # 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. +from contextlib import closing import json import os import shutil @@ -24,32 +25,34 @@ def _load_all_entities_from_sql(db_file: str) -> Tuple[Dict, Dict]: conn = sqlite3.connect(db_file) - query = "SELECT model_id, document FROM taipy_model" - query_version = "SELECT * FROM taipy_version" - cursor = conn.execute(query) - entities = {} - versions = {} - - for row in cursor: - _id = row[0] - document = row[1] - entities[_id] = {"data": json.loads(document)} - - cursor = conn.execute(query_version) - for row in cursor: - id = row[0] - config_id = row[1] - creation_date = row[2] - is_production = row[3] - is_development = row[4] - is_latest = row[5] - versions[id] = { - "config_id": config_id, - "creation_date": creation_date, - "is_production": is_production, - "is_development": is_development, - "is_latest": is_latest, - } + with closing(conn): + query = "SELECT model_id, document FROM taipy_model" + query_version = "SELECT * FROM taipy_version" + cursor = conn.execute(query) + entities = {} + versions = {} + + for row in cursor: + _id = row[0] + document = row[1] + entities[_id] = {"data": json.loads(document)} + + cursor = conn.execute(query_version) + for row in cursor: + id = row[0] + config_id = row[1] + creation_date = row[2] + is_production = row[3] + is_development = row[4] + is_latest = row[5] + versions[id] = { + "config_id": config_id, + "creation_date": creation_date, + "is_production": is_production, + "is_development": is_development, + "is_latest": is_latest, + } + return entities, versions @@ -123,21 +126,21 @@ def __insert_version(version: dict, conn): def __write_entities_to_sql(_entities: Dict, _versions: Dict, db_file: str): conn = sqlite3.connect(db_file) - - for k, entity in _entities.items(): - if "SCENARIO" in k: - __insert_scenario(entity["data"], conn) - elif "TASK" in k: - __insert_task(entity["data"], conn) - elif "DATANODE" in k: - __insert_datanode(entity["data"], conn) - elif "JOB" in k: - __insert_job(entity["data"], conn) - elif "CYCLE" in k: - __insert_cycle(entity["data"], conn) - - for _, version in _versions.items(): - __insert_version(version, conn) + with closing(conn): + for k, entity in _entities.items(): + if "SCENARIO" in k: + __insert_scenario(entity["data"], conn) + elif "TASK" in k: + __insert_task(entity["data"], conn) + elif "DATANODE" in k: + __insert_datanode(entity["data"], conn) + elif "JOB" in k: + __insert_job(entity["data"], conn) + elif "CYCLE" in k: + __insert_cycle(entity["data"], conn) + + for _, version in _versions.items(): + __insert_version(version, conn) def _restore_migrate_sql_entities(path: str) -> bool: diff --git a/tests/core/_entity/test_migrate_cli.py b/tests/core/_entity/test_migrate_cli.py index 5863183d5e..0d9ad05531 100644 --- a/tests/core/_entity/test_migrate_cli.py +++ b/tests/core/_entity/test_migrate_cli.py @@ -207,7 +207,6 @@ def test_migrate_sql_backup_and_remove(caplog, tmp_sqlite): assert not os.path.exists(backup_sqlite) -@pytest.mark.skipif(sys.platform == "win32", reason="Does not run on windows due to PermissionError: [WinError 32]") def test_migrate_sql_backup_and_restore(caplog, tmp_sqlite): _MigrateCLI.create_parser() From cb4d0ec9319e3b1b9fa675f984301b8d61ff378e Mon Sep 17 00:00:00 2001 From: Joao Andre Date: Tue, 25 Jun 2024 20:40:53 -0300 Subject: [PATCH 06/10] chore: fix unsorted imports --- taipy/core/_entity/_migrate/_migrate_sql.py | 2 +- tests/core/_entity/test_migrate_cli.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/taipy/core/_entity/_migrate/_migrate_sql.py b/taipy/core/_entity/_migrate/_migrate_sql.py index 5cf56091f0..817a1037ac 100644 --- a/taipy/core/_entity/_migrate/_migrate_sql.py +++ b/taipy/core/_entity/_migrate/_migrate_sql.py @@ -9,11 +9,11 @@ # 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. -from contextlib import closing import json import os import shutil import sqlite3 +from contextlib import closing from typing import Dict, Tuple from taipy.logger._taipy_logger import _TaipyLogger diff --git a/tests/core/_entity/test_migrate_cli.py b/tests/core/_entity/test_migrate_cli.py index 0d9ad05531..7a344b20c3 100644 --- a/tests/core/_entity/test_migrate_cli.py +++ b/tests/core/_entity/test_migrate_cli.py @@ -12,7 +12,6 @@ import filecmp import os import shutil -import sys from sqlite3 import OperationalError from unittest.mock import patch From 16e90fba5bce8adf2b5531135b263e0312a8944b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fred=20Lef=C3=A9v=C3=A8re-Laoide?= <90181748+FredLL-Avaiga@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:20:44 +0200 Subject: [PATCH 07/10] use taipy-assets favicon (#1453) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use taipy-assets favicon resolves #1327 allow to change favicon dynamically resolves #1244 * with doc * doc * test * Fab's comments * allow to change favicon for one state * fab's comments * favicon * mypy * trying to fix cross-env ... * trying to fix cross-env ... * trying to fix cross-env ... * do not run test if cache hit * noncoverage either if cache hit * class name --------- Co-authored-by: Fred Lefévère-Laoide --- .github/workflows/frontend.yml | 12 ++-- frontend/taipy-gui/public/favicon.ico | Bin 16958 -> 4066 bytes frontend/taipy-gui/public/favicon.png | Bin 11708 -> 4867 bytes frontend/taipy-gui/public/index.html | 2 +- .../taipy-gui/src/context/taipyReducers.ts | 5 +- frontend/taipy-gui/src/context/utils.ts | 20 ++++++ frontend/taipy-gui/src/context/wsUtils.ts | 3 +- taipy/gui/gui.py | 60 +++++++++++++----- taipy/gui/state.py | 12 ++++ taipy/gui/types.py | 1 + tests/gui/gui_specific/test_favicon.py | 35 ++++++++++ 11 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 tests/gui/gui_specific/test_favicon.py diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 8e4b573baf..046bc43d84 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -59,13 +59,15 @@ jobs: - if: steps.cache-gui-fe-build.outputs.cache-hit != 'true' run: npm run build --if-present - - run: npm test + - if: steps.cache-gui-fe-build.outputs.cache-hit != 'true' + run: npm test - name: Code coverage - if: matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' - uses: artiomtr/jest-coverage-report-action@v2.2.6 + if: matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' && steps.cache-gui-fe-build.outputs.cache-hit != 'true' + uses: artiomtr/jest-coverage-report-action@v2.3.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} threshold: "80" - working-directory: "frontend/taipy-gui" - skip-step: "install" + working-directory: "./frontend/taipy-gui" + skip-step: install + annotations: failed-tests diff --git a/frontend/taipy-gui/public/favicon.ico b/frontend/taipy-gui/public/favicon.ico index 674f9745fda5c16ce9261bbeaba1bcda34a23111..9c145731e32ff542a4dec5dba3355164d839f22f 100644 GIT binary patch literal 4066 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 WW~U{0v0V+e%&kv2SX3PMN%%i}Zxl`d literal 16958 zcmeI1d32Z6naAT)9UZN9rmeMgIuTI{$ST=G$i9N8AP9slKoYW&kN_beiwJ615=1~* z>QY$)BmqJIMTf;YbCBXPTBm?#v_K@)(o7#I54*T<{ra2m0fS>&g#Wuhoxs8*8f>y|;_+i#x=f z;s@RKEARu)@w_`1F35w9ud?%cYJKk5qn*9WK4$I2 z@@IlLqYu4(M1L_r_<65-Jchlx8{(BxNF zRGPgYD(vlw0X@uldYbv6{>NXQocgT#3>5f*T(CYMe;AK(ZB4`n&fFb~ROj^auE8C9 z@}3(16G2TQm(lKDl@wQVUNic8dhmPDR5l4GcN1XM=g(JC3ieeGTlYBU+4uJeC-!rL`g~F0 z*WHx+d1AgXuW*IhMvG+2p|u-}=7k1*Rqo&4l>7G+51E%zGM-ku%>l+^T*hY)*gDAG zyPT%`DYY)8*v6g*|7%Rq!@!o6lp|v$dW&Q(9 znZKVY3-B|QQE_jpjao?kU_8cUeCA;fwA6t1SK2wPn%}LTn#A7Zyr%9^yZhIbRAiU? z_mQT)qMv!QYQpbMcxhAON`6-yceF4pHOdI-PTlDWAa?dV5_lk;eu_?u1J%9oE$d7XPPL5y2DK4WWH zK%n#oImpI40`Cm?(^oLQtpU`Wt5O3sZ(I6V*WIFvVDJ0aRXm+nroCJiFu;@s_{-$P zKIPp_brluC1HUc}2sYJ^jU~@h#Q2pHv$vK8gsJZ!GbbQiCNoG-19}SHCEPXeTGCHG zqTgNn0_rL%^X5onX+Ws-gsR!e^GR!_IV6tN&-lCt-6pP;__w8>cm2R^^PZcI#u8~kul|U0>gx2FJFpc8#+%}x z1ng79B=i>tCa6F5MC|Af5a_pOpw-VAcrEOItDkk*_@h4v{pcuGJd1;pWOCx1zACM# zp*YB+-K#%MZBt%MEv_#P8mzv9&Fr8@=zl%x@2YikW(U>IdvSVE&`{&S(LZ>1RJ(FY&kfgB|_Bk2!jZJn=;T)K!^J?L_0O;3pigiPYt( zbL(dXk5K=|WgjJz6Mx=;u1oz}pIKcnGkCOtS<1b;Ko9yemuFY)oF1BLriZ3G$mY?Xv0xjz>7O5p>3Y|{z5tzQnI1}t zd-YFS-Z(up&9QrRW~wdy`K+be@x&(-?=bED?wJ%sl$)`kB2 z1?OML`bBkK{dp$Wqmy$!b7|f$8*>KEGC9(j6ILXXNB?uVtGDNb%{JL#B~F{{(E|Ov zPvBR^=eNMy1@kih75xsV`5b?H=e7D-59_ku{rl=ZWW(b6!tAhO>G5ci&C$Ok|CPp^ zuvsQ6tlVjnJxffj&V6Zn=AgO8L2dd&f3UE>0o|ZOj<-l%t=k{@Co9Rr$Z#!DmPh zuf!fJ_~O8LjLZ1$-vDmb=Rfm-Y?sv!jOW6wogB2^{p@%D?y~WnJ#5qSyJr&142O9# zIr_7z3N|)Q9rU#9^Td4YIU;M(^qL)M;Z-Ire38|kAiMnypoi}76})4!*YG*Mcl)4a zFUWpbe84y^h&lddPOF{YeaK02v#aPW*s~Ee%ikzM3rrpKj7-jZUiRV{TX&^}FLpGh zhcCcBP2?;r+_Ez@{5dlve3@8^eTaw>jA!=%Iq(y~_YHi2|Je)H53KL9_(9`X?Tp8` z3fAf}D-3d0~Hv)sJ5NQRj0O&#c{r#?=gTHm9us> zP6~ffwiirF#2W06ig-Z|*xv!}*FC?5@mm0M|3I|m2V!N{|E+Vu^=xbH_?!8dmw%f= zyRZ0#=qG|iq)4h+^>&#jmdnf}Y0@8cE_dPV#@&2QiCE=mJ>B%j64~=Ai}viA5b=r` zFUCc#r9b};Kn}zRe9FG!yYK_TS1>pGj{QI^hzYSFM#KsqwB$lZe+kty)^Bd@BN$_9^8BMS=sY;Zv9~S#E6&7gou}&wlz!N z-YEO@%HlV6kB@lOBuCar|9Y96-wTK9I#@&td_W#BS9furU_amoV&UEsZXZ}Z9m)fZ zWBcPj{CJDNx5V7$J2}`@kn_adYLD0bBjSn8Yu|gCcutVkiOT0)TRvPSd%h@`6#LpY zHr8oFHk>)#bau|ObN25^j@)RHqc)pS(e>)){5CpT*I|M@h!y-MNRF}3$OU|lABY8Y zf%Oq1@)RGiZ_(jsx>OF>e%@zzmN6I$?VNGEgRs}TiC%)73=xqcQJ{UK7qJF1jc8$iV;+R#Wr4v{||P z))nXP-&9(-e@pp{eRXqZ?%OuEXnzCy7{)4}ClA?jM*U}qna$1rQ#NU6{h8#b8Z$=m z9uu|2iTQ|_MgzlRUN_~XM|RFB{a{C7>9P80rJvSjmwdh@v-tO$(q^C9@M^3r!`q;g@o`~6Q9HO^7`rSV8_+qR4vDJw!@dfmL^wC$98e^6q56TO34jg_w zW|yP?v6z20kH+jXkHjAI>OX7};y*C4@xL`u3IA>)6OWnj#7`alp@}C z>?3NP6Z>7eK6}h+xB4?Dym{ovkuQtXCqrb3T#+ZHi$Y>NVQAwS{9~V;G;(j#{sSkg z)lNO6ALjzkw8u2H=N|gxPi4b1PQM?Vbjr~lkaXI>wqO5gl-k(`#OXdk+}hj&9neqC zP(R2a&K7D*zqI7NjU!_kqyu)Cc~2f%D|?Da6X?$r*~0q3Iq&uzXsSw1Z1{#}*yrT? zsKnR5si=5=chl*w@g4nhE<*0YHO=dWCVzVJ#FziezM)@cLG>Ru68k`}{-pFj?2$c4 z^bza#K1<4e6fzo7CrbtAm{oa^H;dsOE6Bi)uoG?Hb<%Mk^f0!Pn>+E@m%%GJI^g%z2oZzfzt1Zf3pdE zxcQvcZS~);`@clL&40IkYKfEoZtZ`5Pff77apBe0b~JOgImEtUJoRpWOTBwI?q8gm zpA>(@B*eXM;^IFt(eaUg^}&dkoyrFf?04{J-|OT(u?KtB z5qqnh*vCq{6Myfy=c)Ujd*YvTQnjzCW$%;wRU6Lnp4eNEtJEyc?K{M+;%0Ht`cFMj zyK^?^%KoQ6&)FKt8NeCH*+PBs6TyP_&3UZ&b%bx!?>As`->35?b2dJIo5IH~{Ag}N*XAFMmFYpED z4)5bY4Fc8$AzpY_1gwF+uVJbhxZ-woptmStjkyYRPeq|>^b|)Ph`KeL1yZbR0 z^D;l{U_GqsZo!#-r?^d6?d`_io`>q++6Onwh7TAEe{!DU2WktxASalYcW&^#2&@mf zw8GkbW&buX7w_cE&pOCYbQ7oV3mbQv_wB~M4gL7R=7IGAW8NbcfFU@XQ2#%CUj^$GL0hIL&oT93`Vb`7kF z7}+&mpk4MhYo68H)|F>kfiD@??F%~}b6;uf-1cfUx5S+Fx25gd*cjs?FpnGSn=5g3 o&wGJh_gcS=&P(-mk9#fOUrI-3v|nj|bcTlS#{A9$|KB|De-B8LF8}}l diff --git a/frontend/taipy-gui/public/favicon.png b/frontend/taipy-gui/public/favicon.png index b094841eda36cd4234d8f110572ced0ee05e8fbe..4205726cedf7a2ed5b23aeda5e2a418ec3e8d3ed 100644 GIT binary patch literal 4867 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 Wk%q;1uO8d60XZop$x?AczyATn+iceW diff --git a/frontend/taipy-gui/public/index.html b/frontend/taipy-gui/public/index.html index 65c4e82cb6..74d9e6627b 100644 --- a/frontend/taipy-gui/public/index.html +++ b/frontend/taipy-gui/public/index.html @@ -5,7 +5,7 @@ - + {{title}}