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.4, created at 2024-11-14 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.DataCollection.datacollectionplanid, 

92 ) 

93 .join( 

94 self.DataCollectionGroup, 

95 self.DataCollectionGroup.datacollectiongroupid 

96 == self.DataCollection.datacollectiongroupid, 

97 ) 

98 .outerjoin( 

99 self.BLSample, 

100 self.BLSample.blsampleid == self.DataCollectionGroup.blsampleid, 

101 ) 

102 .join(self.BLSession) 

103 .outerjoin( 

104 self.GridInfo, 

105 self.GridInfo.datacollectionid 

106 == self.DataCollection.datacollectionid, 

107 ) 

108 ) 

109 

110 if kwargs.get("datacollectiongroupid"): 

111 datacollections = datacollections.filter( 

112 self.DataCollection.datacollectiongroupid 

113 == kwargs["datacollectiongroupid"] 

114 ) 

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

116 pass 

117 else: 

118 datacollections = datacollections.add_columns( 

119 func.count( 

120 func.distinct(self.DataCollection.datacollectionid) 

121 ).label("datacollections") 

122 ) 

123 datacollections = datacollections.group_by( 

124 self.DataCollection.datacollectiongroupid 

125 ) 

126 

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

128 datacollections = datacollections.filter( 

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

130 ) 

131 

132 if not g.user.staff(): 

