Coverage for /opt/conda/envs/apienv/lib/python3.10/site-packages/daiquiri/core/metadata/ispyalchemy/dc.py: 80%

298 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-02-06 02:13 +0000

1# -*- coding: utf-8 -*- 

2import os 

3import json 

4 

5from flask import g 

6 

7import sqlalchemy 

8from sqlalchemy import func, or_ 

9from sqlalchemy.sql.expression import cast 

10 

11from daiquiri.core.metadata.ispyalchemy.handler import IspyalchemyHandler 

12from daiquiri.core.metadata.ispyalchemy.utils import page, order 

13 

14 

15class DCHandler(IspyalchemyHandler): 

16 exported = [ 

17 "get_datacollections", 

18 "add_datacollection", 

19 "update_datacollection", 

20 "get_datacollection_attachments", 

21 "add_datacollection_attachment", 

22 "add_sampleaction", 

23 "update_sampleaction", 

24 "get_sampleactions", 

25 "get_sampleaction_positions", 

26 "add_sampleaction_position", 

27 "remove_sampleaction_position", 

28 "get_scanqualityindicators", 

29 "add_scanqualityindicators", 

30 "get_datacollectionplans", 

31 "add_datacollectionplan", 

32 "update_datacollectionplan", 

33 "remove_datacollectionplan", 

34 "_decode_scanparameters", 

35 ] 

36 

37 def get_datacollections(self, datacollectionid=None, **kwargs): 

38 with self.session_scope() as ses: 

39 duration = func.time_to_sec( 

40 func.timediff( 

41 self.DataCollection.endtime, 

42 self.DataCollection.starttime, 

43 ) 

44 ) 

45 dcid = self.DataCollection.datacollectionid 

46 starttime = self.DataCollection.starttime 

47 endtime = self.DataCollection.endtime 

48 

49 if not kwargs.get("datacollectiongroupid") and not kwargs.get("ungroup"): 

50 duration = func.sum(duration) 

51 dcid = func.min(self.DataCollection.datacollectionid).label( 

52 "datacollectionid" 

53 ) 

54 starttime = func.min(self.DataCollection.starttime).label("starttime") 

55 endtime = func.max(self.DataCollection.endtime).label("endtime") 

56 

57 datacollections = ( 

58 ses.query( 

59 dcid, 

60 self.DataCollectionGroup.datacollectiongroupid, 

61 self.DataCollectionGroup.blsampleid.label("sampleid"), 

62 self.BLSample.name.label("sample"), 

63 self.DataCollection.blsubsampleid.label("subsampleid"), 

64 self.DataCollection.datacollectionnumber, 

65 self.DataCollection.comments, 

66 starttime, 

67 endtime, 

68 self.DataCollection.xtalsnapshotfullpath1, 

69 self.DataCollection.xtalsnapshotfullpath2, 

70 self.DataCollection.xtalsnapshotfullpath3, 

71 self.DataCollection.xtalsnapshotfullpath4, 

72 self.DataCollection.filetemplate, 

73 self.DataCollection.imagedirectory, 

74 duration.label("duration"), 

75 self.DataCollection.runstatus, 

76 self.DataCollectionGroup.experimenttype, 

77 self.DataCollection.exposuretime, 

78 self.DataCollection.numberofimages, 

79 self.DataCollection.numberofpasses, 

80 self.DataCollection.wavelength, 

81 self.DataCollection.xbeam, 

82 self.DataCollection.ybeam, 

83 self.DataCollection.beamsizeatsamplex, 

84 self.DataCollection.beamsizeatsampley, 

85 cast(self.GridInfo.steps_x, sqlalchemy.Integer).label("steps_x"), 

86 cast(self.GridInfo.steps_y, sqlalchemy.Integer).label("steps_y"), 

87 self.GridInfo.patchesx, 

88 self.GridInfo.patchesy, 

89 self.GridInfo.dx_mm.label("dx_mm"), 

90 self.GridInfo.dy_mm.label("dy_mm"), 

91 self.GridInfo.snaked, 

92 self.DataCollection.datacollectionplanid, 

93 ) 

94 .join( 

95 self.DataCollectionGroup, 

96 self.DataCollectionGroup.datacollectiongroupid 

97 == self.DataCollection.datacollectiongroupid, 

98 ) 

99 .outerjoin( 

100 self.BLSample, 

101 self.BLSample.blsampleid == self.DataCollectionGroup.blsampleid, 

102 ) 

103 .join(self.BLSession) 

104 .outerjoin( 

105 self.GridInfo, 

106 self.GridInfo.datacollectionid 

107 == self.DataCollection.datacollectionid, 

108 ) 

109 ) 

