Skip to content

Commit cbcd156

Browse files
Merge pull request #157 from jverswijver/update_forms
Update forms to enable presets
2 parents da3c32c + 39a0cc7 commit cbcd156

File tree

6 files changed

+96
-6
lines changed

6 files changed

+96
-6
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) convention.
44

5-
## [0.8.1] - TBD
5+
## [0.8.1] - 2023-03-20
66

77
### Added
88
- Api endpoint `/spec` which returns the spec for the current dynamic routes [#156](https://github.com/datajoint/pharus/pull/156)
9+
- Support for presets in Dynamic forms [#157](https://github.com/datajoint/pharus/pull/157)
910

1011
### Bugfix
11-
1212
- Added print statement to let user know if their component override has gone through [#156](https://github.com/datajoint/pharus/pull/156)
1313

1414
## [0.8.0] - 2023-02-06
@@ -275,6 +275,7 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and
275275
- Support for DataJoint attribute types: `varchar`, `int`, `float`, `datetime`, `date`, `time`, `decimal`, `uuid`.
276276
- Check dependency utility to determine child table references.
277277

278+
[0.8.1]: https://github.com/datajoint/pharus/compare/0.8.0...0.8.1
278279
[0.8.0]: https://github.com/datajoint/pharus/compare/0.7.3...0.8.0
279280
[0.7.3]: https://github.com/datajoint/pharus/compare/0.7.2...0.7.3
280281
[0.7.2]: https://github.com/datajoint/pharus/compare/0.7.1...0.7.2

pharus/component_interface.py

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,11 +207,14 @@ def dj_query_route(self):
207207
class InsertComponent(Component):
208208
rest_verb = ["POST", "GET"]
209209
fields_route_format = "{route}/fields"
210+
presets_route_format = "{route}/presets"
210211

211212
def __init__(self, *args, **kwargs):
212213
super().__init__(*args, **kwargs)
213-
component_config = kwargs.get("component_config", args[1] if args else None)
214-
self.fields_map = component_config.get("map")
214+
self.component_config = kwargs.get(
215+
"component_config", args[1] if args else None
216+
)
217+
self.fields_map = self.component_config.get("map")
215218
self.tables = [
216219
getattr(
217220
dj.VirtualModule(
@@ -222,7 +225,8 @@ def __init__(self, *args, **kwargs):
222225
t,
223226
)
224227
for s, t in (
225-
_.format(**request.args).split(".") for _ in component_config["tables"]
228+
_.format(**request.args).split(".")
229+
for _ in self.component_config["tables"]
226230
)
227231
]
228232
self.parents = sorted(
@@ -243,6 +247,24 @@ def __init__(self, *args, **kwargs):
243247
}
244248
self.input_lookup = {v: k for k, v in self.destination_lookup.items()}
245249

250+
if "presets" in self.component_config:
251+
lcls = locals()
252+
exec(self.component_config["presets"], globals(), lcls)
253+
self.presets = lcls["presets"]
254+
255+
self.preset_vm_list = [
256+
dj.VirtualModule(
257+
s,
258+
s.replace("__", "-"),
259+
connection=self.connection,
260+
)
261+
for s in inspect.getfullargspec(self.presets).args
262+
]
263+
264+
@property
265+
def presets_dict(self):
266+
return self.presets(*self.preset_vm_list)
267+
246268
def dj_query_route(self):
247269
with self.connection.transaction:
248270
for t in self.tables:
@@ -315,6 +337,45 @@ def fields_route(self):
315337
+ list(source_fields.values())
316338
)
317339

340+
def presets_route(self):
341+
# Table content for presets should follow the following format:
342+
#
343+
# preset_names: string
344+
# ---
345+
# presets: blob or json
346+
#
347+
# Example result from query:
348+
# [['preset_name', {"b_id": 1, "b_number": 2345}],
349+
# ['preset2_name', {"b_id": 13, "b_number": 225}]]
350+
#
351+
# If you have a name mapping it will be applied to each preset
352+
# Route will 404 if no preset query is defined and 500 if there is an Exception
353+
354+
# Helper function to filter out fields not in the insert,
355+
# as well as apply the fields_map
356+
def filterPreset(preset: dict):
357+
return {
358+
(self.input_lookup[k] if k in self.input_lookup else k): v
359+
for k, v in preset.items()
360+
}
361+
362+
if "presets" not in self.component_config:
363+
return (
364+
"No Preset query found",
365+
404,
366+
{"Content-Type": "text/plain"},
367+
)
368+
369+
filtered_preset_dictionary = {
370+
k: filterPreset(v) for k, v in self.presets_dict.items()
371+
}
372+
373+
return (
374+
NumpyEncoder.dumps(filtered_preset_dictionary),
375+
200,
376+
{"Content-Type": "application/json"},
377+
)
378+
318379

319380
class TableComponent(FetchComponent):
320381
attributes_route_format = "{route}/attributes"

pharus/dynamic_api_gen.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ def {method_name}() -> dict:
170170
fields_route = type_map[
171171
comp["type"]
172172
].fields_route_format.format(route=comp["route"])
173+
presets_route = type_map[
174+
comp["type"]
175+
].presets_route_format.format(route=comp["route"])
173176
f.write(
174177
(active_route_template).format(
175178
route=fields_route,
@@ -183,6 +186,19 @@ def {method_name}() -> dict:
183186
method_name_type="fields_route",
184187
)
185188
)
189+
f.write(
190+
(active_route_template).format(
191+
route=presets_route,
192+
rest_verb=[InsertComponent.rest_verb[1]],
193+
method_name=presets_route.replace("/", ""),
194+
component_type=comp["type"],
195+
component_name=comp_name,
196+
component=json.dumps(comp),
197+
static_config=static_config,
198+
payload="payload=None",
199+
method_name_type="presets_route",
200+
)
201+
)
186202
elif issubclass(type_map[comp["type"]], TableComponent):
187203
attributes_route = type_map[
188204
comp["type"]

pharus/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""Package metadata."""
2-
__version__ = "0.8.0"
2+
__version__ = "0.8.1"

tests/init/test_dynamic_api_spec.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ SciViz: # top level tab
9494
destination: c_id
9595
- type: attribute #omitted input means display is session_note
9696
destination: c_name
97+
presets: >
98+
def presets():
99+
return {'preset 1': {'b_id': 14}}
97100
insert3:
98101
route: /insert3
99102
x: 1

tests/test_form.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,3 +374,12 @@ def test_form_datetime_FPK(token, client, connection, schemas_simple):
374374
{"datatype": "int", "default": "1", "name": "u_int", "type": "attribute"},
375375
]
376376
}
377+
378+
379+
def test_form_response_presets(token, client, connection, schemas_simple):
380+
REST_response = client.get(
381+
"/insert2/presets",
382+
headers=dict(Authorization=f"Bearer {token}"),
383+
)
384+
assert REST_response.status_code == 200, f"Error: {REST_response.data}"
385+
assert REST_response.get_json() == {"preset 1": {"B Id": 14}}

0 commit comments

Comments
 (0)