133 datacollections = datacollections.join( 

134 self.Session_has_Person 

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

136 

137 if kwargs.get("sampleid"): 

138 datacollections = datacollections.filter( 

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

140 ) 

141 

142 if kwargs.get("subsampleid"): 

143 datacollections = datacollections.filter( 

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

145 ) 

146 

147 if kwargs.get("status"): 

148 datacollections = datacollections.filter( 

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

150 ) 

151 

152 if datacollectionid: 

153 datacollections = datacollections.filter( 

154 self.DataCollection.datacollectionid == datacollectionid 

155 ) 

156 datacollection = datacollections.first() 

157 if datacollection: 

158 datacollection = datacollection._asdict() 

159 return self._check_snapshots(datacollection) 

160 

161 else: 

162 total = datacollections.count() 

163 datacollections = order( 

164 datacollections, 

165 { 

166 "id": self.DataCollection.datacollectionid, 

167 "starttime": self.DataCollection.starttime, 

168 "runstatus": self.DataCollection.runstatus, 

169 "experimenttype": self.DataCollectionGroup.experimenttype, 

170 "duration": duration, 

171 }, 

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

173 **kwargs, 

174 ) 

175 datacollections = page(datacollections, **kwargs) 

176 

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

178 for dc in dcs: 

179 dc = self._check_snapshots(dc) 

180 

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

182 

183 def add_datacollection(self, **kwargs): 

184 with self.session_scope() as ses: 

185 dcg_id = kwargs.get("datacollectiongroupid") 

186 if dcg_id is None: 

187 dcg = self.DataCollectionGroup( 

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

189 # New location of sampleid 

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

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

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

193 ) 

194 

195 ses.add(dcg) 

196 ses.commit() 

197 

198 dcg_id = dcg.datacollectiongroupid 

199 

200 dc = self.DataCollection( 

201 datacollectiongroupid=dcg_id, 

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

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

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

205 # This is set in dcg, not dc 

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

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

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

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

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

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

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

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

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

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

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

217 ) 

218 

219 ses.add(dc) 

220 ses.commit() 

221 

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

223 grid = self.GridInfo( 

224 datacollectionid=dc.datacollectionid, 

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

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

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

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

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

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

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

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

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

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

235 ) 

236 

237 ses.add(grid) 

238 ses.commit() 

239 

240 return self.get_datacollections( 

241 datacollectionid=dc.datacollectionid, no_context=True 

242 ) 

243 

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

245 with self.session_scope() as ses: 

246 chk = self.get_datacollections( 

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

248 ) 

249 if chk: 

250 dc = ( 

251 ses.query(self.DataCollection) 

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

253 .first() 

254 ) 

255 

256 updatable = [ 

257 "runstatus", 

258 "endtime", 

259 "comments", 

260 "datacollectionnumber", 

261 "numberofimages", 

262 "numberofpasses", 

263 "xtalsnapshotfullpath1", 

264 "xtalsnapshotfullpath2", 

265 "xtalsnapshotfullpath3", 

266 "xtalsnapshotfullpath4", 

267 "wavelength", 

268 "transmission", 

269 "xbeam", 

270 "ybeam", 

271 "beamsizeatsamplex", 

272 "beamsizeatsampley", 

273 "imageprefix", 

274 "imagedirectory", 

275 "imagesuffix", 

276 "filetemplate", 

277 "imagecontainersubpath", 

278 "detectordistance", 

279 "detectorid", 

280 "flux", 

281 "exposuretime", 

282 ] 

283 for kw in kwargs: 

284 if kw in updatable: 

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

286 

287 gridinfo = ( 

288 ses.query(self.GridInfo) 

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

290 .first() 

291 ) 

292 

293 gr_updatable = [ 

294 "orientation", 

295 "steps_x", 

296 "steps_y", 

297 "dx_mm", 

298 "dy_mm", 

299 "pixelspermicronx", 

300 "pixelspermicrony", 

301 "snapshot_offsetxpixel", 

302 "snapshot_offsetypixel", 

303 ] 

304 update_grid = False 

305 for kw in kwargs: 

306 if kw in gr_updatable: 

307 update_grid = True 

308 

309 if update_grid: 

310 if not gridinfo: 

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

312 ses.add(gridinfo) 

313 ses.commit() 

314 

315 for kw in kwargs: 

316 if kw in gr_updatable: 

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

318 

319 ses.commit() 

320 

321 return self.get_datacollections( 

322 datacollectionid=datacollectionid, 

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

324 ) 

325 

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

327 with self.session_scope() as ses: 

328 attachments = ( 

329 ses.query( 

330 self.DataCollectionFileAttachment.datacollectionfileattachmentid, 

331 self.DataCollectionFileAttachment.filefullpath, 

332 self.DataCollectionFileAttachment.filetype, 

333 ) 

334 .join(self.DataCollection) 

335 .join(self.DataCollectionGroup) 

336 ) 

337 

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

339 attachments = attachments.filter( 

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

341 ) 

342 

343 if kwargs.get("datacollectionid"): 

344 attachments = attachments.filter( 

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

346 ) 

347 

348 if kwargs.get("filetype"): 

349 attachments = attachments.filter( 

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

351 ) 

352 

353 if datacollectionattachmentid: 

354 attachments = attachments.filter( 

355 self.DataCollectionFileAttachment.datacollectionfileattachmentid 

356 == datacollectionattachmentid 

357 ) 

358 attachment = attachments.first() 

359 if attachment: 

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

361 

362 else: 

363 attachments = [ 

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

365 ] 

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

367 

368 def _add_file_to_attachment(self, attachment): 

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

370 attachment["extension"] = ( 

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

372 ) 

373 return attachment 

374 

375 def add_datacollection_attachment(self, **kwargs): 

376 dc = self.get_datacollections( 

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

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

379 ) 

380 

381 if dc: 

382 with self.session_scope() as ses: 

383 attachment = self.DataCollectionFileAttachment( 

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

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

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

387 ) 

388 

389 ses.add(attachment) 

390 ses.commit() 

391 

392 return self.get_datacollection_attachments( 

393 attachment.datacollectionfileattachmentid, 

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

395 ) 

396 

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

398 with self.session_scope() as ses: 

399 sampleactions = ses.query( 

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

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

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

403 self.RobotAction.actiontype, 

404 self.RobotAction.starttimestamp, 

405 self.RobotAction.endtimestamp, 

406 self.RobotAction.status, 

407 self.RobotAction.message, 

408 self.RobotAction.xtalsnapshotbefore, 

409 self.RobotAction.xtalsnapshotafter, 

410 self.RobotAction.resultfilepath, 

411 ) 

412 

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

414 sampleactions = sampleactions.filter( 

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

416 ) 

417 

418 if kwargs.get("actiontype"): 

419 sampleactions = sampleactions.filter( 

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

421 ) 

422 

423 if kwargs.get("sampleid"): 

424 sampleactions = sampleactions.filter( 

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

426 ) 

427 

428 if sampleactionid: 

429 sampleactions = sampleactions.filter( 

430 self.RobotAction.robotactionid == sampleactionid 

431 ) 

432 sampleaction = sampleactions.first() 

433 if sampleaction: 

434 sampleaction = sampleaction._asdict() 

435 sampleaction = self._check_result(sampleaction) 

436 return sampleaction 

437 

438 else: 

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

440 for sampleaction in sampleactions: 

441 sampleaction = self._check_result(sampleaction) 

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

443 

444 def _check_result(self, sampleaction): 

445 sampleaction["has_result"] = ( 

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

447 if sampleaction["resultfilepath"] 

448 else False 

449 ) 

450 return sampleaction 

451 

452 def add_sampleaction(self, **kwargs): 

453 with self.session_scope() as ses: 

454 sample = self.get_samples( 

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

456 ) 

457 

458 if sample: 

459 sampleaction = self.RobotAction( 

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

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

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

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

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

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

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

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

468 ) 

469 

470 ses.add(sampleaction) 

471 ses.commit() 

472 

473 return self.get_sampleactions( 

474 sampleactionid=sampleaction.robotactionid, 

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

476 ) 

477 

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

479 with self.session_scope() as ses: 

480 chk = self.get_sampleactions( 

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

482 ) 

483 if chk: 

484 sampleaction = ( 

485 ses.query(self.RobotAction) 

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

487 .first() 

488 ) 

489 

490 updatable = [ 

491 "starttimestamp", 

492 "endtimestamp", 

493 "status", 

494 "message", 

495 "xtalsnapshotbefore", 

496 "xtalsnapshotafter", 

497 "resultfilepath", 

498 ] 

499 for kw in kwargs: 

500 if kw in updatable: 

501 val = kwargs[kw] 

502 

503 if kw == "message": 

504 val = val[:255] 

505 

506 setattr(sampleaction, kw, val) 

507 

508 ses.commit() 

509 

510 return self.get_sampleactions( 

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

512 ) 

513 

514 def get_sampleaction_positions( 

515 self, 

516 sampleactionpositionid=None, 

517 sampleactionid=None, 

518 type=None, 

519 no_context=None, 

520 ): 

521 with self.session_scope() as ses: 

522 sampleactionspositions = ( 

523 ses.query( 

524 self.RobotActionPosition.robotactionpositionid.label( 

525 "sampleactionpositionid" 

526 ), 

527 self.RobotActionPosition.type, 

528 self.RobotActionPosition.id, 

529 self.RobotActionPosition.posx, 

530 self.RobotActionPosition.posy, 

531 ) 

532 .join( 

533 self.RobotAction, 

534 self.RobotAction.robotactionid 

535 == self.RobotActionPosition.robotactionid, 

536 ) 

537 .group_by(self.RobotActionPosition.robotactionpositionid) 

538 ) 

539 

540 if not no_context: 

541 sampleactionspositions = sampleactionspositions.filter( 

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

543 ) 

544 

545 if sampleactionid: 

546 sampleactionspositions = sampleactionspositions.filter( 

547 self.RobotActionPosition.robotactionid == sampleactionid 

548 ) 

549 

550 if type: 

551 sampleactionspositions = sampleactionspositions.filter( 

552 self.RobotActionPosition.type == type 

553 ) 

554 

555 if sampleactionpositionid: 

556 sampleactionspositions = sampleactionspositions.filter( 

557 self.RobotActionPosition.robotactionpositionid 

558 == sampleactionpositionid 

559 ) 

560 sampleactionspositions = sampleactionspositions.first() 

561 if sampleactionspositions: 

562 return sampleactionspositions._asdict() 

563 

564 else: 

565 sampleactionspositions = [ 

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

567 ] 

568 return { 

569 "total": len(sampleactionspositions), 

570 "rows": sampleactionspositions, 

571 } 

572 

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

574 sampleaction = self.get_sampleactions(sampleactionid=sampleactionid) 

575 if not sampleaction: 

576 return 

577 

578 with self.session_scope() as ses: 

579 sampleactionposition = self.RobotActionPosition( 

580 robotactionid=sampleactionid, 

581 posx=posx, 

582 posy=posy, 

583 type=type, 

584 id=id, 

585 ) 

586 ses.add(sampleactionposition) 

587 ses.commit() 

588 

589 return self.get_sampleaction_positions( 

590 sampleactionpositionid=sampleactionposition.robotactionpositionid 

591 ) 

592 

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

594 with self.session_scope() as ses: 

595 chk = self.get_sampleaction_positions( 

596 sampleactionpositionid=sampleactionpositionid 

597 ) 

598 if chk: 

599 sampleactionposition = ( 

600 ses.query(self.RobotActionPosition) 

601 .filter( 

602 self.RobotActionPosition.robotactionpositionid 

603 == sampleactionpositionid 

604 ) 

605 .first() 

606 ) 

607 

608 ses.delete(sampleactionposition) 

609 ses.commit() 

610 

611 return True 

612 

613 return False 

614 

615 def get_scanqualityindicators(self, **kwargs): 

616 with self.session_scope() as ses: 

617 sqis = ( 

618 ses.query( 

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

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

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

622 self.ImageQualityIndicators.autoprocprogramid, 

623 ) 

624 .join( 

625 self.DataCollection, 

626 self.DataCollection.datacollectionid 

627 == self.ImageQualityIndicators.datacollectionid, 

628 ) 

629 .join(self.DataCollectionGroup) 

630 ) 

631 

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

633 sqis = sqis.filter( 

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

635 ) 

636 

637 sqis = sqis.filter( 

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

639 ) 

640 

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

642 

643 data = {} 

644 for sqi in sqis.all(): 

645 row = sqi._asdict() 

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

647 if k not in data: 

648 data[k] = [] 

649 

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

651 

652 return data 

653 

654 def add_scanqualityindicators(self, **kwargs): 

655 with self.session_scope() as ses: 

656 dc = self.get_datacollections( 

657 datacollectionid=kwargs["datacollectionid"], 

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

659 ) 

660 

661 if dc: 

662 args = {} 

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

664 args[key] = None 

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

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

667 

668 sqi = self.ImageQualityIndicators( 

669 datacollectionid=kwargs["datacollectionid"], 

670 imagenumber=kwargs["point"], 

671 totalintegratedsignal=args["total"], 

672 spottotal=args["spots"], 

673 ) 

674 

675 ses.add(sqi) 

676 ses.commit() 

677 

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

679 

680 def get_datacollectionplans( 

681 self, 

682 datacollectionplanid=None, 

683 blsampleid=None, 

684 status=None, 

685 queued=None, 

686 no_context=False, 

687 **kwargs, 

688 ): 

689 with self.session_scope() as ses: 

690 datacollectionplans = ( 

691 ses.query( 

692 self.DiffractionPlan.diffractionplanid.label( 

693 "datacollectionplanid" 

694 ), 

695 self.DiffractionPlan.scanparameters, 

696 self.DiffractionPlan.experimentkind, 

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

698 self.DiffractionPlan.energy, 

699 self.DiffractionPlan.exposuretime, 

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

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

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

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

704 self.BLSample_has_DataCollectionPlan.planorder, 

705 self.DataCollection.datacollectionid, 

706 self.ContainerQueueSample.containerqueuesampleid, 

707 func.IF( 

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

709 ).label("status"), 

710 func.IF( 

711 self.ContainerQueueSample.containerqueuesampleid, True, False 

712 ).label("queued"), 

713 func.IF( 

714 self.BLSample_has_DataCollectionPlan.blsampleid, 

715 True, 

716 False, 

717 ).label("linked"), 

718 ) 

719 .outerjoin( 

720 self.BLSample_has_DataCollectionPlan, 

721 self.DiffractionPlan.diffractionplanid 

722 == self.BLSample_has_DataCollectionPlan.datacollectionplanid, 

723 ) 

724 .outerjoin( 

725 self.ContainerQueueSample, 

726 self.ContainerQueueSample.datacollectionplanid 

727 == self.DiffractionPlan.diffractionplanid, 

728 ) 

729 .outerjoin( 

730 self.BLSubSample, 

731 self.BLSubSample.blsubsampleid 

732 == self.ContainerQueueSample.blsubsampleid, 

733 ) 

734 .outerjoin( 

735 self.BLSample, 

736 or_( 

737 self.BLSample.blsampleid 

738 == self.BLSample_has_DataCollectionPlan.blsampleid, 

739 self.BLSample.blsampleid 

740 == self.ContainerQueueSample.blsampleid, 

741 self.BLSample.blsampleid == self.BLSubSample.blsampleid, 

742 ), 

743 ) 

744 .outerjoin( 

745 self.Crystal, self.Crystal.crystalid == self.BLSample.crystalid 

746 ) 

747 .outerjoin( 

748 self.Protein, self.Crystal.proteinid == self.Protein.proteinid 

749 ) 

750 .outerjoin( 

751 self.DataCollection, 

752 self.DataCollection.datacollectionplanid 

753 == self.DiffractionPlan.diffractionplanid, 

754 ) 

755 ) 

756 

757 if not no_context: 

758 datacollectionplans = datacollectionplans.filter( 

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

760 ) 

761 

762 if blsampleid: 

763 datacollectionplans = datacollectionplans.filter( 

764 self.BLSample_has_DataCollectionPlan.blsampleid == blsampleid 

765 ) 

766 

767 if status: 

768 if status == "executed": 

769 datacollectionplans = datacollectionplans.filter( 

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

771 ) 

772 else: 

773 datacollectionplans = datacollectionplans.filter( 

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

775 ) 

776 

777 if queued: 

778 datacollectionplans = datacollectionplans.filter( 

779 self.ContainerQueueSample.containerqueuesampleid 

780 != None # noqa: E711 

781 ) 

782 

783 if datacollectionplanid: 

784 datacollectionplans = datacollectionplans.filter( 

785 self.DiffractionPlan.diffractionplanid == datacollectionplanid 

786 ) 

787 datacollectionplan = datacollectionplans.first() 

788 if datacollectionplan: 

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

790 

791 else: 

792 datacollectionplans = order( 

793 datacollectionplans, 

794 { 

795 "sampleid": self.BLSample.blsampleid, 

796 "planorder": self.BLSample_has_DataCollectionPlan.planorder, 

797 "datacollectionplanid": self.DiffractionPlan.diffractionplanid, 

798 }, 

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

800 **kwargs, 

801 ) 

802 

803 datacollectionplans = [ 

804 self._decode_scanparameters(r._asdict()) 

805 for r in datacollectionplans.all() 

806 ] 

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

808 

809 def _decode_scanparameters(self, datacollectionplan): 

810 if datacollectionplan["scanparameters"]: 

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

812 datacollectionplan["scanparameters"] 

813 ) 

