nova-trame 0.18.0.dev0__py3-none-any.whl → 0.19.0.dev0__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.
@@ -2,65 +2,99 @@
2
2
 
3
3
  import os
4
4
  from pathlib import Path
5
- from typing import List
5
+ from typing import List, Optional
6
+ from warnings import warn
6
7
 
7
- from pydantic import BaseModel, Field
8
+ from pydantic import BaseModel, Field, field_validator, model_validator
9
+ from typing_extensions import Self
8
10
 
9
- FACILITIES = ["HFIR", "SNS"]
10
11
  INSTRUMENTS = {
11
- "HFIR": [
12
- "CG1A",
13
- "CG1B",
14
- "CG1D",
15
- "CG2",
16
- "CG3",
17
- "CG4B",
18
- "CG4C",
19
- "CG4D",
20
- "HB1",
21
- "HB1A",
22
- "HB2A",
23
- "HB2B",
24
- "HB2C",
25
- "HB3",
26
- "HB3A",
27
- "NOWG",
28
- "NOWV",
29
- ],
30
- "SNS": [
31
- "ARCS",
32
- "BL0",
33
- "BSS",
34
- "CNCS",
35
- "CORELLI",
36
- "EQSANS",
37
- "HYS",
38
- "LENS",
39
- "MANDI",
40
- "NOM",
41
- "NOWG",
42
- "NSE",
43
- "PG3",
44
- "REF_L",
45
- "REF_M",
46
- "SEQ",
47
- "SNAP",
48
- "TOPAZ",
49
- "USANS",
50
- "VENUS",
51
- "VIS",
52
- "VULCAN",
53
- ],
12
+ "HFIR": {
13
+ "CG-1A": "CG1A",
14
+ "CG-1B": "CG1B",
15
+ "CG-1D": "CG1D",
16
+ "CG-2": "CG2",
17
+ "CG-3": "CG3",
18
+ "CG-4B": "CG4B",
19
+ "CG-4C": "CG4C",
20
+ "CG-4D": "CG4D",
21
+ "HB-1": "HB1",
22
+ "HB-1A": "HB1A",
23
+ "HB-2A": "HB2A",
24
+ "HB-2B": "HB2B",
25
+ "HB-2C": "HB2C",
26
+ "HB-3": "HB3",
27
+ "HB-3A": "HB3A",
28
+ "NOW-G": "NOWG",
29
+ "NOW-V": "NOWV",
30
+ },
31
+ "SNS": {
32
+ "BL-18": "ARCS",
33
+ "BL-0": "BL0",
34
+ "BL-2": "BSS",
35
+ "BL-5": "CNCS",
36
+ "BL-9": "CORELLI",
37
+ "BL-6": "EQSANS",
38
+ "BL-14B": "HYS",
39
+ "BL-11B": "MANDI",
40
+ "BL-1B": "NOM",
41
+ "NOW-G": "NOWG",
42
+ "BL-15": "NSE",
43
+ "BL-11A": "PG3",
44
+ "BL-4B": "REF_L",
45
+ "BL-4A": "REF_M",
46
+ "BL-17": "SEQ",
47
+ "BL-3": "SNAP",
48
+ "BL-12": "TOPAZ",
49
+ "BL-1A": "USANS",
50
+ "BL-10": "VENUS",
51
+ "BL-16B": "VIS",
52
+ "BL-7": "VULCAN",
53
+ },
54
54
  }
55
55
 
56
56
 
57
- class DataSelectorState(BaseModel):
57
+ def get_facilities() -> List[str]:
58
+ return list(INSTRUMENTS.keys())
59
+
60
+
61
+ def get_instruments(facility: str) -> List[str]:
62
+ return list(INSTRUMENTS.get(facility, {}).keys())
63
+
64
+
65
+ class DataSelectorState(BaseModel, validate_assignment=True):
58
66
  """Selection state for identifying datafiles."""
59
67
 
60
68
  facility: str = Field(default="", title="Facility")
61
69
  instrument: str = Field(default="", title="Instrument")
62
70
  experiment: str = Field(default="", title="Experiment")