110 

111 if kwargs.get("datacollectiongroupid"): 

112 datacollections = datacollections.filter( 

113 self.DataCollection.datacollectiongroupid 

114 == kwargs["datacollectiongroupid"] 

115 ) 

116 elif kwargs.get("ungroup") is not None: 

117 pass 

118 else: 

119 datacollections = datacollections.add_columns( 

120 func.count( 

121 func.distinct(self.DataCollection.datacollectionid) 

122 ).label("datacollections") 

123 ) 

124 datacollections = datacollections.group_by( 

125 self.DataCollection.datacollectiongroupid 

126 ) 

127 

128 if not kwargs.get("no_context"): 

129 datacollections = datacollections.filter( 

130 self.BLSession.sessionid == g.blsession.get("sessionid") 

131 ) 

132 

133 if not g.user.staff(): 

134 datacollections = datacollections.join( 

135 self.Session_has_Person 

136 ).filter(self.Session_has_Person.personid == g.user.get("personid")) 

137 

138 if kwargs.get("sampleid"): 

139 datacollections = datacollections.filter( 

140 self.DataCollectionGroup.blsampleid == kwargs.get("sampleid") 

141 ) 

142 

143 if kwargs.get("subsampleid"): 

144 datacollections = datacollections.filter( 

145 self.DataCollection.blsubsampleid == kwargs.get("subsampleid") 

146 ) 

147 

148 if kwargs.get("status"): 

149 datacollections = datacollections.filter( 

150 self.DataCollection.runstatus == kwargs.get("status") 

151 ) 

152 

153 if datacollectionid: 

154 datacollections = datacollections.filter( 

155 self.DataCollection.datacollectionid == datacollectionid 

156 ) 

157 datacollection = datacollections.first() 

158 if datacollection: 

159 datacollection = datacollection._asdict() 

160 return self._check_snapshots(datacollection) 

161 

162 else: 

163 total = datacollections.count() 

164 datacollections = order( 

165 datacollections, 

166 { 

167 "id": self.DataCollection.datacollectionid, 

168 "starttime": self.DataCollection.starttime, 

169 "runstatus": self.DataCollection.runstatus, 

170 "experimenttype": self.DataCollectionGroup.experimenttype, 

171 "duration": duration, 

172 }, 

173 default=["id", "desc"], 

174 **kwargs, 

175 ) 

176 datacollections = page(datacollections, **kwargs) 

177 

178 dcs = [r._asdict() for r in datacollections.all()] 

179 for dc in dcs: 

180 dc = self._check_snapshots(dc) 

181 

182 return {"total": total, "rows": dcs} 

183 

184 def add_datacollection(self, **kwargs): 

185 with self.session_scope() as ses: 

186 dcg_id = kwargs.get("datacollectiongroupid") 

187 if dcg_id is None: 

188 dcg = self.DataCollectionGroup( 

189 sessionid=kwargs.get("sessionid"), 

190 # New location of sampleid 

191 blsampleid=kwargs.get("sampleid"), 

192 experimenttype=kwargs.get("experimenttype"), 

193 scanparameters=kwargs.get("scanparameters"), 

194 ) 

195 

196 ses.add(dcg) 

197 ses.commit() 

198 

199 dcg_id = dcg.datacollectiongroupid 

200 

201 dc = self.DataCollection( 

202 datacollectiongroupid=dcg_id, 

203 filetemplate=kwargs.get("filetemplate"), 

204 imagedirectory=kwargs.get("imagedirectory"), 

205 starttime=kwargs.get("starttime"), 

206 # This is set in dcg, not dc 

207 # experimenttype=kwargs.get("experimenttype"), 

208 datacollectionplanid=kwargs.get("datacollectionplanid"), 

209 # Legacy, but still used in synchweb, should be in dcg above 

210 blsampleid=kwargs.get("sampleid"), 

211 blsubsampleid=kwargs.get("subsampleid"), 

212 datacollectionnumber=kwargs.get("datacollectionnumber", None), 

213 exposuretime=kwargs.get("exposuretime", None), 

214 numberofimages=kwargs.get("numberofimages", None), 

215 xtalsnapshotfullpath1=kwargs.get("xtalsnapshotfullpath1"), 

216 beamsizeatsamplex=kwargs.get("beamsizeatsamplex"), 

217 beamsizeatsampley=kwargs.get("beamsizeatsampley"), 

218 ) 

