nova-trame 0.19.0.dev1__py3-none-any.whl → 0.19.0.dev2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nova/trame/model/data_selector.py +59 -7
- nova/trame/view/components/data_selector.py +65 -8
- nova/trame/view/components/file_upload.py +14 -15
- nova/trame/view/components/remote_file_input.py +17 -3
- nova/trame/view/theme/assets/core_style.scss +35 -0
- nova/trame/view/theme/assets/vuetify_config.json +4 -1
- nova/trame/view_model/data_selector.py +8 -1
- {nova_trame-0.19.0.dev1.dist-info → nova_trame-0.19.0.dev2.dist-info}/METADATA +1 -1
- {nova_trame-0.19.0.dev1.dist-info → nova_trame-0.19.0.dev2.dist-info}/RECORD +12 -12
- {nova_trame-0.19.0.dev1.dist-info → nova_trame-0.19.0.dev2.dist-info}/LICENSE +0 -0
- {nova_trame-0.19.0.dev1.dist-info → nova_trame-0.19.0.dev2.dist-info}/WHEEL +0 -0
- {nova_trame-0.19.0.dev1.dist-info → nova_trame-0.19.0.dev2.dist-info}/entry_points.txt +0 -0
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
import os
|
4
4
|
from pathlib import Path
|
5
|
-
from typing import List, Optional
|
5
|
+
from typing import Any, List, Optional
|
6
6
|
from warnings import warn
|
7
7
|
|
8
8
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
@@ -68,6 +68,8 @@ class DataSelectorState(BaseModel, validate_assignment=True):
|
|
68
68
|
facility: str = Field(default="", title="Facility")
|
69
69
|
instrument: str = Field(default="", title="Instrument")
|
70
70
|
experiment: str = Field(default="", title="Experiment")
|
71
|
+
directory: str = Field(default="")
|
72
|
+
prefix: str = Field(default="")
|
71
73
|
|
72
74
|
@field_validator("experiment", mode="after")
|
73
75
|
@classmethod
|
@@ -99,19 +101,20 @@ class DataSelectorState(BaseModel, validate_assignment=True):
|
|
99
101
|
class DataSelectorModel:
|
100
102
|
"""Manages file system interactions for the DataSelector widget."""
|
101
103
|
|
102
|
-
def __init__(self, facility: str, instrument: str) -> None:
|
104
|
+
def __init__(self, facility: str, instrument: str, prefix: str) -> None:
|
103
105
|
self.state = DataSelectorState()
|
104
106
|
self.state.facility = facility
|
105
107
|
self.state.instrument = instrument
|
108
|
+
self.state.prefix = prefix
|
106
109
|
|
107
110
|
def get_facilities(self) -> List[str]:
|
108
|
-
return get_facilities()
|
111
|
+
return sorted(get_facilities())
|
109
112
|
|
110
113
|
def get_instrument_dir(self) -> str:
|
111
114
|
return INSTRUMENTS.get(self.state.facility, {}).get(self.state.instrument, "")
|
112
115
|
|
113
116
|
def get_instruments(self) -> List[str]:
|
114
|
-
return get_instruments(self.state.facility)
|
117
|
+
return sorted(get_instruments(self.state.facility))
|
115
118
|
|
116
119
|
def get_experiments(self) -> List[str]:
|
117
120
|
experiments = []
|
@@ -126,18 +129,67 @@ class DataSelectorModel:
|
|
126
129
|
|
127
130
|
return sorted(experiments)
|
128
131
|
|
132
|
+
def get_directories(self) -> List[Any]:
|
133
|
+
if not self.state.experiment:
|
134
|
+
return []
|
135
|
+
|
136
|
+
directories = []
|
137
|
+
|
138
|
+
experiment_path = Path("/") / self.state.facility / self.get_instrument_dir() / self.state.experiment
|
139
|
+
try:
|
140
|
+
for dirpath, _, _ in os.walk(experiment_path):
|
141
|
+
# Get the relative path from the start path
|
142
|
+
path_parts = os.path.relpath(dirpath, experiment_path).split(os.sep)
|
143
|
+
|
144
|
+
# Only create a new entry for top-level directories
|
145
|
+
if len(path_parts) == 1 and path_parts[0] != ".": # This indicates a top-level directory
|
146
|
+
current_dir = {"path": dirpath, "title": path_parts[0]}
|
147
|
+
directories.append(current_dir)
|
148
|
+
|
149
|
+
# Add subdirectories to the corresponding parent directory
|
150
|
+
elif len(path_parts) > 1:
|
151
|
+
current_level: Any = directories
|
152
|
+
for part in path_parts[:-1]: # Parent directories
|
153
|
+
for item in current_level:
|
154
|
+
if item["title"] == part:
|
155
|
+
if "children" not in item:
|
156
|
+
item["children"] = []
|
157
|
+
current_level = item["children"]
|
158
|
+
break
|
159
|
+
|
160
|
+
# Add the last part (current directory) as a child
|
161
|
+
current_level.append({"path": dirpath, "title": path_parts[-1]})
|
162
|
+
except OSError:
|
163
|
+
pass
|
164
|
+
|
165
|
+
return directories
|
166
|
+
|
129
167
|
def get_datafiles(self) -> List[str]:
|
130
168
|
datafiles = []
|
131
169
|
|
132
|
-
experiment_path = Path("/") / self.state.facility / self.get_instrument_dir() / self.state.experiment / "nexus"
|
133
170
|
try:
|
134
|
-
|
135
|
-
|
171
|
+
if self.state.prefix:
|
172
|
+
datafile_path = str(
|
173
|
+
Path("/")
|
174
|
+
/ self.state.facility
|
175
|
+
/ self.get_instrument_dir()
|
176
|
+
/ self.state.experiment
|
177
|
+
/ self.state.prefix
|
178
|
+
)
|
179
|
+
else:
|
180
|
+
datafile_path = self.state.directory
|
181
|
+
|
182
|
+
for entry in os.scandir(datafile_path):
|
183
|
+
if entry.is_file():
|
184
|
+
datafiles.append(entry.path)
|
136
185
|
except OSError:
|
137
186
|
pass
|
138
187
|
|
139
188
|
return sorted(datafiles)
|
140
189
|
|
190
|
+
def set_directory(self, directory_path: str) -> None:
|
191
|
+
self.state.directory = directory_path
|
192
|
+
|
141
193
|
def set_state(self, facility: Optional[str], instrument: Optional[str], experiment: Optional[str]) -> None:
|
142
194
|
if facility is not None:
|
143
195
|
self.state.facility = facility
|
@@ -1,8 +1,9 @@
|
|
1
1
|
"""View Implementation for DataSelector."""
|
2
2
|
|
3
|
-
from typing import Any, Optional
|
3
|
+
from typing import Any, Optional, cast
|
4
4
|
|
5
5
|
from trame.app import get_server
|
6
|
+
from trame.widgets import html
|
6
7
|
from trame.widgets import vuetify3 as vuetify
|
7
8
|
|
8
9
|
from nova.mvvm.trame_binding import TrameBinding
|
@@ -12,11 +13,21 @@ from nova.trame.view_model.data_selector import DataSelectorViewModel
|
|
12
13
|
|
13
14
|
from .input_field import InputField
|
14
15
|
|
16
|
+
vuetify.enable_lab()
|
17
|
+
|
15
18
|
|
16
19
|
class DataSelector(vuetify.VDataTable):
|
17
20
|
"""Allows the user to select datafiles from an IPTS experiment."""
|
18
21
|
|
19
|
-
def __init__(
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
v_model: str,
|
25
|
+
facility: str = "",
|
26
|
+
instrument: str = "",
|
27
|
+
prefix: str = "",
|
28
|
+
select_strategy: str = "all",
|
29
|
+
**kwargs: Any,
|
30
|
+
) -> None:
|
20
31
|
"""Constructor for DataSelector.
|
21
32
|
|
22
33
|
Parameters
|
@@ -28,6 +39,12 @@ class DataSelector(vuetify.VDataTable):
|
|
28
39
|
The facility to restrict data selection to. Options: HFIR, SNS
|
29
40
|
instrument : str, optional
|
30
41
|
The instrument to restrict data selection to. Please use the instrument acronym (e.g. CG-2).
|
42
|
+
prefix : str, optional
|
43
|
+
A subdirectory within the user's chosen experiment to show files. If not specified, the user will be shown a
|
44
|
+
folder browser and will be able to see all files in the experiment that they have access to.
|
45
|
+
select_strategy : str, optional
|
46
|
+
The selection strategy to pass to the `VDataTable component <https://trame.readthedocs.io/en/latest/trame.widgets.vuetify3.html#trame.widgets.vuetify3.VDataTable>`__.
|
47
|
+
If unset, the `all` strategy will be used.
|
31
48
|
**kwargs
|
32
49
|
All other arguments will be passed to the underlying
|
33
50
|
`VDataTable component <https://trame.readthedocs.io/en/latest/trame.widgets.vuetify3.html#trame.widgets.vuetify3.VDataTable>`_.
|
@@ -39,11 +56,20 @@ class DataSelector(vuetify.VDataTable):
|
|
39
56
|
if "items" in kwargs:
|
40
57
|
raise AttributeError("The items parameter is not allowed on DataSelector widget.")
|
41
58
|
|
59
|
+
if "label" in kwargs:
|
60
|
+
self._label = kwargs["label"]
|
61
|
+
else:
|
62
|
+
self._label = None
|
63
|
+
|
42
64
|
self._v_model = v_model
|
65
|
+
self._prefix = prefix
|
66
|
+
self._select_strategy = select_strategy
|
67
|
+
|
43
68
|
self._state_name = f"nova__dataselector_{self._next_id}_state"
|
44
69
|
self._facilities_name = f"nova__dataselector_{self._next_id}_facilities"
|
45
70
|
self._instruments_name = f"nova__dataselector_{self._next_id}_instruments"
|
46
71
|
self._experiments_name = f"nova__dataselector_{self._next_id}_experiments"
|
72
|
+
self._directories_name = f"nova__dataselector_{self._next_id}_directories"
|
47
73
|
self._datafiles_name = f"nova__dataselector_{self._next_id}_datafiles"
|
48
74
|
|
49
75
|
self.create_model(facility, instrument)
|
@@ -69,22 +95,52 @@ class DataSelector(vuetify.VDataTable):
|
|
69
95
|
type="autocomplete",
|
70
96
|
)
|
71
97
|
|
98
|
+
with GridLayout(columns=3, valign="start"):
|
99
|
+
if not self._prefix:
|
100
|
+
with html.Div():
|
101
|
+
vuetify.VListSubheader("Available Directories", classes="justify-center px-0")
|
102
|
+
vuetify.VTreeview(
|
103
|
+
v_if=(f"{self._directories_name}.length > 0",),
|
104
|
+
activatable=True,
|
105
|
+
active_strategy="single-independent",
|
106
|
+
item_value="path",
|
107
|
+
items=(self._directories_name,),
|
108
|
+
update_activated=(self._vm.set_directory, "$event"),
|
109
|
+
)
|
110
|
+
vuetify.VListItem("No directories found", v_else=True)
|
111
|
+
|
72
112
|
super().__init__(
|
73
113
|
v_model=self._v_model,
|
74
|
-
column_span=3,
|
75
|
-
headers=("[{ align: 'center', key: '
|
76
|
-
|
77
|
-
|
114
|
+
column_span=3 if self._prefix else 2,
|
115
|
+
headers=("[{ align: 'center', key: 'title', title: 'Available Datafiles' }]",),
|
116
|
+
item_title="title",
|
117
|
+
item_value="path",
|
118
|
+
select_strategy=self._select_strategy,
|
78
119
|
show_select=True,
|
79
120
|
**kwargs,
|
80
121
|
)
|
81
122
|
self.items = (self._datafiles_name,)
|
82
|
-
|
83
123
|
if "update_modelValue" not in kwargs:
|
84
124
|
self.update_modelValue = f"flushState('{self._v_model.split('.')[0]}')"
|
85
125
|
|
126
|
+
with cast(
|
127
|
+
vuetify.VSelect,
|
128
|
+
InputField(
|
129
|
+
v_if=f"{self._v_model}.length > 0",
|
130
|
+
v_model=self._v_model,
|
131
|
+
classes="nova-readonly",
|
132
|
+
clearable=True,
|
133
|
+
label=self._label,
|
134
|
+
readonly=True,
|
135
|
+
type="select",
|
136
|
+
),
|
137
|
+
):
|
138
|
+
with vuetify.Template(raw_attrs=['v-slot:selection="{ item, index }"']):
|
139
|
+
vuetify.VChip("{{ item.title }}", v_if="index < 2")
|
140
|
+
html.Span(f"(+{{{{ {self._v_model}.length - 2 }}}} others)", v_if="index === 2", classes="text-caption")
|
141
|
+
|
86
142
|
def create_model(self, facility: str, instrument: str) -> None:
|
87
|
-
self._model = DataSelectorModel(facility, instrument)
|
143
|
+
self._model = DataSelectorModel(facility, instrument, self._prefix)
|
88
144
|
|
89
145
|
def create_viewmodel(self) -> None:
|
90
146
|
server = get_server(None, client_type="vue3")
|
@@ -95,6 +151,7 @@ class DataSelector(vuetify.VDataTable):
|
|
95
151
|
self._vm.facilities_bind.connect(self._facilities_name)
|
96
152
|
self._vm.instruments_bind.connect(self._instruments_name)
|
97
153
|
self._vm.experiments_bind.connect(self._experiments_name)
|
154
|
+
self._vm.directories_bind.connect(self._directories_name)
|
98
155
|
self._vm.datafiles_bind.connect(self._datafiles_name)
|
99
156
|
|
100
157
|
self._vm.update_view()
|
@@ -10,17 +10,14 @@ from .remote_file_input import RemoteFileInput
|
|
10
10
|
class FileUpload(vuetify.VBtn):
|
11
11
|
"""Component for uploading a file from either the user's filesystem or the server filesystem."""
|
12
12
|
|
13
|
-
def __init__(
|
14
|
-
self, v_model: Optional[str] = None, base_paths: Optional[List[str]] = None, label: str = "", **kwargs: Any
|
15
|
-
) -> None:
|
13
|
+
def __init__(self, v_model: str, base_paths: Optional[List[str]] = None, label: str = "", **kwargs: Any) -> None:
|
16
14
|
"""Constructor for FileUpload.
|
17
15
|
|
18
16
|
Parameters
|
19
17
|
----------
|
20
|
-
v_model : str
|
18
|
+
v_model : str
|
21
19
|
The state variable to set when the user uploads their file. If uploaded from the user's machine, then the
|
22
|
-
state variable will contain
|
23
|
-
filesystem, then the state variable will contain a path to the file.
|
20
|
+
state variable will contain the file contents.
|
24
21
|
base_paths: list[str], optional
|
25
22
|
Passed to :ref:`RemoteFileInput <api_remotefileinput>`.
|
26
23
|
label : str, optional
|
@@ -44,20 +41,22 @@ class FileUpload(vuetify.VBtn):
|
|
44
41
|
self.create_ui()
|
45
42
|
|
46
43
|
def create_ui(self) -> None:
|
47
|
-
self.local_file_input = vuetify.VFileInput(
|
48
|
-
|
44
|
+
self.local_file_input = vuetify.VFileInput(
|
45
|
+
v_model=(self._v_model, None),
|
46
|
+
classes="d-none",
|
47
|
+
ref=self._ref_name,
|
49
48
|
# Serialize the content in a way that will work with nova-mvvm and then push it to the server.
|
50
|
-
|
51
|
-
f"{self._v_model}.text().then((contents) => {{
|
52
|
-
f" {self._v_model} =
|
49
|
+
update_modelValue=(
|
50
|
+
f"{self._v_model}.text().then((contents) => {{"
|
51
|
+
f" {self._v_model} = contents;"
|
53
52
|
f" flushState('{self._v_model.split('.')[0]}');"
|
54
53
|
"});"
|
55
|
-
)
|
54
|
+
),
|
55
|
+
)
|
56
56
|
self.remote_file_input = RemoteFileInput(
|
57
|
-
v_model=self._v_model,
|
58
|
-
base_paths=self._base_paths,
|
59
|
-
input_props={"classes": "d-none"},
|
57
|
+
v_model=self._v_model, base_paths=self._base_paths, input_props={"classes": "d-none"}, return_contents=True
|
60
58
|
)
|
59
|
+
|
61
60
|
with self:
|
62
61
|
with vuetify.VMenu(activator="parent"):
|
63
62
|
with vuetify.VList():
|
@@ -31,6 +31,7 @@ class RemoteFileInput:
|
|
31
31
|
dialog_props: Optional[dict[str, Any]] = None,
|
32
32
|
extensions: Optional[list[str]] = None,
|
33
33
|
input_props: Optional[dict[str, Any]] = None,
|
34
|
+
return_contents: bool = False,
|
34
35
|
) -> None:
|
35
36
|
"""Constructor for RemoteFileInput.
|
36
37
|
|
@@ -53,6 +54,9 @@ class RemoteFileInput:
|
|
53
54
|
Only files with these extensions will be shown by default. The user can still choose to view all files.
|
54
55
|
input_props : dict[str, typing.Any], optional
|
55
56
|
Props to be passed to InputField.
|
57
|
+
return_contents : bool
|
58
|
+
If true, then the v_model will contain the contents of the file. If false, then the v_model will contain the
|
59
|
+
path of the file.
|
56
60
|
|
57
61
|
Raises
|
58
62
|
------
|
@@ -74,6 +78,7 @@ class RemoteFileInput:
|
|
74
78
|
self.dialog_props = dict(dialog_props) if dialog_props else {}
|
75
79
|
self.extensions = extensions if extensions else []
|
76
80
|
self.input_props = dict(input_props) if input_props else {}
|
81
|
+
self.return_contents = return_contents
|
77
82
|
|
78
83
|
if "__events" not in self.input_props:
|
79
84
|
self.input_props["__events"] = []
|
@@ -179,18 +184,27 @@ class RemoteFileInput:
|
|
179
184
|
else:
|
180
185
|
model_name = self.v_model
|
181
186
|
|
187
|
+
self.set_v_model = client.JSEval(
|
188
|
+
exec=f"{model_name} = $event; flushState('{model_name.split('.')[0].split('[')[0]}');"
|
189
|
+
).exec
|
190
|
+
|
182
191
|
self.vm = RemoteFileInputViewModel(self.model, binding)
|
183
192
|
|
184
193
|
self.vm.dialog_bind.connect(self.vm.get_dialog_state_name())
|
185
194
|
self.vm.file_list_bind.connect(self.vm.get_file_list_state_name())
|
186
195
|
self.vm.filter_bind.connect(self.vm.get_filter_state_name())
|
187
196
|
self.vm.on_close_bind.connect(client.JSEval(exec=f"{self.vm.get_dialog_state_name()} = false;").exec)
|
188
|
-
self.
|
189
|
-
|
190
|
-
|
197
|
+
if self.return_contents:
|
198
|
+
self.vm.on_update_bind.connect(self.read_file)
|
199
|
+
else:
|
200
|
+
self.vm.on_update_bind.connect(self.set_v_model)
|
191
201
|
self.vm.showing_all_bind.connect(self.vm.get_showing_all_state_name())
|
192
202
|
self.vm.valid_selection_bind.connect(self.vm.get_valid_selection_state_name())
|
193
203
|
|
204
|
+
def read_file(self, file_path: str) -> None:
|
205
|
+
with open(file_path, mode="r") as file:
|
206
|
+
self.set_v_model(file.read())
|
207
|
+
|
194
208
|
def select_file(self, value: str) -> None:
|
195
209
|
"""Programmatically set the v_model value."""
|
196
210
|
self.vm.select_file(value)
|
@@ -20,6 +20,12 @@ html {
|
|
20
20
|
resize: none !important;
|
21
21
|
}
|
22
22
|
|
23
|
+
.nova-readonly {
|
24
|
+
.v-select__menu-icon {
|
25
|
+
display: none;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
23
29
|
@media only screen and (max-width: 959px) {
|
24
30
|
.d-grid {
|
25
31
|
grid-template-columns: repeat(1, 1fr) !important;
|
@@ -42,6 +48,16 @@ html {
|
|
42
48
|
border-radius: 4px;
|
43
49
|
}
|
44
50
|
|
51
|
+
.v-data-table {
|
52
|
+
td {
|
53
|
+
vertical-align: middle;
|
54
|
+
}
|
55
|
+
|
56
|
+
.v-divider {
|
57
|
+
margin: 0;
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
45
61
|
.v-tab.v-btn {
|
46
62
|
height: 30px !important;
|
47
63
|
min-width: fit-content !important;
|
@@ -85,4 +101,23 @@ html {
|
|
85
101
|
.d-grid {
|
86
102
|
align-items:center;
|
87
103
|
}
|
104
|
+
|
105
|
+
.v-treeview {
|
106
|
+
.v-list-item {
|
107
|
+
text-align: left;
|
108
|
+
|
109
|
+
.v-list-item__prepend {
|
110
|
+
width: 40px;
|
111
|
+
}
|
112
|
+
|
113
|
+
.v-btn {
|
114
|
+
height: 24px;
|
115
|
+
width: 24px;
|
116
|
+
|
117
|
+
.v-icon {
|
118
|
+
font-size: 16px;
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
}
|
88
123
|
}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
"""View model implementation for the DataSelector widget."""
|
2
2
|
|
3
|
+
import os
|
3
4
|
from typing import Any, Optional
|
4
5
|
|
5
6
|
from nova.mvvm.interface import BindingInterface
|
@@ -16,8 +17,13 @@ class DataSelectorViewModel:
|
|
16
17
|
self.facilities_bind = binding.new_bind()
|
17
18
|
self.instruments_bind = binding.new_bind()
|
18
19
|
self.experiments_bind = binding.new_bind()
|
20
|
+
self.directories_bind = binding.new_bind()
|
19
21
|
self.datafiles_bind = binding.new_bind()
|
20
22
|
|
23
|
+
def set_directory(self, directory_path: str) -> None:
|
24
|
+
self.model.set_directory(directory_path)
|
25
|
+
self.update_view()
|
26
|
+
|
21
27
|
def set_state(self, facility: Optional[str], instrument: Optional[str], experiment: Optional[str]) -> None:
|
22
28
|
self.model.set_state(facility, instrument, experiment)
|
23
29
|
self.update_view()
|
@@ -27,7 +33,8 @@ class DataSelectorViewModel:
|
|
27
33
|
self.facilities_bind.update_in_view(self.model.get_facilities())
|
28
34
|
self.instruments_bind.update_in_view(self.model.get_instruments())
|
29
35
|
self.experiments_bind.update_in_view(self.model.get_experiments())
|
36
|
+
self.directories_bind.update_in_view(self.model.get_directories())
|
30
37
|
|
31
38
|
datafile_paths = self.model.get_datafiles()
|
32
|
-
datafile_options = [{"
|
39
|
+
datafile_options = [{"path": datafile, "title": os.path.basename(datafile)} for datafile in datafile_paths]
|
33
40
|
self.datafiles_bind.update_in_view(datafile_options)
|
@@ -1,12 +1,12 @@
|
|
1
1
|
nova/__init__.py,sha256=ED6jHcYiuYpr_0vjGz0zx2lrrmJT9sDJCzIljoDfmlM,65
|
2
2
|
nova/trame/__init__.py,sha256=gFrAg1qva5PIqR5TjvPzAxLx103IKipJLqp3XXvrQL8,59
|
3
|
-
nova/trame/model/data_selector.py,sha256=
|
3
|
+
nova/trame/model/data_selector.py,sha256=8BnnPvdYZTmCOJFuWOs_RhQ1NvYqWFdPmWP09WWP5oA,6689
|
4
4
|
nova/trame/model/remote_file_input.py,sha256=9KAf31ZHzpsh_aXUrNcF81Q5jvUZDWCzW1QATKls-Jk,3675
|
5
5
|
nova/trame/view/components/__init__.py,sha256=u8yzshFp_TmuC1g9TRxKjy_BdGWMIzPQouI52hzcr2U,234
|
6
|
-
nova/trame/view/components/data_selector.py,sha256
|
7
|
-
nova/trame/view/components/file_upload.py,sha256=
|
6
|
+
nova/trame/view/components/data_selector.py,sha256=vRbN2FlcfnUWpmL4MFfRqrbhXQWip2_QLMoMg9KFXCs,7584
|
7
|
+
nova/trame/view/components/file_upload.py,sha256=XbiSx2txpdohwxGyP-ecTbIgMPv6siUARJ7nXhXaiAc,2827
|
8
8
|
nova/trame/view/components/input_field.py,sha256=ncVVSzdJwH_-KP24I2rCqcb6v3J2hPhNTXr8Lb1EZ_U,15931
|
9
|
-
nova/trame/view/components/remote_file_input.py,sha256=
|
9
|
+
nova/trame/view/components/remote_file_input.py,sha256=lFchhhoMo9EgSr7pSlh2LEm8NZO1XXrBcfh_BLGZBV0,9492
|
10
10
|
nova/trame/view/components/visualization/__init__.py,sha256=reqkkbhD5uSksHHlhVMy1qNUCwSekS5HlXk6wCREYxU,152
|
11
11
|
nova/trame/view/components/visualization/interactive_2d_plot.py,sha256=foZCMoqbuahT5dtqIQvm8C4ZJcY9P211eJEcpQJltmM,3421
|
12
12
|
nova/trame/view/components/visualization/matplotlib_figure.py,sha256=yop7Kd_MylUiCwEial2jOYESbvchrYhrpSmRowUhePY,12003
|
@@ -15,18 +15,18 @@ nova/trame/view/layouts/grid.py,sha256=k-QHuH31XeAVDuMKUMoAMVnAM-Yavq7kdLYOC1ZrG
|
|
15
15
|
nova/trame/view/layouts/hbox.py,sha256=r5irhFX6YWTWN4V4NwNQx6mheyM8p6PVcJbrbhvOAwo,2625
|
16
16
|
nova/trame/view/layouts/vbox.py,sha256=Q4EvrtGJORyNF6AnCLGXToy8XU6yofiO5_kt7hK-AYs,2626
|
17
17
|
nova/trame/view/theme/__init__.py,sha256=70_marDlTigIcPEOGiJb2JTs-8b2sGM5SlY7XBPtBDM,54
|
18
|
-
nova/trame/view/theme/assets/core_style.scss,sha256=
|
18
|
+
nova/trame/view/theme/assets/core_style.scss,sha256=bP9tv5B3vivjEMXFPTGJ7rXc1aXjnOXsvnkaBUyRmUs,2095
|
19
19
|
nova/trame/view/theme/assets/favicon.png,sha256=Xbp1nUmhcBDeObjsebEbEAraPDZ_M163M_ZLtm5AbQc,1927
|
20
20
|
nova/trame/view/theme/assets/js/delay_manager.js,sha256=vmb34DZ5YCQIlRW9Tf2M_uvJW6HFCmtlKZ5e_TPR8yg,536
|
21
21
|
nova/trame/view/theme/assets/js/lodash.debounce.min.js,sha256=GLzlQH04WDUNYN7i39ttHHejSdu-CpAvfWgDgKDn-OY,4448
|
22
22
|
nova/trame/view/theme/assets/js/lodash.throttle.min.js,sha256=9csqjX-M-LVGJnF3z4ha1R_36O5AfkFE8rPHkxmt3tE,4677
|
23
|
-
nova/trame/view/theme/assets/vuetify_config.json,sha256=
|
23
|
+
nova/trame/view/theme/assets/vuetify_config.json,sha256=HuR23Ds1dATt5fFUFMolFoLRjINLzg7AiLgQpQvuabc,5567
|
24
24
|
nova/trame/view/theme/theme.py,sha256=OFUtq1IWriFcDu-346J67ZrSES8IOI9PTY_4Vwg7bZQ,11820
|
25
25
|
nova/trame/view/utilities/local_storage.py,sha256=vD8f2VZIpxhIKjZwEaD7siiPCTZO4cw9AfhwdawwYLY,3218
|
26
|
-
nova/trame/view_model/data_selector.py,sha256=
|
26
|
+
nova/trame/view_model/data_selector.py,sha256=Bpkfjd78ZIkexHNF_aA9PfizRBzkeOuoSE7ZsBFcSOs,1749
|
27
27
|
nova/trame/view_model/remote_file_input.py,sha256=ojEOJ8ZPkajpbAaZi9VLj7g-uBjhb8BMrTdMmwf_J6A,3367
|
28
|
-
nova_trame-0.19.0.
|
29
|
-
nova_trame-0.19.0.
|
30
|
-
nova_trame-0.19.0.
|
31
|
-
nova_trame-0.19.0.
|
32
|
-
nova_trame-0.19.0.
|
28
|
+
nova_trame-0.19.0.dev2.dist-info/LICENSE,sha256=Iu5QiDbwNbREg75iYaxIJ_V-zppuv4QFuBhAW-qiAlM,1061
|
29
|
+
nova_trame-0.19.0.dev2.dist-info/METADATA,sha256=7HZZEW7-Vs_z1Oo0T1AUeIUOodsk5Z0sE60yloyGXns,1451
|
30
|
+
nova_trame-0.19.0.dev2.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
31
|
+
nova_trame-0.19.0.dev2.dist-info/entry_points.txt,sha256=J2AmeSwiTYZ4ZqHHp9HO6v4MaYQTTBPbNh6WtoqOT58,42
|
32
|
+
nova_trame-0.19.0.dev2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|