63
71
 
72
+ @field_validator("experiment", mode="after")
73
+ @classmethod
74
+ def validate_experiment(cls, experiment: str) -> str:
75
+ if experiment and not experiment.startswith("IPTS-"):
76
+ raise ValueError("experiment must begin with IPTS-")
77
+ return experiment
78
+
79
+ @model_validator(mode="after")
80
+ def validate_state(self) -> Self:
81
+ valid_facilities = get_facilities()
82
+ if self.facility and self.facility not in valid_facilities:
83
+ warn(f"Facility '{self.facility}' could not be found. Valid options: {valid_facilities}", stacklevel=1)
84
+
85
+ valid_instruments = get_instruments(self.facility)
86
+ if self.instrument and self.instrument not in valid_instruments:
87
+ warn(
88
+ (
89
+ f"Instrument '{self.instrument}' could not be found in '{self.facility}'. "
90
+ f"Valid options: {valid_instruments}"
91
+ ),
92
+ stacklevel=1,
93
+ )
94
+ # Validating the experiment is expensive and will fail in our CI due to the filesystem not being mounted there.
95
+
96
+ return self
97
+
64
98
 
65
99
  class DataSelectorModel:
66
100
  """Manages file system interactions for the DataSelector widget."""
@@ -71,32 +105,43 @@ class DataSelectorModel:
71
105
  self.state.instrument = instrument
72
106
 
73
107
  def get_facilities(self) -> List[str]:
74
- return FACILITIES
108
+ return get_facilities()
109
+
110
+ def get_instrument_dir(self) -> str:
111
+ return INSTRUMENTS.get(self.state.facility, {}).get(self.state.instrument, "")
75
112
 
76
113
  def get_instruments(self) -> List[str]:
77
- return INSTRUMENTS.get(self.state.facility, [])
114
+ return get_instruments(self.state.facility)
78
115
 
79
116
  def get_experiments(self) -> List[str]:
80
117
  experiments = []
81
118
 
82
- instrument_path = Path("/") / self.state.facility / self.state.instrument
119
+ instrument_path = Path("/") / self.state.facility / self.get_instrument_dir()
83
120
  try:
84
121
  for dirname in os.listdir(instrument_path):
85
- if dirname.startswith("IPTS-"):
122
+ if dirname.startswith("IPTS-") and os.access(instrument_path / dirname, mode=os.R_OK):
86
123
  experiments.append(dirname)
87
- except FileNotFoundError:
124
+ except OSError:
88
125
  pass
89
126
 
90
- return experiments
127
+ return sorted(experiments)
91
128
 
92
129
  def get_datafiles(self) -> List[str]:
93
130
  datafiles = []
94
131
 
95
- experiment_path = Path("/") / self.state.facility / self.state.instrument / self.state.experiment / "nexus"
132
+ experiment_path = Path("/") / self.state.facility / self.get_instrument_dir() / self.state.experiment / "nexus"
96
133
  try:
97
134
  for fname in os.listdir(experiment_path):
98
135
  datafiles.append(str(experiment_path / fname))
99
- except FileNotFoundError:
136
+ except OSError:
100
137
  pass
101
138
 
102
- return datafiles
139
+ return sorted(datafiles)
140
+
141
+ def set_state(self, facility: Optional[str], instrument: Optional[str], experiment: Optional[str]) -> None:
142
+ if facility is not None:
143
+ self.state.facility = facility
144
+ if instrument is not None:
145
+ self.state.instrument = instrument
146
+ if experiment is not None:
147
+ self.state.experiment = experiment
@@ -1,5 +1,6 @@
1
1
  from .data_selector import DataSelector
2
+ from .file_upload import FileUpload
2
3
  from .input_field import InputField
3
4
  from .remote_file_input import RemoteFileInput
4
5
 
5
- __all__ = ["DataSelector", "InputField", "RemoteFileInput"]
6
+ __all__ = ["DataSelector", "FileUpload", "InputField", "RemoteFileInput"]
@@ -1,25 +1,45 @@
1
1
  """View Implementation for DataSelector."""