219 

220 ses.add(dc) 

221 ses.commit() 

222 

223 if kwargs.get("steps_x") or kwargs.get("steps_y"): 

224 grid = self.GridInfo( 

225 datacollectionid=dc.datacollectionid, 

226 steps_x=kwargs.get("steps_x"), 

227 steps_y=kwargs.get("steps_y"), 

228 patchesx=kwargs.get("patchesx"), 

229 patchesy=kwargs.get("patchesy"), 

230 dx_mm=kwargs.get("dx_mm"), 

231 dy_mm=kwargs.get("dy_mm"), 

232 pixelspermicronx=kwargs.get("pixelspermicronx"), 

233 pixelspermicrony=kwargs.get("pixelspermicrony"), 

234 snapshot_offsetxpixel=kwargs.get("snapshot_offsetxpixel"), 

235 snapshot_offsetypixel=kwargs.get("snapshot_offsetypixel"), 

236 snaked=kwargs.get("snaked"), 

237 ) 

238 

239 ses.add(grid) 

240 ses.commit() 

241 

242 return self.get_datacollections( 

243 datacollectionid=dc.datacollectionid, no_context=True 

244 ) 

245 

246 def update_datacollection(self, datacollectionid, **kwargs): 

247 with self.session_scope() as ses: 

248 chk = self.get_datacollections( 

249 datacollectionid=datacollectionid, no_context=kwargs.get("no_context") 

250 ) 

251 if chk: 

252 dc = ( 

253 ses.query(self.DataCollection) 

254 .filter(self.DataCollection.datacollectionid == datacollectionid) 

255 .first() 

256 ) 

257 

258 updatable = [ 

259 "runstatus", 

260 "endtime", 

261 "comments", 

262 "datacollectionnumber", 

263 "numberofimages", 

264 "numberofpasses", 

265 "xtalsnapshotfullpath1", 

266 "xtalsnapshotfullpath2", 

267 "xtalsnapshotfullpath3", 

268 "xtalsnapshotfullpath4", 

269 "wavelength", 

270 "transmission", 

271 "xbeam", 

272 "ybeam", 

273 "beamsizeatsamplex", 

274 "beamsizeatsampley", 

275 "imageprefix", 

276 "imagedirectory", 

277 "imagesuffix", 

278 "filetemplate", 

279 "imagecontainersubpath", 

280 "detectordistance", 

281 "detectorid", 

282 "flux", 

283 "exposuretime", 

284 ] 

285 for kw in kwargs: 

286 if kw in updatable: 

287 setattr(dc, kw, kwargs[kw]) 

288 

289 gridinfo = ( 

290 ses.query(self.GridInfo) 

291 .filter(self.GridInfo.datacollectionid == dc.datacollectionid) 

292 .first() 

293 ) 

294 

295 gr_updatable = [ 

296 "orientation", 

297 "steps_x", 

298 "steps_y", 

299 "patchesx", 

300 "patchesy", 

301 "dx_mm", 

302 "dy_mm", 

303 "pixelspermicronx", 

304 "pixelspermicrony", 

305 "snapshot_offsetxpixel", 

306 "snapshot_offsetypixel", 

307 "snaked", 

308 ] 

309 update_grid = False 

310 for kw in kwargs: 

311 if kw in gr_updatable: 

312 update_grid = True 

313 

314 if update_grid: 

315 if not gridinfo: 

316 gridinfo = self.GridInfo(datacollectionid=dc.datacollectionid) 

317 ses.add(gridinfo) 

318 ses.commit() 

319 

320 for kw in kwargs: 

321 if kw in gr_updatable: 

322 setattr(gridinfo, kw, kwargs[kw]) 

323 

324 ses.commit() 

325 

326 return self.get_datacollections( 

327 datacollectionid=datacollectionid, 

328 no_context=kwargs.get("no_context"), 

329 ) 

330 

331 def get_datacollection_attachments(self, datacollectionattachmentid=None, **kwargs): 

332 with self.session_scope() as ses: 