814 return datacollectionplan 

815 

816 def add_datacollectionplan( 

817 self, 

818 *, 

819 scanparameters, 

820 sampleid=None, 

821 energy=None, 

822 exposuretime=None, 

823 experimentkind=None, 

824 planorder=None, 

825 no_context=False, 

826 ): 

827 with self.session_scope() as ses: 

828 datacollectionplan = self.DiffractionPlan( 

829 scanparameters=json.dumps(scanparameters), 

830 experimentkind=experimentkind, 

831 energy=energy, 

832 exposuretime=exposuretime, 

833 ) 

834 

835 ses.add(datacollectionplan) 

836 ses.commit() 

837 

838 if sampleid: 

839 sample_has_datacollectionplan = self.BLSample_has_DataCollectionPlan( 

840 blsampleid=sampleid, 

841 datacollectionplanid=datacollectionplan.diffractionplanid, 

842 planorder=planorder, 

843 ) 

844 

845 ses.add(sample_has_datacollectionplan) 

846 ses.commit() 

847 

848 return self.get_datacollectionplans( 

849 datacollectionplanid=datacollectionplan.diffractionplanid, 

850 no_context=no_context, 

851 ) 

852 

853 def update_datacollectionplan( 

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

855 ): 

856 with self.session_scope() as ses: 