2
2
 
3
- from typing import Any
3
+ from typing import Any, Optional
4
4
 
5
5
  from trame.app import get_server
6
6
  from trame.widgets import vuetify3 as vuetify
7
7
 
8
8
  from nova.mvvm.trame_binding import TrameBinding
9
9
  from nova.trame.model.data_selector import DataSelectorModel
10
- from nova.trame.view.layouts import HBoxLayout
10
+ from nova.trame.view.layouts import GridLayout
11
11
  from nova.trame.view_model.data_selector import DataSelectorViewModel
12
12
 
13
13
  from .input_field import InputField
14
14
 
15
15
 
16
- class DataSelector(vuetify.VAutocomplete):
16
+ class DataSelector(vuetify.VDataTable):
17
17
  """Allows the user to select datafiles from an IPTS experiment."""
18
18
 
19
- def __init__(self, facility: str = "", instrument: str = "", **kwargs: Any) -> None:
19
+ def __init__(self, v_model: str, facility: str = "", instrument: str = "", **kwargs: Any) -> None:
20
+ """Constructor for DataSelector.
21
+
22
+ Parameters
23
+ ----------
24
+ v_model : str
25
+ The name of the state variable to bind to this widget. The state variable will contain a list of the files
26
+ selected by the user.
27
+ facility : str, optional
28
+ The facility to restrict data selection to. Options: HFIR, SNS
29
+ instrument : str, optional
30
+ The instrument to restrict data selection to. Please use the instrument acronym (e.g. CG-2).
31
+ **kwargs
32
+ All other arguments will be passed to the underlying
33
+ `VDataTable component <https://trame.readthedocs.io/en/latest/trame.widgets.vuetify3.html#trame.widgets.vuetify3.VDataTable>`_.
34
+
35
+ Returns
36
+ -------
37
+ None
38
+ """
20
39
  if "items" in kwargs:
21
40
  raise AttributeError("The items parameter is not allowed on DataSelector widget.")
22
41
 
42
+ self._v_model = v_model
23
43
  self._state_name = f"nova__dataselector_{self._next_id}_state"
24
44
  self._facilities_name = f"nova__dataselector_{self._next_id}_facilities"
25
45
  self._instruments_name = f"nova__dataselector_{self._next_id}_instruments"
@@ -32,18 +52,37 @@ class DataSelector(vuetify.VAutocomplete):
32
52
  self.create_ui(facility, instrument, **kwargs)
33
53
 
34
54
  def create_ui(self, facility: str, instrument: str, **kwargs: Any) -> None:
35
- with HBoxLayout(width="100%"):
55
+ with GridLayout(columns=3):
56
+ columns = 3
36
57
  if facility == "":
58
+ columns -= 1
37
59
  InputField(v_model=f"{self._state_name}.facility", items=(self._facilities_name,), type="autocomplete")
38
60
  if instrument == "":
61
+ columns -= 1
39
62
  InputField(
40
63
  v_model=f"{self._state_name}.instrument", items=(self._instruments_name,), type="autocomplete"
41
64
  )
42
- InputField(v_model=f"{self._state_name}.experiment", items=(self._experiments_name,), type="autocomplete")
43
-
44
- super().__init__(**kwargs)
65
+ InputField(
66
+ v_model=f"{self._state_name}.experiment",
67
+ column_span=columns,
68
+ items=(self._experiments_name,),
69
+ type="autocomplete",
70
+ )
71
+
72
+ super().__init__(
73
+ v_model=self._v_model,
74
+ column_span=3,
75
+ headers=("[{ align: 'center', key: 'file', title: 'Available Datafiles' }]",),
76
+ item_value="file",
77
+ select_strategy="all",
78
+ show_select=True,
79
+ **kwargs,
80
+ )
45
81
  self.items = (self._datafiles_name,)
46
82
 
83
+ if "update_modelValue" not in kwargs:
84
+ self.update_modelValue = f"flushState('{self._v_model.split('.')[0]}')"
85
+
47
86
  def create_model(self, facility: str, instrument: str) -> None:
48
87
  self._model = DataSelectorModel(facility, instrument)