333 attachments = ( 

334 ses.query( 

335 self.DataCollectionFileAttachment.datacollectionfileattachmentid, 

336 self.DataCollectionFileAttachment.filefullpath, 

337 self.DataCollectionFileAttachment.filetype, 

338 ) 

339 .join(self.DataCollection) 

340 .join(self.DataCollectionGroup) 

341 ) 

342 

343 if not kwargs.get("no_context"): 

344 attachments = attachments.filter( 

345 self.DataCollectionGroup.sessionid == g.blsession.get("sessionid") 

346 ) 

347 

348 if kwargs.get("datacollectionid"): 

349 attachments = attachments.filter( 

350 self.DataCollection.datacollectionid == kwargs["datacollectionid"] 

351 ) 

352 

353 if kwargs.get("filetype"): 

354 attachments = attachments.filter( 

355 self.DataCollectionFileAttachment.filetype == kwargs["filetype"] 

356 ) 

357 

358 if datacollectionattachmentid: 

359 attachments = attachments.filter( 

360 self.DataCollectionFileAttachment.datacollectionfileattachmentid 

361 == datacollectionattachmentid 

362 ) 

363 attachment = attachments.first() 

364 if attachment: 

365 return self._add_file_to_attachment(attachment._asdict()) 

366 

367 else: 

368 attachments = [ 

369 self._add_file_to_attachment(r._asdict()) for r in attachments.all() 

370 ] 

371 return {"total": len(attachments), "rows": attachments} 

372 

373 def _add_file_to_attachment(self, attachment): 

374 attachment["filename"] = os.path.basename(attachment["filefullpath"]) 

375 attachment["extension"] = ( 

376 os.path.splitext(attachment["filename"])[1][1:].strip().lower() 

377 ) 

378 return attachment 

379 

380 def add_datacollection_attachment(self, **kwargs): 

381 dc = self.get_datacollections( 

382 datacollectionid=kwargs.get("datacollectionid"), 

383 no_context=kwargs.get("no_context"), 

384 ) 

385 

386 if dc: 

387 with self.session_scope() as ses: 

388 attachment = self.DataCollectionFileAttachment( 

389 datacollectionid=kwargs.get("datacollectionid"), 

390 filetype=kwargs.get("filetype"), 

391 filefullpath=kwargs.get("filepath"), 

392 ) 

393 

394 ses.add(attachment) 

395 ses.commit() 

396 

397 return self.get_datacollection_attachments( 

398 attachment.datacollectionfileattachmentid, 

399 no_context=kwargs.get("no_context"), 

400 ) 

401 

402 def get_sampleactions(self, sampleactionid=None, **kwargs): 

403 with self.session_scope() as ses: 

404 sampleactions = ses.query( 

405 self.RobotAction.robotactionid.label("sampleactionid"), 

406 self.RobotAction.blsessionid.label("sessionid"), 

407 self.RobotAction.blsampleid.label("sampleid"), 

408 self.RobotAction.actiontype, 

409 self.RobotAction.starttimestamp, 

410 self.RobotAction.endtimestamp, 

411 self.RobotAction.status, 

412 self.RobotAction.message, 

413 self.RobotAction.xtalsnapshotbefore, 

414 self.RobotAction.xtalsnapshotafter, 

415 self.RobotAction.resultfilepath, 

416 ) 

417 

418 if not kwargs.get("no_context"): 

419 sampleactions = sampleactions.filter( 

420 self.RobotAction.blsessionid == g.blsession.get("sessionid") 

421 ) 

422 

423 if kwargs.get("actiontype"): 

424 sampleactions = sampleactions.filter( 

425 self.RobotAction.actiontype == kwargs["actiontype"] 

426 ) 

427 

428 if kwargs.get("sampleid"): 

429 sampleactions = sampleactions.filter( 

430 self.RobotAction.blsampleid == kwargs["sampleid"] 

431 ) 

432 

433 if sampleactionid: 

434 sampleactions = sampleactions.filter( 

435 self.RobotAction.robotactionid == sampleactionid 

436 ) 

437 sampleaction = sampleactions.first() 

438 if sampleaction: 

439 sampleaction = sampleaction._asdict() 

440 sampleaction = self._check_result(sampleaction) 

441 return sampleaction 

442 

443 else: 

444 sampleactions = [r._asdict() for r in sampleactions.all()] 

445 for sampleaction in sampleactions: 

446 sampleaction = self._check_result(sampleaction) 

447 return {"total": len(sampleactions), "rows": sampleactions} 

448 

449 def _check_result(self, sampleaction): 

