Skip to content

Commit db3daa3

Browse files
committed
Use DataBlockResponse model to validate block db r/w
1 parent 705ecf8 commit db3daa3

File tree

7 files changed

+109
-19
lines changed

7 files changed

+109
-19
lines changed

pydatalab/schemas/cell.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,20 @@
214214
"type": "string"
215215
}
216216
},
217+
"errors": {
218+
"title": "Errors",
219+
"type": "array",
220+
"items": {
221+
"type": "string"
222+
}
223+
},
224+
"warnings": {
225+
"title": "Warnings",
226+
"type": "array",
227+
"items": {
228+
"type": "string"
229+
}
230+
},
217231
"b64_encoded_image": {
218232
"title": "B64 Encoded Image",
219233
"type": "object",
@@ -223,7 +237,11 @@
223237
},
224238
"bokeh_plot_data": {
225239
"title": "Bokeh Plot Data",
226-
"type": "string"
240+
"type": "object"
241+
},
242+
"processed_data": {
243+
"title": "Processed Data",
244+
"type": "object"
227245
}
228246
},
229247
"required": [

pydatalab/schemas/equipment.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,20 @@
178178
"type": "string"
179179
}
180180
},
181+
"errors": {
182+
"title": "Errors",
183+
"type": "array",
184+
"items": {
185+
"type": "string"
186+
}
187+
},
188+
"warnings": {
189+
"title": "Warnings",
190+
"type": "array",
191+
"items": {
192+
"type": "string"
193+
}
194+
},
181195
"b64_encoded_image": {
182196
"title": "B64 Encoded Image",
183197
"type": "object",
@@ -187,7 +201,11 @@
187201
},
188202
"bokeh_plot_data": {
189203
"title": "Bokeh Plot Data",
190-
"type": "string"
204+
"type": "object"
205+
},
206+
"processed_data": {
207+
"title": "Processed Data",
208+
"type": "object"
191209
}
192210
},
193211
"required": [

pydatalab/schemas/sample.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,20 @@
267267
"type": "string"
268268
}
269269
},
270+
"errors": {
271+
"title": "Errors",
272+
"type": "array",
273+
"items": {
274+
"type": "string"
275+
}
276+
},
277+
"warnings": {
278+
"title": "Warnings",
279+
"type": "array",
280+
"items": {
281+
"type": "string"
282+
}
283+
},
270284
"b64_encoded_image": {
271285
"title": "B64 Encoded Image",
272286
"type": "object",
@@ -276,7 +290,11 @@
276290
},
277291
"bokeh_plot_data": {
278292
"title": "Bokeh Plot Data",
279-
"type": "string"
293+
"type": "object"
294+
},
295+
"processed_data": {
296+
"title": "Processed Data",
297+
"type": "object"
280298
}
281299
},
282300
"required": [

pydatalab/schemas/startingmaterial.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,20 @@
320320
"type": "string"
321321
}
322322
},
323+
"errors": {
324+
"title": "Errors",
325+
"type": "array",
326+
"items": {
327+
"type": "string"
328+
}
329+
},
330+
"warnings": {
331+
"title": "Warnings",
332+
"type": "array",
333+
"items": {
334+
"type": "string"
335+
}
336+
},
323337
"b64_encoded_image": {
324338
"title": "B64 Encoded Image",
325339
"type": "object",
@@ -329,7 +343,11 @@
329343
},
330344
"bokeh_plot_data": {
331345
"title": "Bokeh Plot Data",
332-
"type": "string"
346+
"type": "object"
347+
},
348+
"processed_data": {
349+
"title": "Processed Data",
350+
"type": "object"
333351
}
334352
},
335353
"required": [

pydatalab/src/pydatalab/blocks/base.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import functools
2+
import json
23
import random
34
import warnings
45
from collections.abc import Callable, Sequence
56
from typing import Any
67

78
from pydatalab import __version__
89
from pydatalab.logger import LOGGER
10+
from pydatalab.models.blocks import DataBlockResponse
911

1012
__all__ = ("generate_random_id", "DataBlock", "generate_js_callback_single_float_parameter")
1113

@@ -100,6 +102,8 @@ def generate_random_id():
100102
class DataBlock:
101103
"""Base class for a data block."""
102104

105+
block_db_model = DataBlockResponse
106+
103107
name: str = "base"
104108
"""The human-readable block name specifying which technique
105109
or file format it pertains to.
@@ -191,15 +195,12 @@ def __init__(
191195
collection_id,
192196
)
193197

194-
def to_db(self):
198+
def to_db(self) -> dict:
195199
"""returns a dictionary with the data for this
196200
block, ready to be input into mongodb"""
197-
from bson import ObjectId
198201

199202
LOGGER.debug("Casting block %s to database object.", self.__class__.__name__)
200-
dct_for_db = {k: v for k, v in self.data.items() if k != "bokeh_plot_data"}
201-
dct_for_db["file_id"] = ObjectId(dct_for_db["file_id"]) if "file_id" in dct_for_db else None
202-
return dct_for_db
203+
return self.block_db_model(**self.data).dict(exclude_unset=True)
203204

204205
@classmethod
205206
def from_db(cls, block: dict):
@@ -251,7 +252,9 @@ def to_web(self) -> dict[str, Any]:
251252
else:
252253
self.data.pop("warnings", None)
253254

254-
return self.data
255+
model = self.block_db_model(**self.data)
256+
serialized = json.loads(model.json(exclude_unset=True, exclude_none=True))
257+
return serialized
255258

256259
def process_events(self, events: list[dict] | dict):
257260
"""Handle any supported events passed to the block."""
@@ -339,6 +342,10 @@ def update_from_web(self, data: dict):
339342
"Updating block %s from web request",
340343
self.__class__.__name__,
341344
)
342-
self.data.update(data)
345+
self.data.update(
346+
self.block_db_model(**data).dict(
347+
exclude={"bokeh_plot_data", "b64_processed_image"}, exclude_unset=True
348+
)
349+
)
343350

344351
return self

pydatalab/src/pydatalab/models/blocks.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,22 @@ class DataBlockResponse(BaseModel):
3232
file_ids: list[PyObjectId] | None = None
3333
"""A list of file IDs associated with the block, if any."""
3434

35-
b64_encoded_image: dict[PyObjectId, str] | None = None
35+
errors: list[str] | None = None
36+
"""Any errors that occurred during block processing."""
37+
38+
warnings: list[str] | None = None
39+
"""Any warnings that occurred during block processing."""
40+
41+
b64_encoded_image: dict[PyObjectId, str] | None
3642
"""Any base64-encoded image data associated with the block, keyed by file_id, if any."""
3743

38-
bokeh_plot_data: str | None = None
44+
bokeh_plot_data: dict | None
3945
"""A JSON-encoded string containing the Bokeh plot data, if any."""
4046

47+
processed_data: dict | None = None
48+
"""Any processed data associated with the block, small enough to store."""
49+
4150
class Config:
4251
allow_population_by_field_name = True
4352
json_encoders = JSON_ENCODERS
44-
extra = "allow"
53+
extra = "ignore"

pydatalab/tests/server/test_blocks.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def test_invalid_block_type(admin_client, default_sample_dict):
7575
},
7676
)
7777

78-
assert response.status_code == 400
78+
assert response.status_code == 501
7979
assert "Invalid block type" in response.json["message"]
8080

8181

@@ -259,7 +259,9 @@ def test_xrd_block_lifecycle(admin_client, default_sample_dict):
259259
assert "bokeh_plot_data" in web_block
260260
assert "processed_data" in web_block
261261
assert "peak_data" in web_block["processed_data"]
262-
assert web_block.get("errors", []) == []
262+
assert "file_id" in web_block
263+
assert web_block["file_id"] == file_id
264+
assert web_block.get("errors") is None
263265

264266
block = XRDBlock.from_web(web_block)
265267
db = block.to_db()
@@ -306,7 +308,7 @@ def test_comment_block_manipulation(admin_client, default_sample_dict, database)
306308
assert response.json["new_block_data"]["blocktype"] == block_type
307309
assert response.json["new_block_data"]["freeform_comment"] == "This is a test comment block."
308310
assert response.json["new_block_data"]["title"] == "Test Comment Block"
309-
assert "errors" not in response.json["new_block_data"]
311+
assert response.json["new_block_data"].get("errors") is None
310312

311313
# Check that this result was actually stored
312314
response = admin_client.get(f"/get-item-data/{sample_id}")
@@ -319,7 +321,7 @@ def test_comment_block_manipulation(admin_client, default_sample_dict, database)
319321
assert "errors" not in response.json["item_data"]["blocks_obj"][block_id]
320322

321323
# Try to add some bad data
322-
block_data["bokeh_plot_data"] = '{"bokeh": "json"}'
324+
block_data["bokeh_plot_data"] = {"bokeh": "json"}
323325
block_data["random_new_key"] = "test new key"
324326
response = admin_client.post("/update-block/", json={"block_data": block_data})
325327
assert response.status_code == 200
@@ -419,7 +421,7 @@ def test_create_sample_with_example_files(
419421
if block_type == "xrd":
420422
assert response.json["new_block_data"]["processed_data"]["peak_data"] is not None
421423

422-
response = admin_client.get(f"/get-item-data/{sample_id}?load_blocks=1")
424+
response = admin_client.get(f"/get-item-data/{sample_id}")
423425
assert response.status_code == 200
424426
assert response.json["status"] == "success"
425427

0 commit comments

Comments
 (0)