49
88
 
@@ -59,3 +98,27 @@ class DataSelector(vuetify.VAutocomplete):
59
98
  self._vm.datafiles_bind.connect(self._datafiles_name)
60
99
 
61
100
  self._vm.update_view()
101
+
102
+ def set_state(
103
+ self, facility: Optional[str] = None, instrument: Optional[str] = None, experiment: Optional[str] = None
104
+ ) -> None:
105
+ """Programmatically set the facility, instrument, and/or experiment to restrict data selection to.
106
+
107
+ If a parameter is None, then it will not be updated.
108
+
109
+ Parameters
110
+ ----------
111
+ facility : str, optional
112
+ The facility to restrict data selection to. Options: HFIR, SNS
113
+ instrument : str, optional
114
+ The instrument to restrict data selection to. Must be at the selected facility.
115
+ experiment : str, optional
116
+ The experiment to restrict data selection to. Must begin with "IPTS-". It is your responsibility to validate
117
+ that the provided experiment exists within the instrument directory. If it doesn't then no datafiles will be
118
+ shown to the user.
119
+
120
+ Returns
121
+ -------
122
+ None
123
+ """
124
+ self._vm.set_state(facility, instrument, experiment)
@@ -0,0 +1,79 @@
1
+ """View implementation for FileUpload."""
2
+
3
+ from typing import Any, List, Optional
4
+
5
+ from trame.widgets import vuetify3 as vuetify
6
+
7
+ from .remote_file_input import RemoteFileInput
8
+
9
+
10
+ class FileUpload(vuetify.VBtn):
11
+ """Component for uploading a file from either the user's filesystem or the server filesystem."""
12
+
13
+ def __init__(
14
+ self, v_model: Optional[str] = None, base_paths: Optional[List[str]] = None, label: str = "", **kwargs: Any
15
+ ) -> None:
16
+ """Constructor for FileUpload.
17
+
18
+ Parameters
19
+ ----------
20
+ v_model : str, optional
21
+ 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 a dictionary with the file contents and metadata. If uploaded from the server
23
+ filesystem, then the state variable will contain a path to the file.
24
+ base_paths: list[str], optional
25
+ Passed to :ref:`RemoteFileInput <api_remotefileinput>`.
26
+ label : str, optional
27
+ The text to display on the upload button.
28
+ **kwargs
29
+ All other arguments will be passed to the underlying
30
+ `Button component <https://trame.readthedocs.io/en/latest/trame.widgets.vuetify3.html#trame.widgets.vuetify3.VBtn>`_.
31
+
32
+ Returns
33
+ -------
34
+ None
35
+ """
36
+ self._v_model = v_model
37
+ if base_paths:
38
+ self._base_paths = base_paths
39
+ else:
40
+ self._base_paths = ["/"]
41
+ self._ref_name = f"nova__fileupload_{self._next_id}"
42
+
43
+ super().__init__(label, **kwargs)
44
+ self.create_ui()
45
+
46
+ def create_ui(self) -> None:
47
+ self.local_file_input = vuetify.VFileInput(v_model=(self._v_model, None), classes="d-none", ref=self._ref_name)
48
+ if self._v_model:
49
+ # Serialize the content in a way that will work with nova-mvvm and then push it to the server.
50
+ self.local_file_input.update_modelValue = (
51
+ f"{self._v_model}.text().then((contents) => {{ "
52
+ f" {self._v_model} = {{ contents: contents }};"
53
+ f" flushState('{self._v_model.split('.')[0]}');"
54
+ "});"
55
+ )
56
+ self.remote_file_input = RemoteFileInput(
57
+ v_model=self._v_model,
58
+ base_paths=self._base_paths,
59
+ input_props={"classes": "d-none"},
60
+ )
61
+ with self:
62
+ with vuetify.VMenu(activator="parent"):
63
+ with vuetify.VList():
64
+ vuetify.VListItem("From Local Machine", click=f"trame.refs.{self._ref_name}.click()")
65
+ vuetify.VListItem("From Analysis Cluster", click=self.remote_file_input.open_dialog)
66
+
67
+ def select_file(self, value: str) -> None:
68
+ """Programmatically set the RemoteFileInput path.
69
+
70
+ Parameters
71
+ ----------
72
+ value: str
73
+ The new value for the RemoteFileInput.
74
+
75
+ Returns
76
+ -------
77
+ None
78
+ """
79
+ self.remote_file_input.select_file(value)
@@ -10,9 +10,10 @@ from trame_client.widgets.core import AbstractElement
10
10
 