450 sampleaction["has_result"] = ( 

451 os.path.exists(sampleaction["resultfilepath"]) 

452 if sampleaction["resultfilepath"] 

453 else False 

454 ) 

455 return sampleaction 

456 

457 def add_sampleaction(self, **kwargs): 

458 with self.session_scope() as ses: 

459 sample = self.get_samples( 

460 sampleid=kwargs["sampleid"], no_context=kwargs.get("no_context") 

461 ) 

462 

463 if sample: 

464 sampleaction = self.RobotAction( 

465 blsessionid=kwargs.get("sessionid"), 

466 blsampleid=kwargs.get("sampleid"), 

467 actiontype=kwargs.get("actiontype"), 

468 starttimestamp=kwargs.get("starttime"), 

469 status=kwargs.get("status"), 

470 message=kwargs.get("message"), 

471 xtalsnapshotbefore=kwargs.get("xtalsnapshotbefore"), 

472 xtalsnapshotafter=kwargs.get("xtalsnapshotafter"), 

473 ) 

474 

475 ses.add(sampleaction) 

476 ses.commit() 

477 

478 return self.get_sampleactions( 

479 sampleactionid=sampleaction.robotactionid, 

480 no_context=kwargs.get("no_context"), 

481 ) 

482 

483 def update_sampleaction(self, sampleactionid, **kwargs): 

484 with self.session_scope() as ses: 

485 chk = self.get_sampleactions( 

486 sampleactionid=sampleactionid, no_context=kwargs.get("no_context") 

487 ) 

488 if chk: 

489 sampleaction = ( 

490 ses.query(self.RobotAction) 

491 .filter(self.RobotAction.robotactionid == sampleactionid) 

492 .first() 

493 ) 

494 

495 updatable = [ 

496 "starttimestamp", 

497 "endtimestamp", 

498 "status", 

499 "message", 

500 "xtalsnapshotbefore", 

501 "xtalsnapshotafter", 

502 "resultfilepath", 

503 ] 

504 for kw in kwargs: 

505 if kw in updatable: 

506 val = kwargs[kw] 

507 

508 if kw == "message": 

509 val = val[:255] 

510 

511 setattr(sampleaction, kw, val) 

512 

513 ses.commit() 

514 

515 return self.get_sampleactions( 

516 sampleactionid=sampleactionid, no_context=kwargs.get("no_context") 

517 ) 

518 

519 def get_sampleaction_positions( 

520 self, 

521 sampleactionpositionid=None, 

522 sampleactionid=None, 

523 type=None, 

524 no_context=None, 

525 ): 

526 with self.session_scope() as ses: 

527 sampleactionspositions = ( 

528 ses.query( 

529 self.RobotActionPosition.robotactionpositionid.label( 

530 "sampleactionpositionid" 

531 ), 

532 self.RobotActionPosition.type, 

533 self.RobotActionPosition.id, 

534 self.RobotActionPosition.posx, 

535 self.RobotActionPosition.posy, 

536 ) 

537 .join( 

538 self.RobotAction, 

539 self.RobotAction.robotactionid 

540 == self.RobotActionPosition.robotactionid, 

541 ) 

542 .group_by(self.RobotActionPosition.robotactionpositionid) 

543 ) 

544 

545 if not no_context: 

546 sampleactionspositions = sampleactionspositions.filter( 

547 self.RobotAction.blsessionid == g.blsession.get("sessionid") 

548 ) 

549 

550 if sampleactionid: 

551 sampleactionspositions = sampleactionspositions.filter( 

552 self.RobotActionPosition.robotactionid == sampleactionid 

553 ) 

554 

555 if type: 

556 sampleactionspositions = sampleactionspositions.filter( 

557 self.RobotActionPosition.type == type 

558 ) 

559 

560 if sampleactionpositionid: 

561 sampleactionspositions = sampleactionspositions.filter( 

562 self.RobotActionPosition.robotactionpositionid 

563 == sampleactionpositionid 

564 ) 

565 sampleactionspositions = sampleactionspositions.first() 

566 if sampleactionspositions: 

567 return sampleactionspositions._asdict() 

568 

569 else: 

570 sampleactionspositions = [ 

571 r._asdict() for r in sampleactionspositions.all() 

572 ] 

573 return { 

574 "total": len(sampleactionspositions), 

575 "rows": sampleactionspositions, 

576 } 

577 

