mxcubecore 1.409.0__py3-none-any.whl → 1.411.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.
Potentially problematic release.
This version of mxcubecore might be problematic. Click here for more details.
- mxcubecore/HardwareObjects/ICATLIMS.py +135 -114
- mxcubecore/HardwareObjects/abstract/AbstractCollect.py +1 -8
- {mxcubecore-1.409.0.dist-info → mxcubecore-1.411.0.dist-info}/METADATA +1 -1
- {mxcubecore-1.409.0.dist-info → mxcubecore-1.411.0.dist-info}/RECORD +7 -7
- {mxcubecore-1.409.0.dist-info → mxcubecore-1.411.0.dist-info}/COPYING +0 -0
- {mxcubecore-1.409.0.dist-info → mxcubecore-1.411.0.dist-info}/COPYING.LESSER +0 -0
- {mxcubecore-1.409.0.dist-info → mxcubecore-1.411.0.dist-info}/WHEEL +0 -0
|
@@ -4,7 +4,7 @@ import shutil
|
|
|
4
4
|
from collections import defaultdict
|
|
5
5
|
from datetime import datetime, timedelta
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import TYPE_CHECKING, List, Optional
|
|
7
|
+
from typing import TYPE_CHECKING, Any, List, Optional
|
|
8
8
|
from zoneinfo import ZoneInfo
|
|
9
9
|
|
|
10
10
|
import requests
|
|
@@ -231,7 +231,6 @@ class ICATLIMS(AbstractLims):
|
|
|
231
231
|
def __add_download_path_to_processing_plan(
|
|
232
232
|
self, processing_plan, downloads: List[Download]
|
|
233
233
|
):
|
|
234
|
-
# Build a lookup for file_path by filename
|
|
235
234
|
file_path_lookup = {d.filename: d.path for d in downloads}
|
|
236
235
|
group_paths = defaultdict(list)
|
|
237
236
|
for d in downloads:
|
|
@@ -240,21 +239,23 @@ class ICATLIMS(AbstractLims):
|
|
|
240
239
|
|
|
241
240
|
# Enrich the processing_plan
|
|
242
241
|
for item in processing_plan:
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
242
|
+
key = item.get("key")
|
|
243
|
+
value = item.get("value")
|
|
244
|
+
if (
|
|
245
|
+
key == "reference"
|
|
246
|
+
and isinstance(value, str)
|
|
247
|
+
and value in file_path_lookup
|
|
248
|
+
):
|
|
249
|
+
item["value"] = {"filepath": file_path_lookup[value]}
|
|
250
|
+
if key == "search_models":
|
|
251
|
+
models = value
|
|
252
|
+
if isinstance(models, str):
|
|
253
|
+
models = json.loads(models)
|
|
254
|
+
for model in models:
|
|
255
|
+
group = model.get("pdb_group")
|
|
256
|
+
if group in group_paths:
|
|
257
|
+
model["file_paths"] = group_paths[group]
|
|
258
|
+
item["value"] = models
|
|
258
259
|
|
|
259
260
|
def _safe_json_loads(self, json_str):
|
|
260
261
|
try:
|
|
@@ -262,25 +263,7 @@ class ICATLIMS(AbstractLims):
|
|
|
262
263
|
except Exception:
|
|
263
264
|
return str(json_str)
|
|
264
265
|
|
|
265
|
-
def
|
|
266
|
-
self, tracking_sample: dict, puck: dict, sample_sheets: List[SampleSheet]
|
|
267
|
-
) -> dict:
|
|
268
|
-
"""
|
|
269
|
-
Convert a tracking sample and associated metadata into the internal
|
|
270
|
-
sample data structure.
|
|
271
|
-
- Extracts relevant sample metadata.
|
|
272
|
-
- Resolves protein acronym from the sample sheet if available.
|
|
273
|
-
- Maps experiment plan details into a diffraction plan dictionary.
|
|
274
|
-
- Assembles all relevant fields into a structured sample dictionary.
|
|
275
|
-
|
|
276
|
-
Args:
|
|
277
|
-
tracking_sample (dict): The raw sample data from tracking.
|
|
278
|
-
puck (dict): The puck (container) metadata associated with the sample.
|
|
279
|
-
sample_sheets (List[SampleSheet]): List of sample sheets used for lookup.
|
|
280
|
-
|
|
281
|
-
Returns:
|
|
282
|
-
dict: A dictionary representing the standardized internal sample format.
|
|
283
|
-
"""
|
|
266
|
+
def __extract_sample_identifiers(self, tracking_sample: dict, puck: dict) -> dict:
|
|
284
267
|
# Basic identifiers
|
|
285
268
|
sample_name = str(tracking_sample.get("name"))
|
|
286
269
|
|
|
@@ -302,66 +285,8 @@ class ICATLIMS(AbstractLims):
|
|
|
302
285
|
puck_name = puck.get("name", "UnknownPuck")
|
|
303
286
|
parcel_name = puck.get("parcelName")
|
|
304
287
|
parcel_id = puck.get("parcelId")
|
|
305
|
-
# Determine protein acronym using sample sheet if available
|
|
306
|
-
protein_acronym = sample_name # Default fallback
|
|
307
288
|
|
|
308
|
-
|
|
309
|
-
if sample_sheet:
|
|
310
|
-
protein_acronym = sample_sheet.name
|
|
311
|
-
|
|
312
|
-
# This converts to key-value pairs
|
|
313
|
-
experiment_plan = {
|
|
314
|
-
item["key"]: item["value"]
|
|
315
|
-
for item in tracking_sample.get("experimentPlan", {})
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
processing_plan = tracking_sample.get("processingPlan", [])
|
|
319
|
-
downloads: List[Download] = []
|
|
320
|
-
if processing_plan:
|
|
321
|
-
for item in processing_plan:
|
|
322
|
-
# when possible this converts the string to json
|
|
323
|
-
item["value"] = self._safe_json_loads(item["value"])
|
|
324
|
-
|
|
325
|
-
sample_information = None
|
|
326
|
-
try:
|
|
327
|
-
sample_information: SampleInformation = (
|
|
328
|
-
self.__get_sample_information_by(sample_sheet_id)
|
|
329
|
-
)
|
|
330
|
-
if sample_information is not None:
|
|
331
|
-
destination_folder = (
|
|
332
|
-
HWR.beamline.session.get_base_process_directory()
|
|
333
|
-
)
|
|
334
|
-
msg = "Download resource: "
|
|
335
|
-
msg += f"sample_sheet_id={sample_sheet_id} "
|
|
336
|
-
msg += f"destination_folder={destination_folder}"
|
|
337
|
-
logger.debug(msg)
|
|
338
|
-
downloads = self._download_resources(
|
|
339
|
-
sample_sheet_id,
|
|
340
|
-
sample_information.resources,
|
|
341
|
-
destination_folder,
|
|
342
|
-
sample_name,
|
|
343
|
-
)
|
|
344
|
-
msg = f"downloaded {len(downloads)} resources"
|
|
345
|
-
logger.debug(msg)
|
|
346
|
-
if len(downloads) > 0:
|
|
347
|
-
try:
|
|
348
|
-
self.__add_download_path_to_processing_plan(
|
|
349
|
-
processing_plan, downloads
|
|
350
|
-
)
|
|
351
|
-
except RuntimeError:
|
|
352
|
-
logger.exception(
|
|
353
|
-
"Failed __add_download_path_to_processing_plan"
|
|
354
|
-
)
|
|
355
|
-
|
|
356
|
-
processing_plan = {
|
|
357
|
-
item["key"]: item["value"] for item in processing_plan
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
except RuntimeError as e:
|
|
361
|
-
msg = f"error getting sample information {e}"
|
|
362
|
-
logger.warning(msg)
|
|
363
|
-
|
|
364
|
-
comments = tracking_sample.get("comments")
|
|
289
|
+
protein_acronym = self.__resolve_protein_acronym(sample_name, sample_sheet_id)
|
|
365
290
|
|
|
366
291
|
return {
|
|
367
292
|
"sampleName": sample_name,
|
|
@@ -376,29 +301,125 @@ class ICATLIMS(AbstractLims):
|
|
|
376
301
|
"SampleTrackingParcel_id": parcel_id,
|
|
377
302
|
"SampleTrackingContainer_id": puck_name,
|
|
378
303
|
"SampleTrackingContainer_name": parcel_id,
|
|
379
|
-
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
def __resolve_protein_acronym(self, sample_name: str, sample_sheet_id: str) -> str:
|
|
307
|
+
sample_sheet = self.get_sample_sheet_by_id(self.sample_sheets, sample_sheet_id)
|
|
308
|
+
return sample_sheet.name if sample_sheet else sample_name
|
|
309
|
+
|
|
310
|
+
def __parse_experiment_plan(self, tracking_sample: dict) -> dict[str, Any]:
|
|
311
|
+
return {
|
|
312
|
+
item["key"]: item["value"]
|
|
313
|
+
for item in tracking_sample.get("experimentPlan", {})
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
def __build_diffraction_plan(self, experiment_plan: dict) -> dict[str, Any]:
|
|
317
|
+
return {
|
|
318
|
+
# "diffractionPlanId": 457980, TODO: do we need this?
|
|
319
|
+
"experimentKind": experiment_plan.get("experimentKind"),
|
|
320
|
+
"numberOfPositions": experiment_plan.get("numberOfPositions"),
|
|
321
|
+
"observedResolution": experiment_plan.get("observedResolution"),
|
|
322
|
+
"preferredBeamDiameter": experiment_plan.get("preferredBeamDiameter"),
|
|
323
|
+
"radiationSensitivity": experiment_plan.get("radiationSensitivity"),
|
|
324
|
+
"requiredCompleteness": experiment_plan.get("requiredCompleteness"),
|
|
325
|
+
"requiredMultiplicity": experiment_plan.get("requiredMultiplicity"),
|
|
326
|
+
"requiredResolution": experiment_plan.get("requiredResolution"),
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
def __prepare_processing_plan(
|
|
330
|
+
self, tracking_sample: dict, sample_sheet_id: str, protein_acronym: str
|
|
331
|
+
) -> dict[str, Any]:
|
|
332
|
+
processing_plan = tracking_sample.get("processingPlan", [])
|
|
333
|
+
if not processing_plan:
|
|
334
|
+
return {}
|
|
335
|
+
|
|
336
|
+
# Convert string values to JSON if possible
|
|
337
|
+
for item in processing_plan:
|
|
338
|
+
item["value"] = self._safe_json_loads(item.get("value"))
|
|
339
|
+
|
|
340
|
+
downloads = self.__get_or_download_plan_resources(
|
|
341
|
+
sample_sheet_id, protein_acronym
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
if downloads:
|
|
345
|
+
try:
|
|
346
|
+
self.__add_download_path_to_processing_plan(processing_plan, downloads)
|
|
347
|
+
except RuntimeError:
|
|
348
|
+
logger.exception("Failed __add_download_path_to_processing_plan")
|
|
349
|
+
return {item["key"]: item["value"] for item in processing_plan}
|
|
350
|
+
|
|
351
|
+
def __get_or_download_plan_resources(
|
|
352
|
+
self, sample_sheet_id: str, protein_acronym: str
|
|
353
|
+
) -> List[Download]:
|
|
354
|
+
if not hasattr(self, "_downloads_cache"):
|
|
355
|
+
self._downloads_cache = {}
|
|
356
|
+
|
|
357
|
+
cache_key = (sample_sheet_id, protein_acronym)
|
|
358
|
+
if cache_key in self._downloads_cache:
|
|
359
|
+
logger.debug(f"Reusing cached downloads for {cache_key}")
|
|
360
|
+
return self._downloads_cache[cache_key]
|
|
361
|
+
|
|
362
|
+
sample_information = self.__get_sample_information_by(sample_sheet_id)
|
|
363
|
+
if not sample_information:
|
|
364
|
+
return []
|
|
365
|
+
|
|
366
|
+
# create subfolder per protein acronym
|
|
367
|
+
destination_folder = (
|
|
368
|
+
Path(HWR.beamline.session.get_base_process_directory())
|
|
369
|
+
/ "processing_plan_resources"
|
|
370
|
+
/ protein_acronym
|
|
371
|
+
)
|
|
372
|
+
destination_folder.mkdir(parents=True, exist_ok=True)
|
|
373
|
+
|
|
374
|
+
logger.debug(
|
|
375
|
+
f"Download resource: sample_sheet_id={sample_sheet_id} "
|
|
376
|
+
f"destination_folder={destination_folder}"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
downloads = self._download_resources(
|
|
380
|
+
sample_sheet_id, sample_information.resources, destination_folder, ""
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
logger.debug(f"downloaded {len(downloads)} resources")
|
|
384
|
+
|
|
385
|
+
self._downloads_cache[cache_key] = downloads
|
|
386
|
+
return downloads
|
|
387
|
+
|
|
388
|
+
def __to_sample(
|
|
389
|
+
self, tracking_sample: dict, puck: dict, sample_sheets: List[SampleSheet]
|
|
390
|
+
) -> dict[str, Any]:
|
|
391
|
+
"""
|
|
392
|
+
Convert a tracking sample and associated metadata into the internal
|
|
393
|
+
sample data structure.
|
|
394
|
+
- Extracts relevant sample metadata.
|
|
395
|
+
- Resolves protein acronym from the sample sheet if available.
|
|
396
|
+
- Maps experiment plan details into a diffraction plan dictionary.
|
|
397
|
+
- Assembles all relevant fields into a structured sample dictionary.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
tracking_sample (dict): The raw sample data from tracking.
|
|
401
|
+
puck (dict): The puck (container) metadata associated with the sample.
|
|
402
|
+
sample_sheets (List[SampleSheet]): List of sample sheets used for lookup.
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
dict: A dictionary representing the standardized internal sample format.
|
|
406
|
+
"""
|
|
407
|
+
sample_id_info = self.__extract_sample_identifiers(tracking_sample, puck)
|
|
408
|
+
experiment_plan = self.__parse_experiment_plan(tracking_sample)
|
|
409
|
+
processing_plan = self.__prepare_processing_plan(
|
|
410
|
+
tracking_sample,
|
|
411
|
+
sample_id_info["sample_sheet_id"],
|
|
412
|
+
sample_id_info["proteinAcronym"],
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
**sample_id_info,
|
|
380
417
|
"experimentType": experiment_plan.get("workflowType"),
|
|
381
418
|
"crystalSpaceGroup": experiment_plan.get("forceSpaceGroup"),
|
|
382
|
-
"diffractionPlan":
|
|
383
|
-
# "diffractionPlanId": 457980, TODO: do we need this?
|
|
384
|
-
"experimentKind": experiment_plan.get("experimentKind"),
|
|
385
|
-
"numberOfPositions": experiment_plan.get("numberOfPositions"),
|
|
386
|
-
"observedResolution": experiment_plan.get("observedResolution"),
|
|
387
|
-
"preferredBeamDiameter": experiment_plan.get("preferredBeamDiameter"),
|
|
388
|
-
"radiationSensitivity": experiment_plan.get("radiationSensitivity"),
|
|
389
|
-
"requiredCompleteness": experiment_plan.get("requiredCompleteness"),
|
|
390
|
-
"requiredMultiplicity": experiment_plan.get("requiredMultiplicity"),
|
|
391
|
-
"requiredResolution": experiment_plan.get("requiredResolution"),
|
|
392
|
-
},
|
|
393
|
-
"cellA": experiment_plan.get("unit_cell_a"),
|
|
394
|
-
"cellB": experiment_plan.get("unit_cell_b"),
|
|
395
|
-
"cellC": experiment_plan.get("unit_cell_c"),
|
|
396
|
-
"cellAlpha": experiment_plan.get("unit_cell_alpha"),
|
|
397
|
-
"cellBeta": experiment_plan.get("unit_cell_beta"),
|
|
398
|
-
"cellGamma": experiment_plan.get("unit_cell_gamma"),
|
|
419
|
+
"diffractionPlan": self.__build_diffraction_plan(experiment_plan),
|
|
399
420
|
"experimentPlan": experiment_plan,
|
|
400
421
|
"processingPlan": processing_plan,
|
|
401
|
-
"comments": comments,
|
|
422
|
+
"comments": tracking_sample.get("comments"),
|
|
402
423
|
}
|
|
403
424
|
|
|
404
425
|
def create_session(self, session_dict):
|
|
@@ -101,13 +101,6 @@ class AbstractCollect(HardwareObject, object):
|
|
|
101
101
|
def init(self):
|
|
102
102
|
self.ready_event = gevent.event.Event()
|
|
103
103
|
|
|
104
|
-
undulators = []
|
|
105
|
-
try:
|
|
106
|
-
for undulator in self.config.undulators:
|
|
107
|
-
undulators.append(undulator)
|
|
108
|
-
except Exception:
|
|
109
|
-
self.log.exception("")
|
|
110
|
-
|
|
111
104
|
beam_div_hor, beam_div_ver = HWR.beamline.beam.get_beam_divergence()
|
|
112
105
|
|
|
113
106
|
self.set_beamline_configuration(
|
|
@@ -126,7 +119,7 @@ class AbstractCollect(HardwareObject, object):
|
|
|
126
119
|
detector_px=HWR.beamline.detector.get_property("px"),
|
|
127
120
|
detector_py=HWR.beamline.detector.get_property("py"),
|
|
128
121
|
detector_binning_mode=HWR.beamline.detector.get_binning_mode(),
|
|
129
|
-
undulators=undulators,
|
|
122
|
+
undulators=self.get_property("undulators", []),
|
|
130
123
|
focusing_optic=self.get_property("focusing_optic"),
|
|
131
124
|
monochromator_type=self.get_property("monochromator"),
|
|
132
125
|
beam_divergence_vertical=beam_div_hor,
|
|
@@ -196,7 +196,7 @@ mxcubecore/HardwareObjects/GrobMotor.py,sha256=l5A9Us_ceLXlhEF6RNwyXQsI56fJiobWh
|
|
|
196
196
|
mxcubecore/HardwareObjects/GrobSampleChanger.py,sha256=LMetcL45fWrwP4C8rtENrEDNydelLpJ77SD1JC5C3go,8113
|
|
197
197
|
mxcubecore/HardwareObjects/Harvester.py,sha256=0TYUXmz-Pmfd7Dhz7ToUGHzJRuJmnga8-4BK4B0KGQA,26524
|
|
198
198
|
mxcubecore/HardwareObjects/HarvesterMaintenance.py,sha256=8s4yHDEFG-C1WYyW_RlwrFPSpc8o5hGi14aQuFQFrHs,9405
|
|
199
|
-
mxcubecore/HardwareObjects/ICATLIMS.py,sha256=
|
|
199
|
+
mxcubecore/HardwareObjects/ICATLIMS.py,sha256=VqRcCDTzEh_-yaJ9ZNbBlC1OAt79okKcOz9gdJEb1sc,54937
|
|
200
200
|
mxcubecore/HardwareObjects/ISARAMaint.py,sha256=I8LHXK6wCfzixsxWmmcqWlrdaL3AOX91XmVeAwT7GPk,8959
|
|
201
201
|
mxcubecore/HardwareObjects/LNLS/LNLSLIMS.py,sha256=oen9r3Furo_z7YSxo_wAwgXwaON9dWuZP5ob9tvnMO0,1462
|
|
202
202
|
mxcubecore/HardwareObjects/LNLS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -330,7 +330,7 @@ mxcubecore/HardwareObjects/abstract/AbstractAperture.py,sha256=kUaIOGuTrecbXXul7
|
|
|
330
330
|
mxcubecore/HardwareObjects/abstract/AbstractAuthenticator.py,sha256=F5ONnre9GEcg65hTiEYpTcj0frGojP1QrC2P8rmzcjU,1575
|
|
331
331
|
mxcubecore/HardwareObjects/abstract/AbstractBeam.py,sha256=Fvz5K3ov0Glsw8bO2oXiYbkFMK9L8CX4gldJg73lkr0,10389
|
|
332
332
|
mxcubecore/HardwareObjects/abstract/AbstractCharacterisation.py,sha256=knGIC9GVSY_mJfcpMpU9qP8OfaIngasGEc8PIVFsr_4,4990
|
|
333
|
-
mxcubecore/HardwareObjects/abstract/AbstractCollect.py,sha256=
|
|
333
|
+
mxcubecore/HardwareObjects/abstract/AbstractCollect.py,sha256=G6VowtuXAFhU5ohcjPHic7s2XU2z_qVGJ6QxD8XAUZM,32936
|
|
334
334
|
mxcubecore/HardwareObjects/abstract/AbstractDetector.py,sha256=4zZdvtNK86jdqoX3vPf9_k3ZX7JXLVr1Nf0oXRERFTU,11454
|
|
335
335
|
mxcubecore/HardwareObjects/abstract/AbstractEnergy.py,sha256=zG6tICqBdWcEMz8gGh7DFvP6CHz5xNlaRdcG4Y8tFps,4553
|
|
336
336
|
mxcubecore/HardwareObjects/abstract/AbstractEnergyScan.py,sha256=d5-BJMZE7qkbtV0m7_huMXgBaEO4ZCirUgN5UkXaH3k,6050
|
|
@@ -452,8 +452,8 @@ mxcubecore/utils/conversion.py,sha256=G1bk2Mi2ZwGbZa5pEeiFaKWxhSVXVGqu1L9_SioyUO
|
|
|
452
452
|
mxcubecore/utils/qt_import.py,sha256=yL6hLqk2qF-6SVjlVKTg2HTAa9vawPwmPybRPu39Iks,14720
|
|
453
453
|
mxcubecore/utils/tango.py,sha256=vwEVrIrWKEFaeaJUz3xbaC7XWHY8ZeJ-pfcSrTfZPIE,2114
|
|
454
454
|
mxcubecore/utils/units.py,sha256=Gh7ovTUN00XBMUoyDG5W7akCx1pROL-M6pK2z1ouemg,1361
|
|
455
|
-
mxcubecore-1.
|
|
456
|
-
mxcubecore-1.
|
|
457
|
-
mxcubecore-1.
|
|
458
|
-
mxcubecore-1.
|
|
459
|
-
mxcubecore-1.
|
|
455
|
+
mxcubecore-1.411.0.dist-info/COPYING,sha256=u-Mc8zCecwyo4YoP8UulmzCiZZ_MmCLROd_NBtOcRj0,35148
|
|
456
|
+
mxcubecore-1.411.0.dist-info/COPYING.LESSER,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
|
|
457
|
+
mxcubecore-1.411.0.dist-info/METADATA,sha256=zrf5N3BDAiYaNfaYWazSsqssQ5MI4aJoJYDVd8XSri8,4259
|
|
458
|
+
mxcubecore-1.411.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
459
|
+
mxcubecore-1.411.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|