11
11
  from nova.mvvm.trame_binding import TrameBinding
12
12
  from nova.trame.model.remote_file_input import RemoteFileInputModel
13
- from nova.trame.view.components import InputField
14
13
  from nova.trame.view_model.remote_file_input import RemoteFileInputViewModel
15
14
 
15
+ from .input_field import InputField
16
+
16
17
 
17
18
  class RemoteFileInput:
18
19
  """Generates a file selection dialog for picking files off of the server.
@@ -189,3 +190,11 @@ class RemoteFileInput:
189
190
  )
190
191
  self.vm.showing_all_bind.connect(self.vm.get_showing_all_state_name())
191
192
  self.vm.valid_selection_bind.connect(self.vm.get_valid_selection_state_name())
193
+
194
+ def select_file(self, value: str) -> None:
195
+ """Programmatically set the v_model value."""
196
+ self.vm.select_file(value)
197
+
198
+ def open_dialog(self) -> None:
199
+ """Programmatically opens the dialog for selecting a file."""
200
+ self.vm.open_dialog()
@@ -12,12 +12,6 @@ html {
12
12
  box-shadow: none !important;
13
13
  }
14
14
 
15
- .v-tab.v-btn {
16
- height: 30px !important;
17
- min-width: fit-content !important;
18
- padding: 10px !important;
19
- }
20
-
21
15
  .mpl-message, .ui-dialog-titlebar {
22
16
  display: none !important;
23
17
  }
@@ -48,6 +42,12 @@ html {
48
42
  border-radius: 4px;
49
43
  }
50
44
 
45
+ .v-tab.v-btn {
46
+ height: 30px !important;
47
+ min-width: fit-content !important;
48
+ padding: 10px !important;
49
+ }
50
+
51
51
  .v-card-title,
52
52
  .v-list-item-title,
53
53
  .v-toolbar-title {
@@ -56,8 +56,7 @@
56
56
  },
57
57
  "VFileInput": {
58
58
  "color": "primary",
59
- "prependIcon": false,
60
- "variant": "outlined"
59
+ "prependIcon": false
61
60
  },
62
61
  "VLabel": {
63
62
  "style": {
@@ -92,8 +91,7 @@
92
91
  "color": "primary"
93
92
  },
94
93
  "VSelect": {
95
- "color": "primary",
96
- "variant": "outlined"
94
+ "color": "primary"
97
95
  },
98
96
  "VSlider": {
99
97
  "color": "primary"
@@ -108,12 +106,10 @@
108
106
  "color": "primary"
109
107
  },
110
108
  "VTextarea": {
111
- "color": "primary",
112
- "variant": "outlined"
109
+ "color": "primary"
113
110
  },
114
111
  "VTextField": {
115
- "color": "primary",
116
- "variant": "outlined"
112
+ "color": "primary"
117
113
  },
118
114
  "VWindowItem": {
119
115
  "reverseTransition": "fade-transition",
@@ -156,12 +152,27 @@
156
152
  "secondary": "#f48e5c"
157
153
  },
158
154
  "defaults": {
155
+ "VAutocomplete": {
156
+ "variant": "outlined"
157
+ },
159
158
  "VBadge": {
160
159
  "dot": true
161
160
  },
162
161
  "VBtn": {
163
162
  "size": "small"
164
163
  },
164
+ "VCombobox": {
165
+ "variant": "outlined"
166
+ },
167
+ "VFileInput": {
168
+ "variant": "outlined"
169
+ },
170
+ "VSelect": {
171
+ "variant": "outlined"
172
+ },
173
+ "VTextarea": {
174
+ "variant": "outlined"
175
+ },
165
176
  "VTextField": {
166
177
  "VBtn": {
167
178
  "size": "small",
@@ -183,4 +194,4 @@
183
194
  "lighten": 5
184
195
  }
185
196
  }
186
- }
197
+ }
@@ -31,13 +31,6 @@ class ThemedApp:
31
31
  """Automatically injects theming into your Trame application.