578 def add_sampleaction_position(self, *, sampleactionid, posx, posy, type, id): 

579 sampleaction = self.get_sampleactions(sampleactionid=sampleactionid) 

580 if not sampleaction: 

581 return 

582 

583 with self.session_scope() as ses: 

584 sampleactionposition = self.RobotActionPosition( 

585 robotactionid=sampleactionid, 

586 posx=posx, 

587 posy=posy, 

588 type=type, 

589 id=id, 

590 ) 

591 ses.add(sampleactionposition) 

592 ses.commit() 

593 

594 return self.get_sampleaction_positions( 

595 sampleactionpositionid=sampleactionposition.robotactionpositionid 

596 ) 

597 

598 def remove_sampleaction_position(self, sampleactionpositionid, **kargs) -> bool: 

599 with self.session_scope() as ses: 

600 chk = self.get_sampleaction_positions( 

601 sampleactionpositionid=sampleactionpositionid 

602 ) 

603 if chk: 

604 sampleactionposition = ( 

605 ses.query(self.RobotActionPosition) 

606 .filter( 

607 self.RobotActionPosition.robotactionpositionid 

608 == sampleactionpositionid 

609 ) 

610 .first() 

611 ) 

612 

613 ses.delete(sampleactionposition) 

614 ses.commit() 

615 

616 return True 

617 

618 return False 

619 

620 def get_scanqualityindicators(self, **kwargs): 

621 with self.session_scope() as ses: 

622 sqis = ( 

623 ses.query( 

624 self.ImageQualityIndicators.imagenumber.label("point"), 

625 self.ImageQualityIndicators.totalintegratedsignal.label("total"), 

626 self.ImageQualityIndicators.spottotal.label("spots"), 

627 self.ImageQualityIndicators.autoprocprogramid, 

628 ) 

629 .join( 

630 self.DataCollection, 

631 self.DataCollection.datacollectionid 

632 == self.ImageQualityIndicators.datacollectionid, 

633 ) 

634 .join(self.DataCollectionGroup) 

635 ) 

636 

637 if not kwargs.get("no_context"): 

638 sqis = sqis.filter( 

639 self.DataCollectionGroup.sessionid == g.blsession.get("sessionid") 

640 ) 

641 

642 sqis = sqis.filter( 

643 self.DataCollection.datacollectionid == kwargs.get("datacollectionid") 

644 ) 

645 

646 sqis = sqis.order_by(self.ImageQualityIndicators.imagenumber) 

647 

648 data = {} 

649 for sqi in sqis.all(): 

650 row = sqi._asdict() 

651 for k in ["point", "total", "spots", "autoprocprogramid"]: 

652 if k not in data: 

653 data[k] = [] 

654 

655 data[k].append(row[k]) 

656 

657 return data 

658 

659 def add_scanqualityindicators(self, **kwargs): 

660 with self.session_scope() as ses: 

661 dc = self.get_datacollections( 

662 datacollectionid=kwargs["datacollectionid"], 

663 no_context=kwargs.get("no_context"), 

664 ) 

665 

666 if dc: 

667 args = {} 

668 for key in ["total", "spots"]: 

669 args[key] = None 

670 if kwargs.get(key) is not None: 

671 args[key] = float(kwargs[key]) 

672 

673 sqi = self.ImageQualityIndicators( 

674 datacollectionid=kwargs["datacollectionid"], 

675 imagenumber=kwargs["point"], 

676 totalintegratedsignal=args["total"], 

677 spottotal=args["spots"], 

678 ) 

679 

680 ses.add(sqi) 

681 ses.commit() 

682 

683 return f"{sqi.datacollectionid}-{sqi.imagenumber}" 

684 

685 def get_datacollectionplans( 

686 self, 

687 datacollectionplanid=None, 

688 blsampleid=None, 

689 status=None, 

690 queued=None, 

691 no_context=False, 

692 **kwargs, 

693 ): 

694 with self.session_scope() as ses: 

