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:
Mose Müller 2024-02-01 09:27:29 +01:00 committed by GitHub
commit e60880fd30
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 43 additions and 35 deletions

View File

@ -1,10 +1,6 @@
import { useCallback, useEffect, useReducer, useState } from 'react';
import { Navbar, Form, Offcanvas, Container } from 'react-bootstrap';
import { hostname, port, socket } from './socket';
import {
DataServiceComponent,
DataServiceJSON
} from './components/DataServiceComponent';
import './App.css';
import {
Notifications,
@ -14,6 +10,7 @@ import {
import { ConnectionToast } from './components/ConnectionToast';
import { SerializedValue, setNestedValueByPath, State } from './utils/stateUtils';
import { WebSettingsContext, WebSetting } from './WebSettings';
import { Attribute, GenericComponent } from './components/GenericComponent';
type Action =
| { type: 'SET_DATA'; data: State }
@ -35,7 +32,10 @@ const reducer = (state: State, action: Action): State => {
case 'SET_DATA':
return action.data;
case 'UPDATE_ATTRIBUTE': {
return setNestedValueByPath(state, action.fullAccessPath, action.newValue);
return {
...state,
value: setNestedValueByPath(state.value, action.fullAccessPath, action.newValue)
};
}
default:
throw new Error();
@ -184,9 +184,10 @@ const App = () => {
<div className="App navbarOffset">
<WebSettingsContext.Provider value={webSettings}>
<DataServiceComponent
name={''}
props={state as DataServiceJSON}
<GenericComponent
name=""
parentPath=""
attribute={state as Attribute}
isInstantUpdate={isInstantUpdate}
addNotification={addNotification}
/>

View File

@ -6,7 +6,12 @@ export interface SerializedValue {
async?: boolean;
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(
serializationDict: Record<string, SerializedValue>,

View File

@ -1,6 +1,6 @@
[tool.poetry]
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."
authors = ["Mose Mueller <mosmuell@ethz.ch>"]
readme = "README.md"

View File

@ -196,7 +196,7 @@ class DataService(rpyc.Service, AbstractDataService):
)
# 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):
nested_json_dict = get_nested_dict_by_path(json_dict, path)
value = nested_json_dict["value"]
@ -248,7 +248,7 @@ class DataService(rpyc.Service, AbstractDataService):
Returns:
dict: The serialized instance.
"""
return Serializer.serialize_object(self)["value"]
return Serializer.serialize_object(self)
def update_DataService_attribute( # noqa: N802
self,

View File

@ -30,10 +30,10 @@ class DataServiceCache:
self._cache = self.service.serialize()
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]:
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):
return {}

View File

@ -126,7 +126,7 @@ class StateManager:
if self.filename is not None:
with open(self.filename, "w") as f:
json.dump(self.cache, f, indent=4)
json.dump(self.cache["value"], f, indent=4)
else:
logger.info(
"State manager was not initialised with a filename. Skipping "
@ -191,7 +191,7 @@ class StateManager:
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'
if current_value_dict["readonly"]:
@ -234,7 +234,7 @@ class StateManager:
# Update path to reflect the attribute without list indices
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
target_obj = get_object_attr_from_path_list(self.service, parent_path_list)
@ -273,7 +273,7 @@ class StateManager:
return has_decorator
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":

View File

@ -1,13 +1,13 @@
{
"files": {
"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",
"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": [
"static/css/main.2d8458eb.css",
"static/js/main.dba067e7.js"
"static/js/main.1b1d7066.js"
]
}

View File

@ -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

File diff suppressed because one or more lines are too long

View File

@ -126,7 +126,7 @@ class WebServer:
@property
def web_settings(self) -> dict[str, dict[str, Any]]:
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:
continue

View File

@ -91,7 +91,7 @@ class Service(pydase.DataService):
self._property_attr = value
CURRENT_STATE = Service().serialize()
CURRENT_STATE = Service().serialize()["value"]
LOAD_STATE = {
"list_attr": {

View File

@ -323,7 +323,7 @@ def test_derived_data_service_serialization() -> None:
@pytest.fixture
def setup_dict():
def setup_dict() -> dict[str, Any]:
class MySubclass(pydase.DataService):
attr3 = 1.0
list_attr = [1.0, 1]
@ -333,30 +333,32 @@ def setup_dict():
attr2 = 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)
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)
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)
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)
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)
assert (
"Error occured trying to change 'attr_list[10]': list index "