32
32
 
33
33
  You should always inherit from this class when you define your Trame application.
34
-
35
- Currently, it supports two themes:
36
-
37
- 1. ModernTheme - The recommended theme for most applications. Leverages ORNL brand colors and a typical Vuetify \
38
- appearance.
39
- 2. CompactTheme - Similar to ModernTheme but with a smaller global font size and reduced margins and paddings on \
40
- all components.
41
34
  """
42
35
 
43
36
  def __init__(
@@ -151,7 +144,10 @@ class ThemedApp:
151
144
  Parameters
152
145
  ----------
153
146
  theme : str, optional
154
- The new theme to use. If the theme is not found, the default theme will be used.
147
+ The new theme to use. If the theme is not found, the default theme will be used. The available options are:
148
+
149
+ 1. ModernTheme (default) - Leverages ORNL brand colors and a typical Vuetify appearance.
150
+ 2. CompactTheme - Similar to ModernTheme but with a smaller global font size and increased density.
155
151
  force : bool, optional
156
152
  If True, the theme will be set even if the theme selection menu is disabled.
157
153
 
@@ -237,7 +233,7 @@ class ThemedApp:
237
233
  # [slot override example]
238
234
  layout.pre_content = vuetify.VSheet(classes="bg-background ")
239
235
  # [slot override example complete]
240
- with vuetify.VContainer(classes="flex-1-1 overflow-hidden pt-0 pb-0", fluid=True):
236
+ with vuetify.VContainer(classes="flex-0-1 overflow-hidden pt-0 pb-0", fluid=True):
241
237
  layout.content = html.Div(classes="h-100 overflow-y-auto pb-1 ")
242
238
  layout.post_content = vuetify.VSheet(classes="bg-background ")
243
239
 
@@ -1,7 +1,6 @@
1
1
  """View model implementation for the DataSelector widget."""
2
2
 
3
- import os
4
- from typing import Any
3
+ from typing import Any, Optional
5
4
 
6
5
  from nova.mvvm.interface import BindingInterface
7
6
  from nova.trame.model.data_selector import DataSelectorModel
@@ -19,6 +18,10 @@ class DataSelectorViewModel:
19
18
  self.experiments_bind = binding.new_bind()
20
19
  self.datafiles_bind = binding.new_bind()
21
20
 
21
+ def set_state(self, facility: Optional[str], instrument: Optional[str], experiment: Optional[str]) -> None:
22
+ self.model.set_state(facility, instrument, experiment)
23
+ self.update_view()
24
+
22
25
  def update_view(self, _: Any = None) -> None:
23
26
  self.state_bind.update_in_view(self.model.state)
24
27
  self.facilities_bind.update_in_view(self.model.get_facilities())
@@ -26,5 +29,5 @@ class DataSelectorViewModel:
26
29
  self.experiments_bind.update_in_view(self.model.get_experiments())
27
30
 
28
31
  datafile_paths = self.model.get_datafiles()
29
- datafile_options = [{"title": os.path.basename(datafile), "value": datafile} for datafile in datafile_paths]
32
+ datafile_options = [{"file": datafile} for datafile in datafile_paths]
30
33
  self.datafiles_bind.update_in_view(datafile_options)
@@ -35,6 +35,8 @@ class RemoteFileInputViewModel:
35
35
  self.previous_value = self.value
36
36
  self.populate_file_list()
37
37
 
38
+ self.dialog_bind.update_in_view(True)
39
+
38
40
  def close_dialog(self, cancel: bool = False) -> None:
39
41
  if not cancel:
40
42
  self.on_update_bind.update_in_view(self.value)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nova-trame
3
- Version: 0.18.0.dev0
3
+ Version: 0.19.0.dev0
4
4
  Summary: A Python Package for injecting curated themes and custom components into Trame applications
5
5
  License: MIT
6
6
  Keywords: NDIP,Python,Trame,Vuetify
@@ -25,6 +25,7 @@ Requires-Dist: trame-matplotlib
25
25
  Requires-Dist: trame-plotly
26
26
  Requires-Dist: trame-vega
27
27
  Requires-Dist: trame-vuetify
28
+ Project-URL: Changelog, https://code.ornl.gov/ndip/public-packages/nova-trame/blob/main/CHANGELOG.md
28
29
  Description-Content-Type: text/markdown
29
30
 
30
31
  nova-trame
@@ -1,11 +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=ONM7M3o0JJpF5w0Y1abGtPRWDLwb2A9cuHnEWhN5gR4,2371
3
+ nova/trame/model/data_selector.py,sha256=xM4j_BrKXClRbeDjfcCQEAnWWYuUod7GxiF1WXDpfls,4620
4
4
  nova/trame/model/remote_file_input.py,sha256=9KAf31ZHzpsh_aXUrNcF81Q5jvUZDWCzW1QATKls-Jk,3675
5
- nova/trame/view/components/__init__.py,sha256=N4ERzfeALckuXgZnGZSR0kLLUsGlTiPcfqljiM2vKTg,184
6
- nova/trame/view/components/data_selector.py,sha256=A46PdoruTNMHT6Z1gT2O5NeimXDRW7jtxjzhOu8gyIk,2631
5
+ nova/trame/view/components/__init__.py,sha256=u8yzshFp_TmuC1g9TRxKjy_BdGWMIzPQouI52hzcr2U,234
6
+ nova/trame/view/components/data_selector.py,sha256=-u73LayMwepOVLsWx6P_z5tBTPGX0ekzlI9fFVv2sNk,5107
7
+ nova/trame/view/components/file_upload.py,sha256=MafxZQE9fIuDFoizW2I4jAb7n82DPNaOvVhrrPEMbq8,3029
7
8
  nova/trame/view/components/input_field.py,sha256=ncVVSzdJwH_-KP24I2rCqcb6v3J2hPhNTXr8Lb1EZ_U,15931
8
- nova/trame/view/components/remote_file_input.py,sha256=k2yrwkell_g0sGnWR9XLL1LxkmFLr8-AGhduo8E-N4A,8669
9
+ nova/trame/view/components/remote_file_input.py,sha256=0qi4PnmqUBIdUv36dUqYsTWRQ4fHUJ0nlg9oXzA3Qfs,8929
9
10
  nova/trame/view/components/visualization/__init__.py,sha256=reqkkbhD5uSksHHlhVMy1qNUCwSekS5HlXk6wCREYxU,152
10
11
  nova/trame/view/components/visualization/interactive_2d_plot.py,sha256=foZCMoqbuahT5dtqIQvm8C4ZJcY9P211eJEcpQJltmM,3421
11
12
  nova/trame/view/components/visualization/matplotlib_figure.py,sha256=yop7Kd_MylUiCwEial2jOYESbvchrYhrpSmRowUhePY,12003
@@ -14,18 +15,18 @@ nova/trame/view/layouts/grid.py,sha256=k-QHuH31XeAVDuMKUMoAMVnAM-Yavq7kdLYOC1ZrG
14
15
  nova/trame/view/layouts/hbox.py,sha256=r5irhFX6YWTWN4V4NwNQx6mheyM8p6PVcJbrbhvOAwo,2625
15
16
  nova/trame/view/layouts/vbox.py,sha256=Q4EvrtGJORyNF6AnCLGXToy8XU6yofiO5_kt7hK-AYs,2626
16
17
  nova/trame/view/theme/__init__.py,sha256=70_marDlTigIcPEOGiJb2JTs-8b2sGM5SlY7XBPtBDM,54
17
- nova/trame/view/theme/assets/core_style.scss,sha256=OtiO9E4FWFPkSL25LG6lwHyjKwywOsODRe9aOvL3Fj4,1513
18
+ nova/trame/view/theme/assets/core_style.scss,sha256=zZj7klB_iieNNTdh3WADpRbCA6WII0-nG86MKFEBYWY,1533
18
19
  nova/trame/view/theme/assets/favicon.png,sha256=Xbp1nUmhcBDeObjsebEbEAraPDZ_M163M_ZLtm5AbQc,1927
19
20
  nova/trame/view/theme/assets/js/delay_manager.js,sha256=vmb34DZ5YCQIlRW9Tf2M_uvJW6HFCmtlKZ5e_TPR8yg,536
20
21
  nova/trame/view/theme/assets/js/lodash.debounce.min.js,sha256=GLzlQH04WDUNYN7i39ttHHejSdu-CpAvfWgDgKDn-OY,4448
21
22
  nova/trame/view/theme/assets/js/lodash.throttle.min.js,sha256=9csqjX-M-LVGJnF3z4ha1R_36O5AfkFE8rPHkxmt3tE,4677
22
- nova/trame/view/theme/assets/vuetify_config.json,sha256=T2AtYBCXsYj5KDMUP0drO2wF9AVVGX4gHbt7LLovPzw,4927
23
- nova/trame/view/theme/theme.py,sha256=18tHChrB1_qosjIiiLxMwWYRDTdXfgxEP4-4yJ8E3Cw,11893
23
+ nova/trame/view/theme/assets/vuetify_config.json,sha256=axz6cEWE_v1fO9XbM7cCKJghJp2DbU0lH6TuKYEG9j0,5311
24
+ nova/trame/view/theme/theme.py,sha256=OFUtq1IWriFcDu-346J67ZrSES8IOI9PTY_4Vwg7bZQ,11820
24
25
  nova/trame/view/utilities/local_storage.py,sha256=vD8f2VZIpxhIKjZwEaD7siiPCTZO4cw9AfhwdawwYLY,3218
25
- nova/trame/view_model/data_selector.py,sha256=NxTjJiiSXAt3b8E1p-z0qowznL0zMqqpIpHnuCvIYog,1276
26
- nova/trame/view_model/remote_file_input.py,sha256=WHWCQkZBGeKLe1aTPbtVNI8tn-PDt64mi1-561uuBpQ,3320
27
- nova_trame-0.18.0.dev0.dist-info/LICENSE,sha256=Iu5QiDbwNbREg75iYaxIJ_V-zppuv4QFuBhAW-qiAlM,1061
28
- nova_trame-0.18.0.dev0.dist-info/METADATA,sha256=xhfmwLX5SiQviHU9Ty_8hjOC167Mkc-QAPmiFA2pZzQ,1350
29
- nova_trame-0.18.0.dev0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
30
- nova_trame-0.18.0.dev0.dist-info/entry_points.txt,sha256=J2AmeSwiTYZ4ZqHHp9HO6v4MaYQTTBPbNh6WtoqOT58,42
31
- nova_trame-0.18.0.dev0.dist-info/RECORD,,
26
+ nova/trame/view_model/data_selector.py,sha256=baijpcbKCUk0BITPpbhHR6nAG-SF5ho2EyEfARTvDAo,1441
27
+ nova/trame/view_model/remote_file_input.py,sha256=ojEOJ8ZPkajpbAaZi9VLj7g-uBjhb8BMrTdMmwf_J6A,3367
28
+ nova_trame-0.19.0.dev0.dist-info/LICENSE,sha256=Iu5QiDbwNbREg75iYaxIJ_V-zppuv4QFuBhAW-qiAlM,1061
29
+ nova_trame-0.19.0.dev0.dist-info/METADATA,sha256=Sf87sw5O7ATqba0bt-KUVtkGrkmY6oKmueowQAKFPw0,1451
30
+ nova_trame-0.19.0.dev0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
31
+ nova_trame-0.19.0.dev0.dist-info/entry_points.txt,sha256=J2AmeSwiTYZ4ZqHHp9HO6v4MaYQTTBPbNh6WtoqOT58,42
32
+ nova_trame-0.19.0.dev0.dist-info/RECORD,,