695 datacollectionplans = ( 

696 ses.query( 

697 self.DiffractionPlan.diffractionplanid.label( 

698 "datacollectionplanid" 

699 ), 

700 self.DiffractionPlan.scanparameters, 

701 self.DiffractionPlan.experimentkind, 

702 self.DiffractionPlan.recordtimestamp.label("timestamp"), 

703 self.DiffractionPlan.energy, 

704 self.DiffractionPlan.exposuretime, 

705 self.BLSample.blsampleid.label("sampleid"), 

706 self.BLSubSample.blsubsampleid.label("subsampleid"), 

707 self.BLSample.name.label("sample"), 

708 self.Protein.acronym.label("component"), 

709 self.BLSample_has_DataCollectionPlan.planorder, 

710 self.DataCollection.datacollectionid, 

711 self.ContainerQueueSample.containerqueuesampleid, 

712 func.IF( 

713 self.DataCollection.datacollectionid, "executed", "pending" 

714 ).label("status"), 

715 func.IF( 

716 self.ContainerQueueSample.containerqueuesampleid, True, False 

717 ).label("queued"), 

718 func.IF( 

719 self.BLSample_has_DataCollectionPlan.blsampleid, 

720 True, 

721 False, 

722 ).label("linked"), 

723 ) 

724 .outerjoin( 

725 self.BLSample_has_DataCollectionPlan, 

726 self.DiffractionPlan.diffractionplanid 

727 == self.BLSample_has_DataCollectionPlan.datacollectionplanid, 

728 ) 

729 .outerjoin( 

730 self.ContainerQueueSample, 

731 self.ContainerQueueSample.datacollectionplanid 

732 == self.DiffractionPlan.diffractionplanid, 

733 ) 

734 .outerjoin( 

735 self.BLSubSample, 

736 self.BLSubSample.blsubsampleid 

737 == self.ContainerQueueSample.blsubsampleid, 

738 ) 

739 .outerjoin( 

740 self.BLSample, 

741 or_( 

742 self.BLSample.blsampleid 

743 == self.BLSample_has_DataCollectionPlan.blsampleid, 

744 self.BLSample.blsampleid 

745 == self.ContainerQueueSample.blsampleid, 

746 self.BLSample.blsampleid == self.BLSubSample.blsampleid, 

747 ), 

748 ) 

749 .outerjoin( 

750 self.Crystal, self.Crystal.crystalid == self.BLSample.crystalid 

751 ) 

752 .outerjoin( 

753 self.Protein, self.Crystal.proteinid == self.Protein.proteinid 

754 ) 

755 .outerjoin( 

756 self.DataCollection, 

757 self.DataCollection.datacollectionplanid 

758 == self.DiffractionPlan.diffractionplanid, 

759 ) 

760 ) 

761 

762 if not no_context: 

763 datacollectionplans = datacollectionplans.filter( 

764 self.Protein.proposalid == g.blsession.get("proposalid") 

765 ) 

766 

767 if blsampleid: 

768 datacollectionplans = datacollectionplans.filter( 

769 self.BLSample_has_DataCollectionPlan.blsampleid == blsampleid 

770 ) 

771 

772 if status: 

773 if status == "executed": 

774 datacollectionplans = datacollectionplans.filter( 

775 self.DataCollection.datacollectionid != None # noqa: E711 

776 ) 

777 else: 

778 datacollectionplans = datacollectionplans.filter( 

779 self.DataCollection.datacollectionid == None # noqa: E711 

780 ) 

781 

782 if queued: 

783 datacollectionplans = datacollectionplans.filter( 

784 self.ContainerQueueSample.containerqueuesampleid 

785 != None # noqa: E711 

786 ) 

787 

788 if datacollectionplanid: 

789 datacollectionplans = datacollectionplans.filter( 

790 self.DiffractionPlan.diffractionplanid == datacollectionplanid 

791 ) 

792 datacollectionplan = datacollectionplans.first() 

793 if datacollectionplan: 

794 return self._decode_scanparameters(datacollectionplan._asdict()) 

795 

796 else: 

797 datacollectionplans = order( 

798 datacollectionplans, 

799 { 

800 "sampleid": self.BLSample.blsampleid, 

801 "planorder": self.BLSample_has_DataCollectionPlan.planorder, 

802 "datacollectionplanid": self.DiffractionPlan.diffractionplanid, 

803 }, 

804 default=["datacollectionplanid", "desc"], 

805 **kwargs, 

806 ) 

807 

808 datacollectionplans = [ 

809 self._decode_scanparameters(r._asdict()) 

810 for r in datacollectionplans.all() 

811 ] 

812 return {"total": len(datacollectionplans), "rows": datacollectionplans} 

813 

814 def _decode_scanparameters(self, datacollectionplan): 

815 if datacollectionplan["scanparameters"]: 

