prisma-api 0.3.0__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.
- prisma_api/__init__.py +6 -0
- prisma_api/config.py +182 -0
- prisma_api/prisma_api.py +539 -0
- prisma_api/prisma_api_v2.py +1533 -0
- prisma_api-0.3.0.dist-info/METADATA +245 -0
- prisma_api-0.3.0.dist-info/RECORD +9 -0
- prisma_api-0.3.0.dist-info/WHEEL +5 -0
- prisma_api-0.3.0.dist-info/licenses/LICENSE +674 -0
- prisma_api-0.3.0.dist-info/top_level.txt +1 -0
prisma_api/prisma_api.py
ADDED
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from .config import get_or_create_config, update_dev_mode as _update_dev_mode
|
|
3
|
+
from .prisma_api_v2 import PrismaAPIv2
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import json
|
|
10
|
+
import math
|
|
11
|
+
|
|
12
|
+
def _safe_nan_check(x):
|
|
13
|
+
if x is None:
|
|
14
|
+
return None
|
|
15
|
+
try:
|
|
16
|
+
if isinstance(x, (int, float)) and math.isnan(x):
|
|
17
|
+
return None
|
|
18
|
+
except (TypeError, ValueError):
|
|
19
|
+
pass
|
|
20
|
+
return x
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# prisma_api main class
|
|
25
|
+
class prisma_api():
|
|
26
|
+
|
|
27
|
+
def __init__(self, use_config_file=True):
|
|
28
|
+
|
|
29
|
+
# Initialise `prisma_api` object with api_key location
|
|
30
|
+
self.verbose = False
|
|
31
|
+
# Initialise `prisma_api` object with api_key location
|
|
32
|
+
if use_config_file:
|
|
33
|
+
cfg = get_or_create_config()
|
|
34
|
+
self.key = cfg['api_key']
|
|
35
|
+
self.dev = cfg.get('dev', False)
|
|
36
|
+
if self.dev:
|
|
37
|
+
self.dev_host_port = cfg['dev_host_port']
|
|
38
|
+
self.key = cfg['dev_api_key']
|
|
39
|
+
else:
|
|
40
|
+
self.key = os.getenv('PRISMA_API_KEY', '')
|
|
41
|
+
self.dev = os.getenv('PRISMA_API_DEV', 'False').lower() in ('true', '1', 't')
|
|
42
|
+
if self.dev:
|
|
43
|
+
self.dev_host_port = os.getenv('PRISMA_API_DEV_HOST_PORT', '')
|
|
44
|
+
|
|
45
|
+
self.v2 = PrismaAPIv2(
|
|
46
|
+
key=self.key,
|
|
47
|
+
dev=self.dev,
|
|
48
|
+
dev_host_port=getattr(self, 'dev_host_port', ''),
|
|
49
|
+
return_format=getattr(self, '_return_format', 'json'),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def set_return_format(self, fmt: str) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Set the output format for all v2 list endpoints.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
fmt: ``'dataframe'`` (default) — return ``pd.DataFrame``.
|
|
58
|
+
``'json'`` — return a plain ``list[dict]``.
|
|
59
|
+
"""
|
|
60
|
+
self._return_format = fmt
|
|
61
|
+
self.v2.set_return_format(fmt)
|
|
62
|
+
|
|
63
|
+
def update_dev_mode(self, dev: bool):
|
|
64
|
+
"""Update the dev flag in config.yaml.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
dev: Boolean flag to enable/disable dev mode.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
dict: Updated config.
|
|
71
|
+
"""
|
|
72
|
+
return _update_dev_mode(dev)
|
|
73
|
+
|
|
74
|
+
def get_mofs(self, payload={}):
|
|
75
|
+
|
|
76
|
+
api = self
|
|
77
|
+
|
|
78
|
+
if self.dev:
|
|
79
|
+
url = f"http://localhost:{self.dev_host_port}/api/get_mofs/"
|
|
80
|
+
else:
|
|
81
|
+
url = "https://www.dun-eideann-labs.co.uk/prisma_cloud/api/get_mofs/"
|
|
82
|
+
|
|
83
|
+
headers = {
|
|
84
|
+
"X-API-Key": api.key,
|
|
85
|
+
"Content-Type": "application/json"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
response = requests.post(url, json=payload, headers=headers, timeout=60)
|
|
89
|
+
data = response.json()['data']
|
|
90
|
+
|
|
91
|
+
data = pd.DataFrame.from_dict(data)
|
|
92
|
+
|
|
93
|
+
return data
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_carbon_isotherms(self, payload={}):
|
|
97
|
+
|
|
98
|
+
api = self
|
|
99
|
+
|
|
100
|
+
if self.dev:
|
|
101
|
+
url = f"http://localhost:{self.dev_host_port}/api/get_carbon_isotherms/"
|
|
102
|
+
else:
|
|
103
|
+
url = "https://www.prisma-platform.org/api/get_carbon_isotherms/"
|
|
104
|
+
|
|
105
|
+
headers = {
|
|
106
|
+
"X-API-Key": api.key,
|
|
107
|
+
"Content-Type": "application/json"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
response = requests.post(url, json=payload, headers=headers, timeout=60)
|
|
112
|
+
data = response.json()['data']
|
|
113
|
+
|
|
114
|
+
data = pd.DataFrame.from_dict(data)
|
|
115
|
+
|
|
116
|
+
# Flatten nested mof fields
|
|
117
|
+
if 'mof' in data.columns and not data.empty:
|
|
118
|
+
mof_df = pd.json_normalize(data['mof'])
|
|
119
|
+
mof_df.columns = ['mof_' + col for col in mof_df.columns]
|
|
120
|
+
data = pd.concat([data.drop('mof', axis=1), mof_df], axis=1)
|
|
121
|
+
|
|
122
|
+
# Flatten nested molecule fields
|
|
123
|
+
if 'molecule' in data.columns and not data.empty:
|
|
124
|
+
molecule_df = pd.json_normalize(data['molecule'])
|
|
125
|
+
molecule_df.columns = ['molecule_' + col for col in molecule_df.columns]
|
|
126
|
+
data = pd.concat([data.drop('molecule', axis=1), molecule_df], axis=1)
|
|
127
|
+
|
|
128
|
+
return data
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
print("Error retrieving carbon isotherms: check that the query parameter names are correct.")
|
|
132
|
+
return pd.DataFrame()
|
|
133
|
+
|
|
134
|
+
def get_carbon_data_nested(self, payload={}, safe_names=False):
|
|
135
|
+
"""
|
|
136
|
+
Get carbon data with nested structure, returned as separate DataFrames.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
payload: Dictionary containing query parameters for filtering
|
|
140
|
+
safe_names: If True (default), keep API-safe column names (e.g. 'Pressure_bar').
|
|
141
|
+
If False, rename columns to original names (e.g. 'Pressure [bar]').
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
dict: {
|
|
145
|
+
'Simulated': {'isotherm': pd.DataFrame, 'geometry': pd.DataFrame},
|
|
146
|
+
'Experimental': {'isotherm': pd.DataFrame, 'geometry': pd.DataFrame},
|
|
147
|
+
'meta': dict
|
|
148
|
+
}
|
|
149
|
+
"""
|
|
150
|
+
api = self
|
|
151
|
+
|
|
152
|
+
if self.dev:
|
|
153
|
+
url = f"http://localhost:{self.dev_host_port}/api/get_carbon_data_nested/"
|
|
154
|
+
else:
|
|
155
|
+
url = "https://www.dun-eideann-labs.co.uk/prisma_cloud/api/get_carbon_data_nested/"
|
|
156
|
+
|
|
157
|
+
headers = {
|
|
158
|
+
"X-API-Key": api.key,
|
|
159
|
+
"Content-Type": "application/json"
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
response = requests.post(url, json=payload, headers=headers, timeout=60)
|
|
164
|
+
data = response.json()
|
|
165
|
+
|
|
166
|
+
col_names_carbon = data.get('meta', {}).get('original_column_names', {})
|
|
167
|
+
col_names_water = data.get('meta', {}).get('Water', {}).get('original_column_names', {})
|
|
168
|
+
|
|
169
|
+
# col_names maps: api_field_name -> original_column_name
|
|
170
|
+
# Use directly as a rename map for each DataFrame
|
|
171
|
+
sim_iso = pd.DataFrame(data.get('Simulated', {}).get('isotherm', []))
|
|
172
|
+
sim_geo = pd.DataFrame(data.get('Simulated', {}).get('geometry', []))
|
|
173
|
+
exp_iso = pd.DataFrame(data.get('Experimental', {}).get('isotherm', []))
|
|
174
|
+
exp_geo = pd.DataFrame(data.get('Experimental', {}).get('geometry', []))
|
|
175
|
+
|
|
176
|
+
sim_water_dac = pd.DataFrame(data.get('Water', {}).get('Simulated', {}).get('DAC', []))
|
|
177
|
+
sim_water_cement = pd.DataFrame(data.get('Water', {}).get('Simulated', {}).get('cement', []))
|
|
178
|
+
sim_water_coal = pd.DataFrame(data.get('Water', {}).get('Simulated', {}).get('coal', []))
|
|
179
|
+
sim_water_ngcc = pd.DataFrame(data.get('Water', {}).get('Simulated', {}).get('NGCC-onshore', []))
|
|
180
|
+
exp_water_dac = pd.DataFrame(data.get('Water', {}).get('Experimental', {}).get('DAC', []))
|
|
181
|
+
exp_water_cement = pd.DataFrame(data.get('Water', {}).get('Experimental', {}).get('cement', []))
|
|
182
|
+
exp_water_coal = pd.DataFrame(data.get('Water', {}).get('Experimental', {}).get('coal', []))
|
|
183
|
+
exp_water_ngcc = pd.DataFrame(data.get('Water', {}).get('Experimental', {}).get('NGCC-onshore', []))
|
|
184
|
+
|
|
185
|
+
if not safe_names:
|
|
186
|
+
if col_names_carbon.get('isotherm'):
|
|
187
|
+
sim_iso = sim_iso.rename(columns=col_names_carbon['isotherm'])
|
|
188
|
+
exp_iso = exp_iso.rename(columns=col_names_carbon['isotherm'])
|
|
189
|
+
|
|
190
|
+
if col_names_carbon.get('simulated_geometry'):
|
|
191
|
+
sim_geo = sim_geo.rename(columns=col_names_carbon['simulated_geometry'])
|
|
192
|
+
|
|
193
|
+
if col_names_carbon.get('experimental_geometry'):
|
|
194
|
+
exp_geo = exp_geo.rename(columns=col_names_carbon['experimental_geometry'])
|
|
195
|
+
|
|
196
|
+
if col_names_water:
|
|
197
|
+
sim_water_dac = sim_water_dac.rename(columns=col_names_water)
|
|
198
|
+
exp_water_dac = exp_water_dac.rename(columns=col_names_water)
|
|
199
|
+
sim_water_cement = sim_water_cement.rename(columns=col_names_water)
|
|
200
|
+
exp_water_cement = exp_water_cement.rename(columns=col_names_water)
|
|
201
|
+
sim_water_coal = sim_water_coal.rename(columns=col_names_water)
|
|
202
|
+
exp_water_coal = exp_water_coal.rename(columns=col_names_water)
|
|
203
|
+
sim_water_ngcc = sim_water_ngcc.rename(columns=col_names_water)
|
|
204
|
+
exp_water_ngcc = exp_water_ngcc.rename(columns=col_names_water)
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
'Simulated': {'isotherm': sim_iso, 'geometry': sim_geo},
|
|
208
|
+
'Experimental': {'isotherm': exp_iso, 'geometry': exp_geo},
|
|
209
|
+
'meta': data.get('meta', {}),
|
|
210
|
+
'Water': {
|
|
211
|
+
'Simulated': {'DAC': sim_water_dac, 'cement': sim_water_cement, 'coal': sim_water_coal, 'NGCC-onshore': sim_water_ngcc},
|
|
212
|
+
'Experimental': {'DAC': exp_water_dac, 'cement': exp_water_cement, 'coal': exp_water_coal, 'NGCC-onshore': exp_water_ngcc},
|
|
213
|
+
'meta': data.get('meta', {}).get('water', {}),
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
except Exception as e:
|
|
218
|
+
print(f"Error retrieving carbon data nested: {e}")
|
|
219
|
+
return {}
|
|
220
|
+
|
|
221
|
+
def get_materials_data(self, payload={}, separate_experimental=True):
|
|
222
|
+
"""
|
|
223
|
+
Args:
|
|
224
|
+
payload: Dictionary containing query parameters for filtering.
|
|
225
|
+
separate_experimental: If True, split the result into separate 'simulated' and
|
|
226
|
+
'experimental' DataFrames based on the sim_or_exp flag,
|
|
227
|
+
before unpacking. Requires unpack=True.
|
|
228
|
+
"""
|
|
229
|
+
api = self
|
|
230
|
+
|
|
231
|
+
# Deprecated argument, forced by-pass as unpack=False format not likely needed.
|
|
232
|
+
# Can be removed after Apr-2026.
|
|
233
|
+
unpack=True
|
|
234
|
+
|
|
235
|
+
if self.dev:
|
|
236
|
+
urls = {
|
|
237
|
+
'localhost': f"http://localhost:{self.dev_host_port}/api/get_materials_data/"
|
|
238
|
+
}
|
|
239
|
+
base_urls = {
|
|
240
|
+
'localhost': f"http://localhost:{self.dev_host_port}"
|
|
241
|
+
}
|
|
242
|
+
else:
|
|
243
|
+
urls = {
|
|
244
|
+
'prisma-platform.org': "https://prisma-platform.org/api/get_materials_data/",
|
|
245
|
+
'dun-eideann-labs.co.uk': "https://www.dun-eideann-labs.co.uk/prisma_cloud/api/get_materials_data/"
|
|
246
|
+
}
|
|
247
|
+
base_urls = {
|
|
248
|
+
'prisma-platform.org': "https://prisma-platform.org",
|
|
249
|
+
'dun-eideann-labs.co.uk': "https://www.dun-eideann-labs.co.uk"
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
headers = {
|
|
253
|
+
"X-API-Key": api.key,
|
|
254
|
+
"Content-Type": "application/json"
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
try:
|
|
258
|
+
data_raw = None
|
|
259
|
+
source_key = None
|
|
260
|
+
for name, endpoint in urls.items():
|
|
261
|
+
try:
|
|
262
|
+
response = requests.post(endpoint, json=payload, headers=headers, timeout=60)
|
|
263
|
+
data_raw = response.json()
|
|
264
|
+
if data_raw.get('data'):
|
|
265
|
+
source_key = name
|
|
266
|
+
break
|
|
267
|
+
except Exception:
|
|
268
|
+
continue
|
|
269
|
+
|
|
270
|
+
if data_raw is None:
|
|
271
|
+
raise RuntimeError("All endpoints failed to return data.")
|
|
272
|
+
|
|
273
|
+
df = pd.DataFrame(data_raw.get('data', []))
|
|
274
|
+
|
|
275
|
+
# Prepend source base URL to cif_file column
|
|
276
|
+
if source_key and 'cif_file' in df.columns:
|
|
277
|
+
base = base_urls.get(source_key, '')
|
|
278
|
+
df['cif_file'] = df['cif_file'].apply(
|
|
279
|
+
lambda v: f"{base}{v}" if isinstance(v, str) and v else v
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
if unpack and not df.empty:
|
|
283
|
+
# Unpack carbon_isotherm independently (keep prefixed columns)
|
|
284
|
+
if 'carbon_isotherm' in df.columns:
|
|
285
|
+
unpacked = pd.json_normalize(
|
|
286
|
+
df['carbon_isotherm'].apply(lambda x: x[0] if isinstance(x, list) and len(x) > 0 else (x if isinstance(x, dict) else {}))
|
|
287
|
+
)
|
|
288
|
+
unpacked.columns = [f"carbon_isotherm__{c}" for c in unpacked.columns]
|
|
289
|
+
df = df.drop(columns=['carbon_isotherm']).join(unpacked)
|
|
290
|
+
|
|
291
|
+
# Unpack carbon_zeopp and carbon_zeopp_experimental, then combine shared fields
|
|
292
|
+
zeopp_cols = [c for c in ['carbon_zeopp', 'carbon_zeopp_experimental'] if c in df.columns]
|
|
293
|
+
if zeopp_cols:
|
|
294
|
+
unpacked_frames = {}
|
|
295
|
+
for col in zeopp_cols:
|
|
296
|
+
unpacked = pd.json_normalize(
|
|
297
|
+
df[col].apply(lambda x: x[0] if isinstance(x, list) and len(x) > 0 else (x if isinstance(x, dict) else {}))
|
|
298
|
+
)
|
|
299
|
+
unpacked_frames[col] = unpacked
|
|
300
|
+
df = df.drop(columns=[col])
|
|
301
|
+
|
|
302
|
+
# Derive sim_or_exp flag: 'exp' if carbon_zeopp_experimental has data, else 'sim'
|
|
303
|
+
if 'carbon_zeopp_experimental' in unpacked_frames:
|
|
304
|
+
exp_has_data = unpacked_frames['carbon_zeopp_experimental'].notna().any(axis=1)
|
|
305
|
+
else:
|
|
306
|
+
exp_has_data = pd.Series(False, index=df.index)
|
|
307
|
+
|
|
308
|
+
zeopp_sim_or_exp = exp_has_data.map({True: 'exp', False: 'sim'})
|
|
309
|
+
|
|
310
|
+
# Collect all field names across both unpacked frames
|
|
311
|
+
all_fields = set()
|
|
312
|
+
for unpacked in unpacked_frames.values():
|
|
313
|
+
all_fields.update(unpacked.columns)
|
|
314
|
+
all_fields.discard('sim_or_exp')
|
|
315
|
+
|
|
316
|
+
# Coalesce: for shared fields prefer carbon_zeopp, fall back to carbon_zeopp_experimental
|
|
317
|
+
combined = pd.DataFrame(index=df.index)
|
|
318
|
+
for field in sorted(all_fields):
|
|
319
|
+
series_list = [unpacked_frames[col][field] for col in zeopp_cols if field in unpacked_frames[col].columns]
|
|
320
|
+
if len(series_list) == 1:
|
|
321
|
+
combined[f"carbon_zeopp__{field}"] = series_list[0].values
|
|
322
|
+
else:
|
|
323
|
+
coalesced = series_list[0].copy()
|
|
324
|
+
for fallback in series_list[1:]:
|
|
325
|
+
coalesced = coalesced.combine_first(fallback.rename(coalesced.name))
|
|
326
|
+
combined[f"carbon_zeopp__{field}"] = coalesced.values
|
|
327
|
+
|
|
328
|
+
df = df.join(combined)
|
|
329
|
+
|
|
330
|
+
# Build a single top-level sim_or_exp column, coalescing sources in priority order
|
|
331
|
+
sim_or_exp = pd.Series(index=df.index, dtype=object)
|
|
332
|
+
for source_col in ['carbon_isotherm__sim_or_exp', 'carbon_zeopp__sim_or_exp']:
|
|
333
|
+
if source_col in df.columns:
|
|
334
|
+
sim_or_exp = sim_or_exp.combine_first(df[source_col])
|
|
335
|
+
df = df.drop(columns=[source_col])
|
|
336
|
+
# Fall back to the zeopp-derived flag if still null
|
|
337
|
+
if 'zeopp_sim_or_exp' in locals():
|
|
338
|
+
sim_or_exp = sim_or_exp.combine_first(zeopp_sim_or_exp)
|
|
339
|
+
|
|
340
|
+
# Insert sim_or_exp as the first column after unpacking
|
|
341
|
+
df.insert(0, 'sim_or_exp', sim_or_exp)
|
|
342
|
+
|
|
343
|
+
# Drop internal ID columns not needed in output
|
|
344
|
+
df = df.drop(columns=[c for c in ['carbon_isotherm__id'] if c in df.columns])
|
|
345
|
+
df = df.drop(columns=[c for c in ['carbon_zeopp__id'] if c in df.columns])
|
|
346
|
+
df = df.drop(columns=[c for c in ['carbon_zeopp__Molecule'] if c in df.columns])
|
|
347
|
+
df = df.drop(columns=[c for c in ['carbon_zeopp__good_structure'] if c in df.columns])
|
|
348
|
+
df = df.drop(columns=[c for c in ['id'] if c in df.columns])
|
|
349
|
+
|
|
350
|
+
# Rename unpacked columns to friendlier names
|
|
351
|
+
rename_map = {
|
|
352
|
+
'carbon_isotherm__Molecule': 'Molecule',
|
|
353
|
+
'carbon_isotherm__good_structure': 'Good Structure',
|
|
354
|
+
'carbon_isotherm__Henry_mol_per_kg_Pa': 'CO2 Henry (mol/kg/Pa)',
|
|
355
|
+
'carbon_isotherm__Pressure_bar': 'CO2 Pressure (bar)',
|
|
356
|
+
'carbon_isotherm__Uptake_mol_per_kg': 'CO2 Uptake (mol/kg)',
|
|
357
|
+
'carbon_isotherm__Heat_kJ_per_mol': 'CO2 Heat (kJ/mol)',
|
|
358
|
+
'carbon_isotherm__T_ref_K': 'CO2 T_ref (K)',
|
|
359
|
+
'carbon_zeopp__Binder': 'Zeo++ Binder',
|
|
360
|
+
'carbon_zeopp__Cp_J_per_gK': 'Zeo++ Cp_J_per_gK',
|
|
361
|
+
'carbon_zeopp__DOI': 'Zeo++ DOI',
|
|
362
|
+
'carbon_zeopp__Density_g_per_cm3': 'Zeo++ Density_g_per_cm3',
|
|
363
|
+
'carbon_zeopp__Formula': 'Zeo++ Formula',
|
|
364
|
+
'carbon_zeopp__Macroporosity': 'Zeo++ Macroporosity',
|
|
365
|
+
'carbon_zeopp__Molecule': 'Zeo++ Molecule',
|
|
366
|
+
'carbon_zeopp__POAVF': 'Zeo++ POAVF',
|
|
367
|
+
'carbon_zeopp__Pellet_Density_g_per_cm3': 'Zeo++ Pellet_Density_g_per_cm3',
|
|
368
|
+
'carbon_zeopp__Round': 'Zeo++ Round',
|
|
369
|
+
}
|
|
370
|
+
rename_map_ = {k: v for k, v in rename_map.items() if k in df.columns}
|
|
371
|
+
if rename_map_:
|
|
372
|
+
df = df.rename(columns=rename_map_)
|
|
373
|
+
|
|
374
|
+
if separate_experimental and unpack and not df.empty and 'sim_or_exp' in df.columns:
|
|
375
|
+
df_sim = df[df['sim_or_exp'] == 'sim'].reset_index(drop=True)
|
|
376
|
+
df_exp = df[df['sim_or_exp'] == 'exp'].reset_index(drop=True)
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
'simulated': df_sim,
|
|
380
|
+
'experimental': df_exp,
|
|
381
|
+
'meta': {'source': source_key},
|
|
382
|
+
}
|
|
383
|
+
else:
|
|
384
|
+
return {
|
|
385
|
+
'data': df,
|
|
386
|
+
'meta': {'source': source_key},
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
except Exception as e:
|
|
390
|
+
print(f"Error retrieving materials data: {e}")
|
|
391
|
+
return {}
|
|
392
|
+
|
|
393
|
+
#### -------------------------- AutoPrism -------------------------- ####
|
|
394
|
+
|
|
395
|
+
def update_adsorption_singlepoint(self, df):
|
|
396
|
+
"""
|
|
397
|
+
Update adsorption singlepoint data via PUT request.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
df: DataFrame containing the adsorption singlepoint data to update
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
pd.DataFrame: Response data from the API
|
|
404
|
+
"""
|
|
405
|
+
api = self
|
|
406
|
+
|
|
407
|
+
if self.dev:
|
|
408
|
+
url = f"http://localhost:{self.dev_host_port}/api/update_adsorption_singlepoint/"
|
|
409
|
+
else:
|
|
410
|
+
url = "https://www.dun-eideann-labs.co.uk/prisma_cloud/api/update_adsorption_singlepoint/"
|
|
411
|
+
|
|
412
|
+
headers = {
|
|
413
|
+
"X-API-Key": api.key,
|
|
414
|
+
"Content-Type": "application/json"
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
# Convert dataframe to JSON payload, handling NaN and infinite values
|
|
418
|
+
json_data = self._clean_dataframe_for_json(df)
|
|
419
|
+
|
|
420
|
+
response = requests.put(url, data=json_data, headers=headers, timeout=300) # 300sec (5 minutes)
|
|
421
|
+
|
|
422
|
+
return response.json()
|
|
423
|
+
|
|
424
|
+
def _clean_dataframe_for_json(self, df):
|
|
425
|
+
"""Helper method to clean DataFrame for JSON serialization."""
|
|
426
|
+
if df.empty:
|
|
427
|
+
return "[]"
|
|
428
|
+
|
|
429
|
+
import numpy as np
|
|
430
|
+
import json
|
|
431
|
+
|
|
432
|
+
# Use pandas to_json which handles NaN properly, then parse back
|
|
433
|
+
df_clean = df.copy()
|
|
434
|
+
df_clean = df_clean.replace([np.nan, np.inf, -np.inf], None)
|
|
435
|
+
|
|
436
|
+
# Convert using pandas to_json (which handles NaN correctly) then back to dict
|
|
437
|
+
json_str = df_clean.to_json(orient='records', force_ascii=False)
|
|
438
|
+
|
|
439
|
+
return json_str
|
|
440
|
+
|
|
441
|
+
def update_heat_capacity_all_tidy(self, df):
|
|
442
|
+
"""
|
|
443
|
+
Update heat capacity all tidy data via PUT request.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
df: DataFrame containing the heat capacity data to update
|
|
447
|
+
|
|
448
|
+
Returns:
|
|
449
|
+
dict: Response data from the API
|
|
450
|
+
"""
|
|
451
|
+
if self.dev:
|
|
452
|
+
url = f"http://localhost:{self.dev_host_port}/api/update_heat_capacity_all_tidy/"
|
|
453
|
+
else:
|
|
454
|
+
url = "https://www.dun-eideann-labs.co.uk/prisma_cloud/api/update_heat_capacity_all_tidy/"
|
|
455
|
+
|
|
456
|
+
headers = {
|
|
457
|
+
"X-API-Key": self.key,
|
|
458
|
+
"Content-Type": "application/json"
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
json_data = self._clean_dataframe_for_json(df)
|
|
462
|
+
response = requests.put(url, data=json_data, headers=headers, timeout=300)
|
|
463
|
+
|
|
464
|
+
return response.json()
|
|
465
|
+
|
|
466
|
+
def update_isotherm_h2(self, df):
|
|
467
|
+
"""
|
|
468
|
+
Update H2 isotherm data via PUT request.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
df: DataFrame containing the H2 isotherm data to update
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
dict: Response data from the API
|
|
475
|
+
"""
|
|
476
|
+
if self.dev:
|
|
477
|
+
url = f"http://localhost:{self.dev_host_port}/api/update_isotherm_h2/"
|
|
478
|
+
else:
|
|
479
|
+
url = "https://www.dun-eideann-labs.co.uk/prisma_cloud/api/update_isotherm_h2/"
|
|
480
|
+
|
|
481
|
+
headers = {
|
|
482
|
+
"X-API-Key": self.key,
|
|
483
|
+
"Content-Type": "application/json"
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
json_data = self._clean_dataframe_for_json(df)
|
|
487
|
+
response = requests.put(url, data=json_data, headers=headers, timeout=300)
|
|
488
|
+
|
|
489
|
+
return response.json()
|
|
490
|
+
|
|
491
|
+
def update_mofchecker(self, df):
|
|
492
|
+
"""
|
|
493
|
+
Update MOF checker data via PUT request.
|
|
494
|
+
|
|
495
|
+
Args:
|
|
496
|
+
df: DataFrame containing the MOF checker data to update
|
|
497
|
+
|
|
498
|
+
Returns:
|
|
499
|
+
dict: Response data from the API
|
|
500
|
+
"""
|
|
501
|
+
if self.dev:
|
|
502
|
+
url = f"http://localhost:{self.dev_host_port}/api/update_mofchecker/"
|
|
503
|
+
else:
|
|
504
|
+
url = "https://www.dun-eideann-labs.co.uk/prisma_cloud/api/update_mofchecker/"
|
|
505
|
+
|
|
506
|
+
headers = {
|
|
507
|
+
"X-API-Key": self.key,
|
|
508
|
+
"Content-Type": "application/json"
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
json_data = self._clean_dataframe_for_json(df)
|
|
512
|
+
response = requests.put(url, data=json_data, headers=headers, timeout=300)
|
|
513
|
+
|
|
514
|
+
return response.json()
|
|
515
|
+
|
|
516
|
+
def update_zeopp_metrics(self, df):
|
|
517
|
+
"""
|
|
518
|
+
Update Zeo++ metrics data via PUT request.
|
|
519
|
+
|
|
520
|
+
Args:
|
|
521
|
+
df: DataFrame containing the Zeo++ metrics data to update
|
|
522
|
+
|
|
523
|
+
Returns:
|
|
524
|
+
dict: Response data from the API
|
|
525
|
+
"""
|
|
526
|
+
if self.dev:
|
|
527
|
+
url = f"http://localhost:{self.dev_host_port}/api/update_zeopp_metrics/"
|
|
528
|
+
else:
|
|
529
|
+
url = "https://www.dun-eideann-labs.co.uk/prisma_cloud/api/update_zeopp_metrics/"
|
|
530
|
+
|
|
531
|
+
headers = {
|
|
532
|
+
"X-API-Key": self.key,
|
|
533
|
+
"Content-Type": "application/json"
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
json_data = self._clean_dataframe_for_json(df)
|
|
537
|
+
response = requests.put(url, data=json_data, headers=headers, timeout=300)
|
|
538
|
+
|
|
539
|
+
return response.json()
|