Browse Source

GUI: frontend decouple get function list (#820) (#858)

* get function list

* per Fred

---------

Co-authored-by: Fred Lefévère-Laoide <90181748+FredLL-Avaiga@users.noreply.github.com>
Dinh Long Nguyen 1 year ago
parent
commit
2ed3aa52de

+ 16 - 9
frontend/taipy-gui/base/src/app.ts

@@ -3,7 +3,7 @@ import { sendWsMessage, TAIPY_CLIENT_ID } from "../../src/context/wsUtils";
 import { uploadFile } from "../../src/workers/fileupload";
 
 import { Socket, io } from "socket.io-client";
-import { VariableManager } from "./variableManager";
+import { DataManager } from "./dataManager";
 import { initSocket } from "./utils";
 
 export type OnInitHandler = (appManager: TaipyApp) => void;
@@ -13,7 +13,8 @@ export class TaipyApp {
     socket: Socket;
     _onInit: OnInitHandler | undefined;
     _onChange: OnChangeHandler | undefined;
-    variableManager: VariableManager | undefined;
+    variableData: DataManager | undefined;
+    functionData: DataManager | undefined;
     appId: string;
     clientId: string;
     context: string;
@@ -28,7 +29,8 @@ export class TaipyApp {
         socket = socket || io("/", { autoConnect: false });
         this.onInit = onInit;
         this.onChange = onChange;
-        this.variableManager = undefined;
+        this.variableData = undefined;
+        this.functionData = undefined;
         this.clientId = "";
         this.context = "";
         this.appId = "";
@@ -74,27 +76,32 @@ export class TaipyApp {
 
     // Public methods
     getEncodedName(varName: string, module: string) {
-        return this.variableManager?.getEncodedName(varName, module);
+        return this.variableData?.getEncodedName(varName, module);
     }
 
     getName(encodedName: string) {
-        return this.variableManager?.getName(encodedName);
+        return this.variableData?.getName(encodedName);
     }
 
     get(encodedName: string) {
-        return this.variableManager?.get(encodedName);
+        return this.variableData?.get(encodedName);
     }
 
     getInfo(encodedName: string) {
-        return this.variableManager?.getInfo(encodedName);
+        return this.variableData?.getInfo(encodedName);
     }
 
     getDataTree() {
-        return this.variableManager?.getDataTree();
+        return this.variableData?.getDataTree();
     }
 
     getAllData() {
-        return this.variableManager?.getAllData();
+        return this.variableData?.getAllData();
+    }
+
+    getFunctionList() {
+        const functionData = this.functionData?.getDataTree()[this.context];
+        return Object.keys(functionData || {});
     }
 
     // This update will only send the request to Taipy Gui backend

+ 29 - 33
frontend/taipy-gui/base/src/variableManager.ts → frontend/taipy-gui/base/src/dataManager.ts

@@ -1,73 +1,69 @@
-export interface VariableModuleData {
-    [key: string]: VariableName;
-}
+export type ModuleData = Record<string, VarName>;
 
-interface VariableName {
-    [key: string]: VariableData;
-}
+export type VarName = Record<string, VarData>;
 
-interface VariableData {
+interface VarData {
     type: string;
     value: unknown;
     encoded_name: string;
 }
 
 // This class hold the information of variables and real-time value of variables
-export class VariableManager {
+export class DataManager {
     // key: encoded name, value: real-time value
     _data: Record<string, unknown>;
     // Initial data fetched from taipy-gui backend
-    _variables: VariableModuleData;
+    _init_data: ModuleData;
 
-    constructor(variableModuleData: VariableModuleData) {
+    constructor(variableModuleData: ModuleData) {
         this._data = {};
-        this._variables = {};
+        this._init_data = {};
         this.init(variableModuleData);
     }
 
-    init(variableModuleData: VariableModuleData) {
+    init(variableModuleData: ModuleData) {
         // Identify changes between the new and old data
-        const changes: VariableModuleData = {};
-        for (const context in this._variables) {
+        const changes: ModuleData = {};
+        for (const context in this._init_data) {
             if (!(context in variableModuleData)) {
-                changes[context] = this._variables[context];
+                changes[context] = this._init_data[context];
                 continue;
             }
-            for (const variable in this._variables[context]) {
+            for (const variable in this._init_data[context]) {
                 if (!(variable in variableModuleData[context])) {
                     if (!(context in changes)) {
                         changes[context] = {};
                     }
-                    changes[context][variable] = this._variables[context][variable];
+                    changes[context][variable] = this._init_data[context][variable];
                 }
             }
         }
-        if (Object.keys(changes).length === 0) {
+        if (Object.keys(changes).length !== 0) {
             console.error("Unmatched data tree! Removed changes: ", changes);
         }
         // Reset the initial data
-        this._variables = variableModuleData;
+        this._init_data = variableModuleData;
         this._data = {};
-        for (const context in this._variables) {
-            for (const variable in this._variables[context]) {
-                const vData = this._variables[context][variable];
+        for (const context in this._init_data) {
+            for (const variable in this._init_data[context]) {
+                const vData = this._init_data[context][variable];
                 this._data[vData["encoded_name"]] = vData.value;
             }
         }
     }
 
     getEncodedName(varName: string, module: string): string | undefined {
-        if (module in this._variables && varName in this._variables[module]) {
-            return this._variables[module][varName].encoded_name;
+        if (module in this._init_data && varName in this._init_data[module]) {
+            return this._init_data[module][varName].encoded_name;
         }
         return undefined;
     }
 
     // return [name, moduleName]
     getName(encodedName: string): [string, string] | undefined {
-        for (const context in this._variables) {
-            for (const variable in this._variables[context]) {
-                const vData = this._variables[context][variable];
+        for (const context in this._init_data) {
+            for (const variable in this._init_data[context]) {
+                const vData = this._init_data[context][variable];
                 if (vData.encoded_name === encodedName) {
                     return [variable, context];
                 }
@@ -83,10 +79,10 @@ export class VariableManager {
         return this._data[encodedName];
     }
 
-    getInfo(encodedName: string): VariableData | undefined {
-        for (const context in this._variables) {
-            for (const variable in this._variables[context]) {
-                const vData = this._variables[context][variable];
+    getInfo(encodedName: string): VarData | undefined {
+        for (const context in this._init_data) {
+            for (const variable in this._init_data[context]) {
+                const vData = this._init_data[context][variable];
                 if (vData.encoded_name === encodedName) {
                     return { ...vData, value: this._data[encodedName] };
                 }
@@ -95,8 +91,8 @@ export class VariableManager {
         return undefined;
     }
 
-    getDataTree(): VariableModuleData {
-        return this._variables;
+    getDataTree(): ModuleData {
+        return this._init_data;
     }
 
     getAllData(): Record<string, unknown> {

+ 2 - 2
frontend/taipy-gui/base/src/index.ts

@@ -1,9 +1,9 @@
 import { TaipyApp, createApp, OnChangeHandler, OnInitHandler } from "./app";
-import { VariableModuleData } from "./variableManager";
+import { ModuleData } from "./dataManager";
 
 export default TaipyApp;
 export { TaipyApp, createApp };
-export type { OnChangeHandler, OnInitHandler, VariableModuleData };
+export type { OnChangeHandler, OnInitHandler, ModuleData };
 
 window.addEventListener("beforeunload", () => {
     document.cookie = "tprh=;path=/;Max-Age=-99999999;";

+ 12 - 8
frontend/taipy-gui/base/src/utils.ts

@@ -2,7 +2,7 @@ import { Socket } from "socket.io-client";
 import { IdMessage, storeClientId } from "../../src/context/utils";
 import { WsMessage, sendWsMessage } from "../../src/context/wsUtils";
 import { TaipyApp } from "./app";
-import { VariableManager, VariableModuleData } from "./variableManager";
+import { DataManager, ModuleData } from "./dataManager";
 
 interface MultipleUpdatePayload {
     name: string;
@@ -49,7 +49,7 @@ const processWsMessage = (message: WsMessage, appManager: TaipyApp) => {
             for (const muPayload of message.payload as [MultipleUpdatePayload]) {
                 const encodedName = muPayload.name;
                 const { value } = muPayload.payload;
-                appManager.variableManager?.update(encodedName, value);
+                appManager.variableData?.update(encodedName, value);
                 appManager.onChange && appManager.onChange(appManager, encodedName, value);
             }
         } else if (message.type === "ID") {
@@ -61,12 +61,16 @@ const processWsMessage = (message: WsMessage, appManager: TaipyApp) => {
             const mc = (message.payload as Record<string, unknown>).data as string;
             window.localStorage.setItem("ModuleContext", mc);
             appManager.context = mc;
-        } else if (message.type === "GVS") {
-            const variableData = (message.payload as Record<string, unknown>).data as VariableModuleData;
-            if (appManager.variableManager) {
-                appManager.variableManager.init(variableData);
+        } else if (message.type === "GDT") {
+            const payload = message.payload as Record<string, ModuleData>;
+            const variableData = payload.variable;
+            const functionData = payload.function;
+            if (appManager.variableData && appManager.functionData) {
+                appManager.variableData.init(variableData);
+                appManager.functionData.init(functionData);
             } else {
-                appManager.variableManager = new VariableManager(variableData);
+                appManager.variableData = new DataManager(variableData);
+                appManager.functionData = new DataManager(functionData);
                 appManager.onInit && appManager.onInit(appManager);
             }
         } else if (message.type === "AID") {
@@ -88,6 +92,6 @@ const postWsMessageProcessing = (message: WsMessage, appManager: TaipyApp) => {
         appManager.appId !== "" &&
         appManager.context !== ""
     ) {
-        sendWsMessage(appManager.socket, "GVS", "get_variables", {}, appManager.clientId, appManager.context);
+        sendWsMessage(appManager.socket, "GDT", "get_data_tree", {}, appManager.clientId, appManager.context);
     }
 };

+ 1 - 1
frontend/taipy-gui/src/context/wsUtils.ts

@@ -18,7 +18,7 @@ export type WsMessageType =
     | "PR"
     | "ACK"
     | "GMC"
-    | "GVS"
+    | "GDT"
     | "AID";
 
 export interface WsMessage {

+ 5 - 0
taipy/gui/_renderers/json.py

@@ -13,6 +13,7 @@ from __future__ import annotations
 from datetime import date, datetime, time
 from json import JSONEncoder
 from pathlib import Path
+from types import FunctionType, LambdaType
 
 from flask.json.provider import DefaultJSONProvider
 
@@ -32,6 +33,10 @@ def _default(o):
         return _date_to_string(o)
     if isinstance(o, Path):
         return str(o)
+    if isinstance(o, FunctionType):
+        return o.__name__
+    if isinstance(o, LambdaType):
+        return str(o)
     try:
         raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
     except Exception as e:

+ 26 - 16
taipy/gui/gui.py

@@ -617,8 +617,8 @@ class Gui:
                         self.__request_var_update(message.get("payload"))
                     elif msg_type == _WsType.GET_MODULE_CONTEXT.value:
                         self.__handle_ws_get_module_context(payload)
-                    elif msg_type == _WsType.GET_VARIABLES.value:
-                        self.__handle_ws_get_variables()
+                    elif msg_type == _WsType.GET_DATA_TREE.value:
+                        self.__handle_ws_get_data_tree()
                     elif msg_type == _WsType.APP_ID.value:
                         self.__handle_ws_app_id(message)
                 self.__send_ack(message.get("ack_id"))
@@ -1064,20 +1064,9 @@ class Gui:
                     }
                 )
 
-    def __handle_ws_get_variables(self):
-        # Get Variables
-        self.__pre_render_pages()
+    def __get_variable_tree(self, data: t.Dict[str, t.Any]):
         # Module Context -> Variable -> Variable data (name, type, initial_value)
         variable_tree: t.Dict[str, t.Dict[str, t.Dict[str, t.Any]]] = {}
-        data = vars(self._bindings()._get_data_scope())
-        data = {
-            k: v
-            for k, v in data.items()
-            if not k.startswith("_")
-            and not callable(v)
-            and "TpExPr" not in k
-            and not isinstance(v, (ModuleType, FunctionType, LambdaType, type, Page))
-        }
         for k, v in data.items():
             if isinstance(v, _TaipyBase):
                 data[k] = v.get()
@@ -1091,10 +1080,31 @@ class Gui:
                 "value": data[k],
                 "encoded_name": k,
             }
+        return variable_tree
+
+    def __handle_ws_get_data_tree(self):
+        # Get Variables
+        self.__pre_render_pages()
+        data = {
+            k: v
+            for k, v in vars(self._get_data_scope()).items()
+            if not k.startswith("_")
+            and not callable(v)
+            and "TpExPr" not in k
+            and not isinstance(v, (ModuleType, FunctionType, LambdaType, type, Page))
+        }
+        function_data = {
+            k: v
+            for k, v in vars(self._get_data_scope()).items()
+            if not k.startswith("_") and "TpExPr" not in k and isinstance(v, (FunctionType, LambdaType))
+        }
         self.__send_ws(
             {
-                "type": _WsType.GET_VARIABLES.value,
-                "payload": {"data": variable_tree},
+                "type": _WsType.GET_DATA_TREE.value,
+                "payload": {
+                    "variable": self.__get_variable_tree(data),
+                    "function": self.__get_variable_tree(function_data),
+                },
             }
         )
 

+ 1 - 1
taipy/gui/gui_types.py

@@ -48,7 +48,7 @@ class _WsType(Enum):
     PARTIAL = "PR"
     ACKNOWLEDGEMENT = "ACK"
     GET_MODULE_CONTEXT = "GMC"
-    GET_VARIABLES = "GVS"
+    GET_DATA_TREE = "GDT"
 
 
 NumberTypes = {"int", "int64", "float", "float64"}