816 datacollectionplan["scanparameters"] = json.loads( 

817 datacollectionplan["scanparameters"] 

818 ) 

819 return datacollectionplan 

820 

821 def add_datacollectionplan( 

822 self, 

823 *, 

824 scanparameters, 

825 sampleid=None, 

826 energy=None, 

827 exposuretime=None, 

828 experimentkind=None, 

829 planorder=None, 

830 no_context=False, 

831 ): 

832 with self.session_scope() as ses: 

833 datacollectionplan = self.DiffractionPlan( 

834 scanparameters=json.dumps(scanparameters), 

835 experimentkind=experimentkind, 

836 energy=energy, 

837 exposuretime=exposuretime, 

838 ) 

839 

840 ses.add(datacollectionplan) 

841 ses.commit() 

842 

843 if sampleid: 

844 sample_has_datacollectionplan = self.BLSample_has_DataCollectionPlan( 

845 blsampleid=sampleid, 

846 datacollectionplanid=datacollectionplan.diffractionplanid, 

847 planorder=planorder, 

848 ) 

849 

850 ses.add(sample_has_datacollectionplan) 

851 ses.commit() 

852 

853 return self.get_datacollectionplans( 

854 datacollectionplanid=datacollectionplan.diffractionplanid, 

855 no_context=no_context, 

856 ) 

857 

858 def update_datacollectionplan( 

859 self, datacollectionplanid, no_context=False, **kwargs 

860 ): 

861 with self.session_scope() as ses: 

862 chk = self.get_datacollectionplans( 

863 datacollectionplanid=datacollectionplanid, 

864 no_context=kwargs.get("no_context"), 

865 ) 

866 if chk: 

867 datacollectionplan = ( 

868 ses.query(self.DiffractionPlan) 

869 .filter( 

870 self.DiffractionPlan.diffractionplanid == datacollectionplanid 

871 ) 

872 .first() 

873 ) 

874 

875 updatable = [ 

876 "scanparameters", 

877 "experimentkind", 

878 "exposuretime", 

879 "energy", 

880 ] 

881 for kw in kwargs: 

882 if kw in updatable: 

883 val = kwargs[kw] 

884 

885 if kw == "scanparameters": 

886 val = json.dumps(val) 

887 

888 setattr(datacollectionplan, kw, val) 

889 

890 sample_has_datacollectionplan = ( 

891 ses.query(self.BLSample_has_DataCollectionPlan) 

892 .filter( 

893 self.BLSample_has_DataCollectionPlan.datacollectionplanid 

894 == datacollectionplanid, 

895 ) 

896 .first() 

897 ) 

898 

899 updatable_in_sample = ["planorder"] 

900 for kw in kwargs: 

901 if kw in updatable_in_sample: 

902 val = kwargs[kw] 

903 

904 setattr(sample_has_datacollectionplan, kw, val) 

905 

906 ses.commit() 

907 

908 return self.get_datacollectionplans( 

909 datacollectionplanid=datacollectionplanid, no_context=no_context 

910 ) 

911 

912 def remove_datacollectionplan(self, datacollectionplanid, **kwargs): 

913 with self.session_scope() as ses: 

914 chk = self.get_datacollectionplans( 

915 datacollectionplanid=datacollectionplanid, 

916 no_context=kwargs.get("no_context"), 

917 ) 

918 

919 if chk: 

920 if chk["datacollectionid"]: 

921 raise RuntimeError( 

922 "Data collection plan has been executed, it cannot be deleted" 

923 ) 

924 

925 if chk["queued"]: 

926 raise RuntimeError( 

927 "Data collection plan has been queued, it cannot be deleted without being unqueued" 

928 ) 

929 

930 dp = ( 

931 ses.query( 

932 self.DiffractionPlan, 

933 ) 

934 .filter( 

935 self.DiffractionPlan.diffractionplanid == datacollectionplanid, 

936 ) 

937 .first() 

938 ) 

939 

940 sample_has_datacollectionplan = ( 

941 ses.query(self.BLSample_has_DataCollectionPlan) 

942 .filter( 

943 self.BLSample_has_DataCollectionPlan.datacollectionplanid 

944 == datacollectionplanid 

945 ) 

946 .first() 

947 ) 

948 

949 if sample_has_datacollectionplan: 

950 ses.delete(sample_has_datacollectionplan) 

951 ses.commit() 

952 

953 ses.delete(dp) 

954 ses.commit() 

955 

956 return True