857 chk = self.get_datacollectionplans( 

858 datacollectionplanid=datacollectionplanid, 

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

860 ) 

861 if chk: 

862 datacollectionplan = ( 

863 ses.query(self.DiffractionPlan) 

864 .filter( 

865 self.DiffractionPlan.diffractionplanid == datacollectionplanid 

866 ) 

867 .first() 

868 ) 

869 

870 updatable = [ 

871 "scanparameters", 

872 "experimentkind", 

873 "exposuretime", 

874 "energy", 

875 ] 

876 for kw in kwargs: 

877 if kw in updatable: 

878 val = kwargs[kw] 

879 

880 if kw == "scanparameters": 

881 val = json.dumps(val) 

882 

883 setattr(datacollectionplan, kw, val) 

884 

885 sample_has_datacollectionplan = ( 

886 ses.query(self.BLSample_has_DataCollectionPlan) 

887 .filter( 

888 self.BLSample_has_DataCollectionPlan.datacollectionplanid 

889 == datacollectionplanid, 

890 ) 

891 .first() 

892 ) 

893 

894 updatable_in_sample = ["planorder"] 

895 for kw in kwargs: 

896 if kw in updatable_in_sample: 

897 val = kwargs[kw] 

898 

899 setattr(sample_has_datacollectionplan, kw, val) 

