prisma-api 0.4.3__tar.gz → 0.4.5__tar.gz
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.
- {prisma_api-0.4.3 → prisma_api-0.4.5}/PKG-INFO +30 -4
- {prisma_api-0.4.3 → prisma_api-0.4.5}/README.md +29 -3
- prisma_api-0.4.5/prisma_api/__init__.py +31 -0
- {prisma_api-0.4.3 → prisma_api-0.4.5}/prisma_api/config.py +3 -4
- {prisma_api-0.4.3 → prisma_api-0.4.5}/prisma_api/prisma_api.py +20 -21
- {prisma_api-0.4.3 → prisma_api-0.4.5}/prisma_api/prisma_api_v2.py +749 -65
- {prisma_api-0.4.3 → prisma_api-0.4.5}/prisma_api.egg-info/PKG-INFO +30 -4
- {prisma_api-0.4.3 → prisma_api-0.4.5}/pyproject.toml +12 -1
- {prisma_api-0.4.3 → prisma_api-0.4.5}/tests/test_prisma_api.py +5 -5
- {prisma_api-0.4.3 → prisma_api-0.4.5}/tests/test_prisma_api_v2.py +601 -48
- prisma_api-0.4.3/prisma_api/__init__.py +0 -11
- {prisma_api-0.4.3 → prisma_api-0.4.5}/LICENSE +0 -0
- {prisma_api-0.4.3 → prisma_api-0.4.5}/prisma_api.egg-info/SOURCES.txt +0 -0
- {prisma_api-0.4.3 → prisma_api-0.4.5}/prisma_api.egg-info/dependency_links.txt +0 -0
- {prisma_api-0.4.3 → prisma_api-0.4.5}/prisma_api.egg-info/requires.txt +0 -0
- {prisma_api-0.4.3 → prisma_api-0.4.5}/prisma_api.egg-info/top_level.txt +0 -0
- {prisma_api-0.4.3 → prisma_api-0.4.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: prisma_api
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.5
|
|
4
4
|
Summary: A package for interacting with PrISMa Platform APIs. PrISMa is a holistic system for numerical synthesis and assessment of Metal-Organic Frameworks and their applications, for Direct Air Capture of CO2. For more information, see: https://prisma.hw.ac.uk.
|
|
5
5
|
Author-email: RCCS-CaptureTeam <team@prisma-platform.org>
|
|
6
6
|
License: GNUv3
|
|
@@ -165,7 +165,7 @@ The main client exposes these v1 methods:
|
|
|
165
165
|
- `get_materials_data(payload={}, separate_experimental=True)`
|
|
166
166
|
- `update_adsorption_singlepoint(df)`
|
|
167
167
|
- `update_heat_capacity_all_tidy(df)`
|
|
168
|
-
- `update_isotherm_h2(df)`
|
|
168
|
+
- `update_isotherm_h2(df)` — deprecated; still works (the server stores its rows in `adsorption_isotherm`)
|
|
169
169
|
- `update_mofchecker(df)`
|
|
170
170
|
- `update_zeopp_metrics(df)`
|
|
171
171
|
|
|
@@ -181,6 +181,7 @@ Module-level helpers exported from the package:
|
|
|
181
181
|
- `prisma_api.update_dev_mode`
|
|
182
182
|
- `prisma_api.update_dev_host_port`
|
|
183
183
|
- `prisma_api.locate_config`
|
|
184
|
+
- `prisma_api.PrismaUpsertError`, `prisma_api.PrismaRowErrorWarning`, `prisma_api.PrismaUnknownFieldsWarning`, `prisma_api.PrismaNewStructureWarning`
|
|
184
185
|
|
|
185
186
|
## v2 wrapper list
|
|
186
187
|
|
|
@@ -266,6 +267,28 @@ All methods below are available on `api.v2`.
|
|
|
266
267
|
- `get_carbon_zeopp_experimental(mof=None, limit=500, offset=0)`
|
|
267
268
|
- `get_carbon_zeopp_experimental_item(item_id)`
|
|
268
269
|
|
|
270
|
+
### AutoPrism tables
|
|
271
|
+
|
|
272
|
+
- `get_computation_runs(workflow_id=None, step=None, status=None, limit=500, offset=0)` / `get_computation_run(run_id)`
|
|
273
|
+
- `upsert_computation_runs(payload, timeout=None, raise_on_error=False)`
|
|
274
|
+
- `get_adsorption_singlepoint(...)` / `get_adsorption_singlepoint_item(row_id)` / `upsert_adsorption_singlepoint(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)`
|
|
275
|
+
- `get_heat_capacity(...)` / `get_heat_capacity_item(row_id)` / `upsert_heat_capacity(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)`
|
|
276
|
+
- `get_isotherm_h2(...)` / `get_isotherm_h2_item(row_id)` / `upsert_isotherm_h2(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)` — deprecated; use the `adsorption_isotherm` methods with `component='H2'`
|
|
277
|
+
- `get_adsorption_isotherm(structure=None, isotherm_id=None, component=None, temperature_K=None, pressure_bar=None, limit=500, offset=0, md5=None, match=None)` / `get_adsorption_isotherm_item(row_id)` / `upsert_adsorption_isotherm(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)` — every gas, H2 included; needs prisma_cloud **>= 0.6.16** (older servers return 404)
|
|
278
|
+
- `get_mofchecker(...)` / `get_mofchecker_item(row_id)` / `upsert_mofchecker(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)`
|
|
279
|
+
- `get_zeopp_metrics(...)` / `get_zeopp_metrics_item(row_id)` / `upsert_zeopp_metrics(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)`
|
|
280
|
+
- `upsert_autoprism_cifs(cifs, tags=None, create_materials=True, batch_size=50, raise_on_error=False, timeout=300)` — AutoPrism's own CIFs, stored as `{structure}__autoprism_{md5[:12]}.cif`
|
|
281
|
+
- `get_autoprism_collection(...)` / `upsert_autoprism_collection(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True, cif_tags=None)`
|
|
282
|
+
|
|
283
|
+
The getters take `match="exact"` for an exact (case-insensitive) name match
|
|
284
|
+
instead of the default substring match.
|
|
285
|
+
|
|
286
|
+
Rejected rows (HTTP 207, or 400 when nothing is stored) raise
|
|
287
|
+
`PrismaUpsertError` with `raise_on_error=True`, else emit
|
|
288
|
+
`PrismaRowErrorWarning`. Ignored payload fields and newly created MOF records
|
|
289
|
+
emit `PrismaUnknownFieldsWarning` / `PrismaNewStructureWarning`. See
|
|
290
|
+
[API_REFERENCE_V2.md](API_REFERENCE_V2.md#autoprism-tables).
|
|
291
|
+
|
|
269
292
|
### TEA, LCA, cases, and scenarios
|
|
270
293
|
|
|
271
294
|
- `get_output_kpis(scenario_id=None, mof=None, good_structure=None, limit=500, offset=0)`
|
|
@@ -373,7 +396,8 @@ pytest tests/ -v --cov=prisma_api --cov-report=term-missing
|
|
|
373
396
|
|
|
374
397
|
## Additional documentation
|
|
375
398
|
|
|
376
|
-
- [
|
|
399
|
+
- [API_REFERENCE_V1.md](API_REFERENCE_V1.md)
|
|
400
|
+
- [API_REFERENCE_V2.md](API_REFERENCE_V2.md)
|
|
377
401
|
- [DEVELOPMENT_SUMMARY.md](DEVELOPMENT_SUMMARY.md)
|
|
378
402
|
- [User Guide.md](User%20Guide.md)
|
|
379
403
|
|
|
@@ -381,4 +405,6 @@ pytest tests/ -v --cov=prisma_api --cov-report=term-missing
|
|
|
381
405
|
|
|
382
406
|
- Production v2 base URL: `https://prisma-platform.org/api/v2`
|
|
383
407
|
- Authentication header: `X-API-Key`
|
|
384
|
-
- Local development routing is
|
|
408
|
+
- Local development routing is selected with `prisma_api.init(local_dev=True)`; the default targets production
|
|
409
|
+
- 0.4.4: the `adsorption-isotherm` endpoints and the 400/notice handling need prisma_cloud >= 0.6.16
|
|
410
|
+
- 0.4.5: H2 isotherms go through `upsert_adsorption_isotherm` (prisma_cloud >= 0.6.16); the H2-only methods and the `isotherm_H2s` collection key are deprecated. New `upsert_autoprism_cifs` uses the existing bundle upsert endpoint. AutoPrism getters take `match="exact"`.
|
|
@@ -145,7 +145,7 @@ The main client exposes these v1 methods:
|
|
|
145
145
|
- `get_materials_data(payload={}, separate_experimental=True)`
|
|
146
146
|
- `update_adsorption_singlepoint(df)`
|
|
147
147
|
- `update_heat_capacity_all_tidy(df)`
|
|
148
|
-
- `update_isotherm_h2(df)`
|
|
148
|
+
- `update_isotherm_h2(df)` — deprecated; still works (the server stores its rows in `adsorption_isotherm`)
|
|
149
149
|
- `update_mofchecker(df)`
|
|
150
150
|
- `update_zeopp_metrics(df)`
|
|
151
151
|
|
|
@@ -161,6 +161,7 @@ Module-level helpers exported from the package:
|
|
|
161
161
|
- `prisma_api.update_dev_mode`
|
|
162
162
|
- `prisma_api.update_dev_host_port`
|
|
163
163
|
- `prisma_api.locate_config`
|
|
164
|
+
- `prisma_api.PrismaUpsertError`, `prisma_api.PrismaRowErrorWarning`, `prisma_api.PrismaUnknownFieldsWarning`, `prisma_api.PrismaNewStructureWarning`
|
|
164
165
|
|
|
165
166
|
## v2 wrapper list
|
|
166
167
|
|
|
@@ -246,6 +247,28 @@ All methods below are available on `api.v2`.
|
|
|
246
247
|
- `get_carbon_zeopp_experimental(mof=None, limit=500, offset=0)`
|
|
247
248
|
- `get_carbon_zeopp_experimental_item(item_id)`
|
|
248
249
|
|
|
250
|
+
### AutoPrism tables
|
|
251
|
+
|
|
252
|
+
- `get_computation_runs(workflow_id=None, step=None, status=None, limit=500, offset=0)` / `get_computation_run(run_id)`
|
|
253
|
+
- `upsert_computation_runs(payload, timeout=None, raise_on_error=False)`
|
|
254
|
+
- `get_adsorption_singlepoint(...)` / `get_adsorption_singlepoint_item(row_id)` / `upsert_adsorption_singlepoint(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)`
|
|
255
|
+
- `get_heat_capacity(...)` / `get_heat_capacity_item(row_id)` / `upsert_heat_capacity(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)`
|
|
256
|
+
- `get_isotherm_h2(...)` / `get_isotherm_h2_item(row_id)` / `upsert_isotherm_h2(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)` — deprecated; use the `adsorption_isotherm` methods with `component='H2'`
|
|
257
|
+
- `get_adsorption_isotherm(structure=None, isotherm_id=None, component=None, temperature_K=None, pressure_bar=None, limit=500, offset=0, md5=None, match=None)` / `get_adsorption_isotherm_item(row_id)` / `upsert_adsorption_isotherm(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)` — every gas, H2 included; needs prisma_cloud **>= 0.6.16** (older servers return 404)
|
|
258
|
+
- `get_mofchecker(...)` / `get_mofchecker_item(row_id)` / `upsert_mofchecker(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)`
|
|
259
|
+
- `get_zeopp_metrics(...)` / `get_zeopp_metrics_item(row_id)` / `upsert_zeopp_metrics(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True)`
|
|
260
|
+
- `upsert_autoprism_cifs(cifs, tags=None, create_materials=True, batch_size=50, raise_on_error=False, timeout=300)` — AutoPrism's own CIFs, stored as `{structure}__autoprism_{md5[:12]}.cif`
|
|
261
|
+
- `get_autoprism_collection(...)` / `upsert_autoprism_collection(payload, meta_provenance=None, repo_dir=None, timeout=None, raise_on_error=False, check_ids=True, cif_tags=None)`
|
|
262
|
+
|
|
263
|
+
The getters take `match="exact"` for an exact (case-insensitive) name match
|
|
264
|
+
instead of the default substring match.
|
|
265
|
+
|
|
266
|
+
Rejected rows (HTTP 207, or 400 when nothing is stored) raise
|
|
267
|
+
`PrismaUpsertError` with `raise_on_error=True`, else emit
|
|
268
|
+
`PrismaRowErrorWarning`. Ignored payload fields and newly created MOF records
|
|
269
|
+
emit `PrismaUnknownFieldsWarning` / `PrismaNewStructureWarning`. See
|
|
270
|
+
[API_REFERENCE_V2.md](API_REFERENCE_V2.md#autoprism-tables).
|
|
271
|
+
|
|
249
272
|
### TEA, LCA, cases, and scenarios
|
|
250
273
|
|
|
251
274
|
- `get_output_kpis(scenario_id=None, mof=None, good_structure=None, limit=500, offset=0)`
|
|
@@ -353,7 +376,8 @@ pytest tests/ -v --cov=prisma_api --cov-report=term-missing
|
|
|
353
376
|
|
|
354
377
|
## Additional documentation
|
|
355
378
|
|
|
356
|
-
- [
|
|
379
|
+
- [API_REFERENCE_V1.md](API_REFERENCE_V1.md)
|
|
380
|
+
- [API_REFERENCE_V2.md](API_REFERENCE_V2.md)
|
|
357
381
|
- [DEVELOPMENT_SUMMARY.md](DEVELOPMENT_SUMMARY.md)
|
|
358
382
|
- [User Guide.md](User%20Guide.md)
|
|
359
383
|
|
|
@@ -361,4 +385,6 @@ pytest tests/ -v --cov=prisma_api --cov-report=term-missing
|
|
|
361
385
|
|
|
362
386
|
- Production v2 base URL: `https://prisma-platform.org/api/v2`
|
|
363
387
|
- Authentication header: `X-API-Key`
|
|
364
|
-
- Local development routing is
|
|
388
|
+
- Local development routing is selected with `prisma_api.init(local_dev=True)`; the default targets production
|
|
389
|
+
- 0.4.4: the `adsorption-isotherm` endpoints and the 400/notice handling need prisma_cloud >= 0.6.16
|
|
390
|
+
- 0.4.5: H2 isotherms go through `upsert_adsorption_isotherm` (prisma_cloud >= 0.6.16); the H2-only methods and the `isotherm_H2s` collection key are deprecated. New `upsert_autoprism_cifs` uses the existing bundle upsert endpoint. AutoPrism getters take `match="exact"`.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from importlib.metadata import PackageNotFoundError
|
|
2
|
+
from importlib.metadata import version as _version
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
__version__ = _version("prisma_api")
|
|
6
|
+
except PackageNotFoundError: # running from a source tree without installing
|
|
7
|
+
__version__ = "0+unknown"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
from prisma_api.config import locate_config, update_dev_host_port, update_dev_mode
|
|
11
|
+
from prisma_api.prisma_api import prisma_api as init # main client
|
|
12
|
+
from prisma_api.prisma_api_v2 import ( # v2 client (also api.v2) and upsert failure signals
|
|
13
|
+
PrismaAPIv2,
|
|
14
|
+
PrismaNewStructureWarning,
|
|
15
|
+
PrismaRowErrorWarning,
|
|
16
|
+
PrismaUnknownFieldsWarning,
|
|
17
|
+
PrismaUpsertError,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"PrismaAPIv2",
|
|
22
|
+
"PrismaNewStructureWarning",
|
|
23
|
+
"PrismaRowErrorWarning",
|
|
24
|
+
"PrismaUnknownFieldsWarning",
|
|
25
|
+
"PrismaUpsertError",
|
|
26
|
+
"__version__",
|
|
27
|
+
"init",
|
|
28
|
+
"locate_config",
|
|
29
|
+
"update_dev_host_port",
|
|
30
|
+
"update_dev_mode",
|
|
31
|
+
]
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
|
|
4
5
|
import pandas as pd
|
|
5
|
-
import yaml
|
|
6
6
|
import platformdirs
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import yaml
|
|
9
8
|
|
|
10
9
|
### Helper functions for config management
|
|
11
10
|
|
|
@@ -42,7 +41,7 @@ def load_config():
|
|
|
42
41
|
return cfg
|
|
43
42
|
|
|
44
43
|
# Internal function to create config.yaml
|
|
45
|
-
def create_config_file(api_key: str = None):
|
|
44
|
+
def create_config_file(api_key: str | None = None):
|
|
46
45
|
"""
|
|
47
46
|
Create config.yaml. If api_key is None, prompt via CLI.
|
|
48
47
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import math
|
|
1
2
|
import os
|
|
2
3
|
import warnings
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
6
|
import pandas as pd
|
|
7
7
|
import requests
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
import
|
|
11
|
-
import
|
|
9
|
+
from .config import get_or_create_config
|
|
10
|
+
from .config import update_dev_mode as _update_dev_mode
|
|
11
|
+
from .prisma_api_v2 import PrismaAPIv2
|
|
12
|
+
|
|
12
13
|
|
|
13
14
|
def _safe_nan_check(x):
|
|
14
15
|
if x is None:
|
|
@@ -23,7 +24,7 @@ def _safe_nan_check(x):
|
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
# prisma_api main class
|
|
26
|
-
class prisma_api
|
|
27
|
+
class prisma_api:
|
|
27
28
|
|
|
28
29
|
def __init__(self, use_config_file=True, local_dev: bool = False,
|
|
29
30
|
upload_timeout: int = 120):
|
|
@@ -91,7 +92,7 @@ class prisma_api():
|
|
|
91
92
|
)
|
|
92
93
|
return _update_dev_mode(dev)
|
|
93
94
|
|
|
94
|
-
def get_mofs(self, payload=
|
|
95
|
+
def get_mofs(self, payload: dict | None = None):
|
|
95
96
|
|
|
96
97
|
api = self
|
|
97
98
|
|
|
@@ -105,7 +106,7 @@ class prisma_api():
|
|
|
105
106
|
"Content-Type": "application/json"
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
response = requests.post(url, json=payload, headers=headers, timeout=60)
|
|
109
|
+
response = requests.post(url, json=payload or {}, headers=headers, timeout=60)
|
|
109
110
|
data = response.json()['data']
|
|
110
111
|
|
|
111
112
|
data = pd.DataFrame.from_dict(data)
|
|
@@ -113,7 +114,7 @@ class prisma_api():
|
|
|
113
114
|
return data
|
|
114
115
|
|
|
115
116
|
|
|
116
|
-
def get_carbon_isotherms(self, payload=
|
|
117
|
+
def get_carbon_isotherms(self, payload: dict | None = None):
|
|
117
118
|
|
|
118
119
|
api = self
|
|
119
120
|
|
|
@@ -128,7 +129,7 @@ class prisma_api():
|
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
try:
|
|
131
|
-
response = requests.post(url, json=payload, headers=headers, timeout=60)
|
|
132
|
+
response = requests.post(url, json=payload or {}, headers=headers, timeout=60)
|
|
132
133
|
data = response.json()['data']
|
|
133
134
|
|
|
134
135
|
data = pd.DataFrame.from_dict(data)
|
|
@@ -147,11 +148,11 @@ class prisma_api():
|
|
|
147
148
|
|
|
148
149
|
return data
|
|
149
150
|
|
|
150
|
-
except Exception
|
|
151
|
+
except Exception: # noqa: BLE001 - v1 contract: print and return empty
|
|
151
152
|
print("Error retrieving carbon isotherms: check that the query parameter names are correct.")
|
|
152
153
|
return pd.DataFrame()
|
|
153
154
|
|
|
154
|
-
def get_carbon_data_nested(self, payload=
|
|
155
|
+
def get_carbon_data_nested(self, payload: dict | None = None, safe_names=False):
|
|
155
156
|
"""
|
|
156
157
|
Get carbon data with nested structure, returned as separate DataFrames.
|
|
157
158
|
|
|
@@ -180,7 +181,7 @@ class prisma_api():
|
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
try:
|
|
183
|
-
response = requests.post(url, json=payload, headers=headers, timeout=60)
|
|
184
|
+
response = requests.post(url, json=payload or {}, headers=headers, timeout=60)
|
|
184
185
|
data = response.json()
|
|
185
186
|
|
|
186
187
|
col_names_carbon = data.get('meta', {}).get('original_column_names', {})
|
|
@@ -234,11 +235,11 @@ class prisma_api():
|
|
|
234
235
|
}
|
|
235
236
|
}
|
|
236
237
|
|
|
237
|
-
except Exception as e:
|
|
238
|
+
except Exception as e: # noqa: BLE001 - v1 contract: print and return empty
|
|
238
239
|
print(f"Error retrieving carbon data nested: {e}")
|
|
239
240
|
return {}
|
|
240
241
|
|
|
241
|
-
def get_materials_data(self, payload=
|
|
242
|
+
def get_materials_data(self, payload: dict | None = None, separate_experimental=True):
|
|
242
243
|
"""
|
|
243
244
|
Args:
|
|
244
245
|
payload: Dictionary containing query parameters for filtering.
|
|
@@ -279,12 +280,12 @@ class prisma_api():
|
|
|
279
280
|
source_key = None
|
|
280
281
|
for name, endpoint in urls.items():
|
|
281
282
|
try:
|
|
282
|
-
response = requests.post(endpoint, json=payload, headers=headers, timeout=60)
|
|
283
|
+
response = requests.post(endpoint, json=payload or {}, headers=headers, timeout=60)
|
|
283
284
|
data_raw = response.json()
|
|
284
285
|
if data_raw.get('data'):
|
|
285
286
|
source_key = name
|
|
286
287
|
break
|
|
287
|
-
except Exception:
|
|
288
|
+
except Exception: # noqa: BLE001, S112 - endpoint fallback: try the next one
|
|
288
289
|
continue
|
|
289
290
|
|
|
290
291
|
if data_raw is None:
|
|
@@ -406,7 +407,7 @@ class prisma_api():
|
|
|
406
407
|
'meta': {'source': source_key},
|
|
407
408
|
}
|
|
408
409
|
|
|
409
|
-
except Exception as e:
|
|
410
|
+
except Exception as e: # noqa: BLE001 - v1 contract: print and return empty
|
|
410
411
|
print(f"Error retrieving materials data: {e}")
|
|
411
412
|
return {}
|
|
412
413
|
|
|
@@ -446,8 +447,6 @@ class prisma_api():
|
|
|
446
447
|
if df.empty:
|
|
447
448
|
return "[]"
|
|
448
449
|
|
|
449
|
-
import numpy as np
|
|
450
|
-
import json
|
|
451
450
|
|
|
452
451
|
# Use pandas to_json which handles NaN properly, then parse back
|
|
453
452
|
df_clean = df.copy()
|