mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-04-21 16:50:02 +02:00
Merge pull request #98 from tiqi-group/refactor/passing_full_serialization_dict_to_frontend
Refactor: passing full serialization dict to frontend
This commit is contained in:
commit
e60880fd30
@ -1,10 +1,6 @@
|
|||||||
import { useCallback, useEffect, useReducer, useState } from 'react';
|
import { useCallback, useEffect, useReducer, useState } from 'react';
|
||||||
import { Navbar, Form, Offcanvas, Container } from 'react-bootstrap';
|
import { Navbar, Form, Offcanvas, Container } from 'react-bootstrap';
|
||||||
import { hostname, port, socket } from './socket';
|
import { hostname, port, socket } from './socket';
|
||||||
import {
|
|
||||||
DataServiceComponent,
|
|
||||||
DataServiceJSON
|
|
||||||
} from './components/DataServiceComponent';
|
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import {
|
import {
|
||||||
Notifications,
|
Notifications,
|
||||||
@ -14,6 +10,7 @@ import {
|
|||||||
import { ConnectionToast } from './components/ConnectionToast';
|
import { ConnectionToast } from './components/ConnectionToast';
|
||||||
import { SerializedValue, setNestedValueByPath, State } from './utils/stateUtils';
|
import { SerializedValue, setNestedValueByPath, State } from './utils/stateUtils';
|
||||||
import { WebSettingsContext, WebSetting } from './WebSettings';
|
import { WebSettingsContext, WebSetting } from './WebSettings';
|
||||||
|
import { Attribute, GenericComponent } from './components/GenericComponent';
|
||||||
|
|
||||||
type Action =
|
type Action =
|
||||||
| { type: 'SET_DATA'; data: State }
|
| { type: 'SET_DATA'; data: State }
|
||||||
@ -35,7 +32,10 @@ const reducer = (state: State, action: Action): State => {
|
|||||||
case 'SET_DATA':
|
case 'SET_DATA':
|
||||||
return action.data;
|
return action.data;
|
||||||
case 'UPDATE_ATTRIBUTE': {
|
case 'UPDATE_ATTRIBUTE': {
|
||||||
return setNestedValueByPath(state, action.fullAccessPath, action.newValue);
|
return {
|
||||||
|
...state,
|
||||||
|
value: setNestedValueByPath(state.value, action.fullAccessPath, action.newValue)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error();
|
throw new Error();
|
||||||
@ -184,9 +184,10 @@ const App = () => {
|
|||||||
|
|
||||||
<div className="App navbarOffset">
|
<div className="App navbarOffset">
|
||||||
<WebSettingsContext.Provider value={webSettings}>
|
<WebSettingsContext.Provider value={webSettings}>
|
||||||
<DataServiceComponent
|
<GenericComponent
|
||||||
name={''}
|
name=""
|
||||||
props={state as DataServiceJSON}
|
parentPath=""
|
||||||
|
attribute={state as Attribute}
|
||||||
isInstantUpdate={isInstantUpdate}
|
isInstantUpdate={isInstantUpdate}
|
||||||
addNotification={addNotification}
|
addNotification={addNotification}
|
||||||
/>
|
/>
|
||||||
|
@ -6,7 +6,12 @@ export interface SerializedValue {
|
|||||||
async?: boolean;
|
async?: boolean;
|
||||||
parameters?: unknown;
|
parameters?: unknown;
|
||||||
}
|
}
|
||||||
export type State = Record<string, SerializedValue> | null;
|
export type State = {
|
||||||
|
type: string;
|
||||||
|
value: Record<string, SerializedValue> | null;
|
||||||
|
readonly: boolean;
|
||||||
|
doc: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
export function setNestedValueByPath(
|
export function setNestedValueByPath(
|
||||||
serializationDict: Record<string, SerializedValue>,
|
serializationDict: Record<string, SerializedValue>,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "pydase"
|
name = "pydase"
|
||||||
version = "0.5.2"
|
version = "0.6.0"
|
||||||
description = "A flexible and robust Python library for creating, managing, and interacting with data services, with built-in support for web and RPC servers, and customizable features for diverse use cases."
|
description = "A flexible and robust Python library for creating, managing, and interacting with data services, with built-in support for web and RPC servers, and customizable features for diverse use cases."
|
||||||
authors = ["Mose Mueller <mosmuell@ethz.ch>"]
|
authors = ["Mose Mueller <mosmuell@ethz.ch>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -196,7 +196,7 @@ class DataService(rpyc.Service, AbstractDataService):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Traverse the serialized representation and set the attributes of the class
|
# Traverse the serialized representation and set the attributes of the class
|
||||||
serialized_class = self.serialize()
|
serialized_class = self.serialize()["value"]
|
||||||
for path in generate_serialized_data_paths(json_dict):
|
for path in generate_serialized_data_paths(json_dict):
|
||||||
nested_json_dict = get_nested_dict_by_path(json_dict, path)
|
nested_json_dict = get_nested_dict_by_path(json_dict, path)
|
||||||
value = nested_json_dict["value"]
|
value = nested_json_dict["value"]
|
||||||
@ -248,7 +248,7 @@ class DataService(rpyc.Service, AbstractDataService):
|
|||||||
Returns:
|
Returns:
|
||||||
dict: The serialized instance.
|
dict: The serialized instance.
|
||||||
"""
|
"""
|
||||||
return Serializer.serialize_object(self)["value"]
|
return Serializer.serialize_object(self)
|
||||||
|
|
||||||
def update_DataService_attribute( # noqa: N802
|
def update_DataService_attribute( # noqa: N802
|
||||||
self,
|
self,
|
||||||
|
@ -30,10 +30,10 @@ class DataServiceCache:
|
|||||||
self._cache = self.service.serialize()
|
self._cache = self.service.serialize()
|
||||||
|
|
||||||
def update_cache(self, full_access_path: str, value: Any) -> None:
|
def update_cache(self, full_access_path: str, value: Any) -> None:
|
||||||
set_nested_value_by_path(self._cache, full_access_path, value)
|
set_nested_value_by_path(self._cache["value"], full_access_path, value)
|
||||||
|
|
||||||
def get_value_dict_from_cache(self, full_access_path: str) -> dict[str, Any]:
|
def get_value_dict_from_cache(self, full_access_path: str) -> dict[str, Any]:
|
||||||
try:
|
try:
|
||||||
return get_nested_dict_by_path(self._cache, full_access_path)
|
return get_nested_dict_by_path(self._cache["value"], full_access_path)
|
||||||
except (SerializationPathError, SerializationValueError, KeyError):
|
except (SerializationPathError, SerializationValueError, KeyError):
|
||||||
return {}
|
return {}
|
||||||
|
@ -126,7 +126,7 @@ class StateManager:
|
|||||||
|
|
||||||
if self.filename is not None:
|
if self.filename is not None:
|
||||||
with open(self.filename, "w") as f:
|
with open(self.filename, "w") as f:
|
||||||
json.dump(self.cache, f, indent=4)
|
json.dump(self.cache["value"], f, indent=4)
|
||||||
else:
|
else:
|
||||||
logger.info(
|
logger.info(
|
||||||
"State manager was not initialised with a filename. Skipping "
|
"State manager was not initialised with a filename. Skipping "
|
||||||
@ -191,7 +191,7 @@ class StateManager:
|
|||||||
value: The new value to set for the attribute.
|
value: The new value to set for the attribute.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
current_value_dict = get_nested_dict_by_path(self.cache, path)
|
current_value_dict = get_nested_dict_by_path(self.cache["value"], path)
|
||||||
|
|
||||||
# This will also filter out methods as they are 'read-only'
|
# This will also filter out methods as they are 'read-only'
|
||||||
if current_value_dict["readonly"]:
|
if current_value_dict["readonly"]:
|
||||||
@ -234,7 +234,7 @@ class StateManager:
|
|||||||
# Update path to reflect the attribute without list indices
|
# Update path to reflect the attribute without list indices
|
||||||
path = ".".join([*parent_path_list, attr_name])
|
path = ".".join([*parent_path_list, attr_name])
|
||||||
|
|
||||||
attr_cache_type = get_nested_dict_by_path(self.cache, path)["type"]
|
attr_cache_type = get_nested_dict_by_path(self.cache["value"], path)["type"]
|
||||||
|
|
||||||
# Traverse the object according to the path parts
|
# Traverse the object according to the path parts
|
||||||
target_obj = get_object_attr_from_path_list(self.service, parent_path_list)
|
target_obj = get_object_attr_from_path_list(self.service, parent_path_list)
|
||||||
@ -273,7 +273,7 @@ class StateManager:
|
|||||||
return has_decorator
|
return has_decorator
|
||||||
|
|
||||||
cached_serialization_dict = get_nested_dict_by_path(
|
cached_serialization_dict = get_nested_dict_by_path(
|
||||||
self.cache, full_access_path
|
self.cache["value"], full_access_path
|
||||||
)
|
)
|
||||||
|
|
||||||
if cached_serialization_dict["value"] == "method":
|
if cached_serialization_dict["value"] == "method":
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "/static/css/main.2d8458eb.css",
|
"main.css": "/static/css/main.2d8458eb.css",
|
||||||
"main.js": "/static/js/main.dba067e7.js",
|
"main.js": "/static/js/main.1b1d7066.js",
|
||||||
"index.html": "/index.html",
|
"index.html": "/index.html",
|
||||||
"main.2d8458eb.css.map": "/static/css/main.2d8458eb.css.map",
|
"main.2d8458eb.css.map": "/static/css/main.2d8458eb.css.map",
|
||||||
"main.dba067e7.js.map": "/static/js/main.dba067e7.js.map"
|
"main.1b1d7066.js.map": "/static/js/main.1b1d7066.js.map"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/css/main.2d8458eb.css",
|
"static/css/main.2d8458eb.css",
|
||||||
"static/js/main.dba067e7.js"
|
"static/js/main.1b1d7066.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site displaying a pydase UI."/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>pydase App</title><script defer="defer" src="/static/js/main.dba067e7.js"></script><link href="/static/css/main.2d8458eb.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site displaying a pydase UI."/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>pydase App</title><script defer="defer" src="/static/js/main.1b1d7066.js"></script><link href="/static/css/main.2d8458eb.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
File diff suppressed because one or more lines are too long
1
src/pydase/frontend/static/js/main.1b1d7066.js.map
Normal file
1
src/pydase/frontend/static/js/main.1b1d7066.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -126,7 +126,7 @@ class WebServer:
|
|||||||
@property
|
@property
|
||||||
def web_settings(self) -> dict[str, dict[str, Any]]:
|
def web_settings(self) -> dict[str, dict[str, Any]]:
|
||||||
current_web_settings = self._get_web_settings_from_file()
|
current_web_settings = self._get_web_settings_from_file()
|
||||||
for path in generate_serialized_data_paths(self.state_manager.cache):
|
for path in generate_serialized_data_paths(self.state_manager.cache["value"]):
|
||||||
if path in current_web_settings:
|
if path in current_web_settings:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ class Service(pydase.DataService):
|
|||||||
self._property_attr = value
|
self._property_attr = value
|
||||||
|
|
||||||
|
|
||||||
CURRENT_STATE = Service().serialize()
|
CURRENT_STATE = Service().serialize()["value"]
|
||||||
|
|
||||||
LOAD_STATE = {
|
LOAD_STATE = {
|
||||||
"list_attr": {
|
"list_attr": {
|
||||||
|
@ -323,7 +323,7 @@ def test_derived_data_service_serialization() -> None:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def setup_dict():
|
def setup_dict() -> dict[str, Any]:
|
||||||
class MySubclass(pydase.DataService):
|
class MySubclass(pydase.DataService):
|
||||||
attr3 = 1.0
|
attr3 = 1.0
|
||||||
list_attr = [1.0, 1]
|
list_attr = [1.0, 1]
|
||||||
@ -333,30 +333,32 @@ def setup_dict():
|
|||||||
attr2 = MySubclass()
|
attr2 = MySubclass()
|
||||||
attr_list = [0, 1, MySubclass()]
|
attr_list = [0, 1, MySubclass()]
|
||||||
|
|
||||||
return ServiceClass().serialize()
|
return ServiceClass().serialize()["value"]
|
||||||
|
|
||||||
|
|
||||||
def test_update_attribute(setup_dict):
|
def test_update_attribute(setup_dict) -> None:
|
||||||
set_nested_value_by_path(setup_dict, "attr1", 15)
|
set_nested_value_by_path(setup_dict, "attr1", 15)
|
||||||
assert setup_dict["attr1"]["value"] == 15
|
assert setup_dict["attr1"]["value"] == 15
|
||||||
|
|
||||||
|
|
||||||
def test_update_nested_attribute(setup_dict):
|
def test_update_nested_attribute(setup_dict) -> None:
|
||||||
set_nested_value_by_path(setup_dict, "attr2.attr3", 25.0)
|
set_nested_value_by_path(setup_dict, "attr2.attr3", 25.0)
|
||||||
assert setup_dict["attr2"]["value"]["attr3"]["value"] == 25.0
|
assert setup_dict["attr2"]["value"]["attr3"]["value"] == 25.0
|
||||||
|
|
||||||
|
|
||||||
def test_update_list_entry(setup_dict):
|
def test_update_list_entry(setup_dict) -> None:
|
||||||
set_nested_value_by_path(setup_dict, "attr_list[1]", 20)
|
set_nested_value_by_path(setup_dict, "attr_list[1]", 20)
|
||||||
assert setup_dict["attr_list"]["value"][1]["value"] == 20
|
assert setup_dict["attr_list"]["value"][1]["value"] == 20
|
||||||
|
|
||||||
|
|
||||||
def test_update_list_append(setup_dict):
|
def test_update_list_append(setup_dict) -> None:
|
||||||
set_nested_value_by_path(setup_dict, "attr_list[3]", 20)
|
set_nested_value_by_path(setup_dict, "attr_list[3]", 20)
|
||||||
assert setup_dict["attr_list"]["value"][3]["value"] == 20
|
assert setup_dict["attr_list"]["value"][3]["value"] == 20
|
||||||
|
|
||||||
|
|
||||||
def test_update_invalid_list_index(setup_dict, caplog: pytest.LogCaptureFixture):
|
def test_update_invalid_list_index(
|
||||||
|
setup_dict, caplog: pytest.LogCaptureFixture
|
||||||
|
) -> None:
|
||||||
set_nested_value_by_path(setup_dict, "attr_list[10]", 30)
|
set_nested_value_by_path(setup_dict, "attr_list[10]", 30)
|
||||||
assert (
|
assert (
|
||||||
"Error occured trying to change 'attr_list[10]': list index "
|
"Error occured trying to change 'attr_list[10]': list index "
|
||||||
|
Loading…
x
Reference in New Issue
Block a user