900 

901 ses.commit() 

902 

903 return self.get_datacollectionplans( 

904 datacollectionplanid=datacollectionplanid, no_context=no_context 

905 ) 

906 

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

908 with self.session_scope() as ses: 

909 chk = self.get_datacollectionplans( 

910 datacollectionplanid=datacollectionplanid, 

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

912 ) 

913 

914 if chk: 

915 if chk["datacollectionid"]: 

916 raise RuntimeError( 

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

918 ) 

919 

920 if chk["queued"]: 

921 raise RuntimeError( 

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

923 ) 

924 

925 dp = ( 

926 ses.query( 

927 self.DiffractionPlan, 

928 ) 

929 .filter( 

930 self.DiffractionPlan.diffractionplanid == datacollectionplanid, 

931 ) 

932 .first() 

933 ) 

934 

935 sample_has_datacollectionplan = ( 

936 ses.query(self.BLSample_has_DataCollectionPlan) 

937 .filter( 

938 self.BLSample_has_DataCollectionPlan.datacollectionplanid 

939 == datacollectionplanid 

940 ) 

941 .first() 

942 ) 

943 

944 if sample_has_datacollectionplan: 

945 ses.delete(sample_has_datacollectionplan) 

946 ses.commit() 

947 

948 ses.delete(dp) 

949 ses.commit() 

950 

951 return True