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

364 statements  

« prev     ^ index     » next       coverage.py v7.6.5, created at 2024-11-15 02:12 +0000

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

2from datetime import datetime 

3import json 

4import os 

5 

6from flask import g 

7from PIL import Image 

8import sqlalchemy 

9from sqlalchemy import orm 

10from sqlalchemy.sql.expression import cast, func, distinct, and_ 

11 

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

13 

14 

15class SampleHandler(IspyalchemyHandler): 

16 exported = [ 

17 "get_components", 

18 "add_component", 

19 "update_component", 

20 "get_samples", 

21 "add_sample", 

22 "update_sample", 

23 "queue_sample", 

24 "unqueue_sample", 

25 "get_subsamples", 

26 "add_subsample", 

27 "update_subsample", 

28 "remove_subsample", 

29 "queue_subsample", 

30 "unqueue_subsample", 

31 "get_sample_tags", 

32 "get_sampleimages", 

33 "add_sampleimage", 

34 "add_container_inspection", 

35 ] 

36 

37 def get_components(self, componentid=None, **kwargs): 

38 with self.session_scope() as ses: 

39 components = ( 

40 ses.query( 

41 self.Protein.proteinid.label("componentid"), 

42 self.Protein.name, 

43 self.Protein.acronym, 

44 self.Protein.description, 

45 cast(self.Protein.molecularmass, sqlalchemy.Float).label( 

46 "molecularmass" 

47 ), 

48 self.Protein.density, 

49 self.Protein.sequence, 

50 func.count(distinct(self.BLSample.blsampleid)).label("samples"), 

51 func.count(distinct(self.DataCollection.datacollectionid)).label( 

52 "datacollections" 

53 ), 

54 ) 

55 .outerjoin( 

56 self.Crystal, self.Crystal.proteinid == self.Protein.proteinid 

57 ) 

58 .outerjoin(self.BLSample) 

59 .outerjoin( 

60 self.DataCollectionGroup, 

61 self.BLSample.blsampleid == self.DataCollectionGroup.blsampleid, 

62 ) 

63 .outerjoin( 

64 self.DataCollection, 

65 self.DataCollectionGroup.datacollectiongroupid 

66 == self.DataCollection.datacollectiongroupid, 

67 ) 

68 .group_by(self.Protein.proteinid) 

69 ) 

70 

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

72 components = components.filter( 

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

74 ) 

75 

76 if kwargs.get("sampleid"): 

77 components = components.filter( 

78 self.BLSample.blsampleid == kwargs["sampleid"] 

79 ) 

80 

81 if componentid: 

82 components = components.filter(self.Protein.proteinid == componentid) 

83 component = components.first() 

84 if component: 

85 return component._asdict() 

86 

87 else: 

88 components = [r._asdict() for r in components.all()] 

89 return {"total": len(components), "rows": components} 

90 

91 def add_component(self, **kwargs): 

92 with self.session_scope() as ses: 

93 component = self.Protein( 

94 proposalid=g.blsession.get("proposalid"), 

95 name=kwargs.get("name"), 

96 acronym=kwargs.get("acronym"), 

97 molecularmass=kwargs.get("molecularmass"), 

98 sequence=kwargs.get("sequence"), 

99 density=kwargs.get("density"), 

100 ) 

101 

102 ses.add(component) 

103 ses.commit() 

104 

105 return self.get_components(componentid=component.proteinid) 

106 

107 def update_component(self, componentid, **kwargs): 

108 with self.session_scope() as ses: 

109 chk = self.get_components(componentid=componentid) 

110 if chk: 

111 component = ( 

112 ses.query(self.Protein) 

113 .filter(self.Protein.proteinid == componentid) 

114 .first() 

115 ) 

116 

117 updatable = ["name", "acronym", "molecularmass", "density", "sequence"] 

118 for k, v in kwargs.items(): 

119 if k in updatable: 

120 setattr(component, k, v) 

121 

122 ses.commit() 

123 

124 return self.get_components(componentid=componentid) 

125 

126 def get_samples(self, sampleid=None, **kwargs): 

127 with self.session_scope() as ses: 

128 dc2 = orm.aliased(self.DataCollection) 

129 samples = ( 

130 ses.query( 

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

132 self.BLSample.name, 

133 self.BLSample.comments, 

134 self.BLSample.extrametadata, 

135 self.Container.containerid, 

136 cast(self.BLSample.location, sqlalchemy.Integer).label("location"), 

137 cast(self.Position.posx, sqlalchemy.Integer).label("offsetx"), 

138 cast(self.Position.posy, sqlalchemy.Integer).label("offsety"), 

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

140 self.Protein.proteinid.label("componentid"), 

141 func.count(distinct(self.BLSubSample.blsubsampleid)).label( 

142 "subsamples" 

143 ), 

144 func.count(distinct(self.DataCollection.datacollectionid)).label( 

145 "datacollections" 

146 ), 

147 func.IF( 

148 func.count(self.ContainerQueueSample.containerqueuesampleid) 

149 > func.count(dc2.datacollectionid), 

150 True, 

151 False, 

152 ).label("queued"), 

153 ) 

154 .join(self.Crystal, self.Crystal.crystalid == self.BLSample.crystalid) 

155 .join(self.Protein) 

156 .join(self.Proposal) 

157 .join(self.Container) 

158 .outerjoin( 

159 self.Position, self.Position.positionid == self.BLSample.positionid 

160 ) 

161 .outerjoin( 

162 self.DataCollectionGroup, 

163 self.DataCollectionGroup.blsampleid == self.BLSample.blsampleid, 

164 ) 

165 .outerjoin( 

166 self.DataCollection, 

167 self.DataCollection.datacollectiongroupid 

168 == self.DataCollectionGroup.datacollectiongroupid, 

169 ) 

170 .outerjoin( 

171 self.BLSubSample, 

172 self.BLSubSample.blsampleid == self.BLSample.blsampleid, 

173 ) 

174 .outerjoin( 

175 self.ContainerQueueSample, 

176 self.BLSample.blsampleid == self.ContainerQueueSample.blsampleid, 

177 ) 

178 .outerjoin( 

179 dc2, 

180 self.ContainerQueueSample.datacollectionplanid 

181 == dc2.datacollectionplanid, 

182 ) 

183 .filter( 

184 and_( 

185 self.Container.beamlinelocation 

186 == self._config["meta_beamline"], 

187 self.Container.samplechangerlocation is not None, 

188 self.Container.containerstatus == "processing", 

189 ) 

190 ) 

191 .group_by(self.BLSample.blsampleid) 

192 ) 

193 

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

195 samples = samples.filter( 

196 self.Proposal.proposalid == g.blsession.get("proposalid") 

197 ) 

198 

199 if kwargs.get("containerid"): 

200 samples = samples.filter( 

201 self.Container.containerid == kwargs.get("containerid") 

202 ) 

203 

204 if sampleid: 

205 samples = samples.filter(self.BLSample.blsampleid == sampleid) 

206 sample = samples.first() 

207 if sample: 

208 return sample._asdict() 

209 

210 else: 

211 samples = [r._asdict() for r in samples.all()] 

212 return {"total": len(samples), "rows": samples} 

213 

214 def add_sample(self, **kwargs): 

215 if kwargs.get("containerid") is None: 

216 containerid = self._ensure_default_container() 

217 else: 

218 containerid = kwargs["containerid"] 

219 

220 if kwargs.get("componentid") is None: 

221 proteinid = self._ensure_default_component() 

222 chk = self.get_components(componentid=proteinid) 

223 if not chk: 

224 return 

225 

226 else: 

227 proteinid = kwargs["componentid"] 

228 

229 samples = self.get_samples(containerid=containerid)["rows"] 

230 max_loc = 0 

231 for s in samples: 

232 if s["location"] > max_loc: 

233 max_loc = s["location"] 

234 

235 with self.session_scope() as ses: 

236 pos = self.Position(posx=kwargs["offsetx"], posy=kwargs["offsety"]) 

237 ses.add(pos) 

238 ses.commit() 

239 

240 crystal = self.Crystal(proteinid=proteinid) 

241 

242 ses.add(crystal) 

243 ses.commit() 

244 

245 sample = self.BLSample( 

246 containerid=containerid, 

247 crystalid=crystal.crystalid, 

248 location=max_loc + 1, 

249 positionid=pos.positionid, 

250 name=kwargs.get("name"), 

251 comments=kwargs.get("comments"), 

252 ) 

253 

254 ses.add(sample) 

255 ses.commit() 

256 

257 return self.get_samples(sampleid=sample.blsampleid) 

258 

259 def update_sample(self, sampleid, **kwargs): 

260 with self.session_scope() as ses: 

261 chk = self.get_samples(sampleid=sampleid) 

262 if chk: 

263 sample = ( 

264 ses.query(self.BLSample) 

265 .filter(self.BLSample.blsampleid == sampleid) 

266 .first() 

267 ) 

268 

269 updatable = ["name", "comments", "offsetx", "offsety", "extrametadata"] 

270 for k, v in kwargs.items(): 

271 if k in updatable: 

272 setattr(sample, k, v) 

273 

274 if "componentid" in kwargs: 

275 crystal = ( 

276 ses.query(self.Crystal) 

277 .filter(self.Crystal.crystalid == sample.crystalid) 

278 .first() 

279 ) 

280 

281 crystal.proteinid = kwargs["componentid"] 

282 

283 ses.commit() 

284 

285 return self.get_samples(sampleid=sampleid) 

286 

287 def get_subsamples(self, subsampleid=None, **kwargs): 

288 dc2 = orm.aliased(self.DataCollection) 

289 position2 = orm.aliased(self.Position) 

290 

291 with self.session_scope() as ses: 

292 subsamples = ( 

293 ses.query( 

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

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

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

297 self.BLSubSample.type, 

298 self.BLSubSample.source, 

299 self.BLSubSample.comments, 

300 self.BLSubSample.extrametadata, 

301 self.Container.containerid, 

302 cast(self.Position.posx, sqlalchemy.Integer).label("x"), 

303 cast(self.Position.posy, sqlalchemy.Integer).label("y"), 

304 cast(position2.posx, sqlalchemy.Integer).label("x2"), 

305 cast(position2.posy, sqlalchemy.Integer).label("y2"), 

306 func.count(distinct(self.DataCollection.datacollectionid)).label( 

307 "datacollections" 

308 ), 

309 func.IF( 

310 func.count(self.ContainerQueueSample.containerqueuesampleid) 

311 > func.count(dc2.datacollectionid), 

312 True, 

313 False, 

314 ).label("queued"), 

315 func.group_concat( 

316 distinct( 

317 func.concat( 

318 self.Positioner.positioner, ":", self.Positioner.value 

319 ) 

320 ) 

321 ).label("positions"), 

322 ) 

323 .join( 

324 self.BLSample, 

325 self.BLSample.blsampleid == self.BLSubSample.blsampleid, 

326 ) 

327 .join(self.Crystal) 

328 .join(self.Protein) 

329 .join(self.Proposal) 

330 .join(self.Container) 

331 .outerjoin( 

332 self.Position, 

333 self.Position.positionid == self.BLSubSample.positionid, 

334 ) 

335 .outerjoin( 

336 position2, position2.positionid == self.BLSubSample.position2id 

337 ) 

338 .outerjoin( 

339 self.DataCollection, 

340 self.DataCollection.blsubsampleid == self.BLSubSample.blsubsampleid, 

341 ) 

342 .outerjoin( 

343 self.ContainerQueueSample, 

344 self.BLSubSample.blsubsampleid 

345 == self.ContainerQueueSample.blsubsampleid, 

346 ) 

347 .outerjoin( 

348 dc2, 

349 self.ContainerQueueSample.datacollectionplanid 

350 == dc2.datacollectionplanid, 

351 ) 

352 .outerjoin(self.BLSubSample_has_Positioner) 

353 .outerjoin( 

354 self.Positioner, 

355 self.BLSubSample_has_Positioner.positionerid 

356 == self.Positioner.positionerid, 

357 ) 

358 .group_by(self.BLSubSample.blsubsampleid) 

359 ) 

360 

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

362 subsamples = subsamples.filter( 

363 self.Proposal.proposalid == g.blsession.get("proposalid") 

364 ) 

365 

366 if kwargs.get("sampleid"): 

367 subsamples = subsamples.filter( 

368 self.BLSample.blsampleid == kwargs.get("sampleid") 

369 ) 

370 

371 if subsampleid: 

372 subsamples = subsamples.filter( 

373 self.BLSubSample.blsubsampleid == subsampleid 

374 ) 

375 subsample = subsamples.first() 

376 if subsample: 

377 subs = subsample._asdict() 

378 subs["positions"] = self._pos_to_dict(subs["positions"]) 

379 return subs 

380 

381 else: 

382 subs = [r._asdict() for r in subsamples.all()] 

383 

384 for s in subs: 

385 s["positions"] = self._pos_to_dict(s["positions"]) 

386 

387 return {"total": len(subs), "rows": subs} 

388 

389 def _pos_to_dict(self, positions): 

390 ps = {} 

391 if positions: 

392 for p in positions.split(","): 

393 k, v = p.split(":") 

394 ps[k] = v 

395 

396 return ps 

397 

398 def add_subsample(self, **kwargs): 

399 with self.session_scope() as ses: 

400 sample = self.get_samples(sampleid=kwargs["sampleid"]) 

401 if sample: 

402 position = self.Position(posx=kwargs["x"], posy=kwargs["y"]) 

403 ses.add(position) 

404 ses.commit() 

405 

406 position2 = None 

407 if kwargs.get("x2"): 

408 position2 = self.Position(posx=kwargs["x2"], posy=kwargs["y2"]) 

409 ses.add(position2) 

410 ses.commit() 

411 

412 subsample = self.BLSubSample( 

413 blsampleid=kwargs["sampleid"], 

414 type=kwargs["type"], 

415 comments=kwargs.get("comments"), 

416 positionid=position.positionid, 

417 position2id=position2.positionid if position2 else None, 

418 ) 

419 

420 ses.add(subsample) 

421 ses.commit() 

422 

423 if kwargs.get("positions"): 

424 for k, v in kwargs["positions"].items(): 

425 positioner = self.Positioner(positioner=k, value=v) 

426 ses.add(positioner) 

427 ses.commit() 

428 

429 blshaspos = self.BLSubSample_has_Positioner( 

430 blsubsampleid=subsample.blsubsampleid, 

431 positionerid=positioner.positionerid, 

432 ) 

433 ses.add(blshaspos) 

434 ses.commit() 

435 

436 return self.get_subsamples(subsampleid=subsample.blsubsampleid) 

437 

438 def update_subsample(self, subsampleid, **kwargs): 

439 with self.session_scope() as ses: 

440 chk = self.get_subsamples(subsampleid=subsampleid) 

441 if chk: 

442 position2 = orm.aliased(self.Position) 

443 subsample, position, position2 = ( 

444 ses.query(self.BLSubSample, self.Position, position2) 

445 .outerjoin( 

446 self.Position, 

447 self.Position.positionid == self.BLSubSample.positionid, 

448 ) 

449 .outerjoin( 

450 position2, position2.positionid == self.BLSubSample.position2id 

451 ) 

452 .filter(self.BLSubSample.blsubsampleid == subsampleid) 

453 .first() 

454 ) 

455 

456 additional = ( 

457 ses.query(self.Positioner) 

458 .join(self.BLSubSample_has_Positioner) 

459 .filter( 

460 self.BLSubSample_has_Positioner.blsubsampleid == subsampleid 

461 ) 

462 .all() 

463 ) 

464 

465 if kwargs.get("positions"): 

466 for pos in additional: 

467 if pos.positioner in kwargs["positions"]: 

468 pos.value = kwargs["positions"][pos.positioner] 

469 

470 if subsample: 

471 updatable = ["comments", "extrametadata"] 

472 for k, v in kwargs.items(): 

473 if k in updatable: 

474 setattr(subsample, k, v) 

475 

476 if kwargs.get("x"): 

477 position.posx = kwargs["x"] 

478 

479 if kwargs.get("y"): 

480 position.posy = kwargs["y"] 

481 

482 if position2: 

483 if kwargs.get("x2"): 

484 position2.posx = kwargs["x2"] 

485 

486 if kwargs.get("y2"): 

487 position2.posy = kwargs["y2"] 

488 

489 ses.commit() 

490 

491 return self.get_subsamples(subsampleid=subsampleid) 

492 

493 def remove_subsample(self, subsampleid): 

494 with self.session_scope() as ses: 

495 chk = self.get_subsamples(subsampleid=subsampleid) 

496 if chk: 

497 positions = ( 

498 ses.query(self.Positioner) 

499 .join(self.BLSubSample_has_Positioner) 

500 .filter( 

501 self.BLSubSample_has_Positioner.blsubsampleid == subsampleid 

502 ) 

503 .all() 

504 ) 

505 

506 bls_has_pos = ( 

507 ses.query(self.BLSubSample_has_Positioner) 

508 .filter( 

509 self.BLSubSample_has_Positioner.blsubsampleid == subsampleid 

510 ) 

511 .all() 

512 ) 

513 for blp in bls_has_pos: 

514 ses.delete(blp) 

515 ses.commit() 

516 

517 for p in positions: 

518 ses.delete(p) 

519 ses.commit() 

520 

521 position2 = orm.aliased(self.Position) 

522 subsample, position, position2 = ( 

523 ses.query(self.BLSubSample, self.Position, position2) 

524 .outerjoin( 

525 self.Position, 

526 self.Position.positionid == self.BLSubSample.positionid, 

527 ) 

528 .outerjoin( 

529 position2, position2.positionid == self.BLSubSample.position2id 

530 ) 

531 .filter(self.BLSubSample.blsubsampleid == subsampleid) 

532 .first() 

533 ) 

534 

535 if subsample: 

536 ses.delete(position) 

537 

538 if position2: 

539 ses.delete(position2) 

540 

541 ses.delete(subsample) 

542 

543 ses.commit() 

544 

545 return True 

546 

547 def queue_subsample(self, subsampleid, datacollectionplanid=None, **kwargs): 

548 with self.session_scope() as ses: 

549 subsample = self.get_subsamples(subsampleid=subsampleid) 

550 if subsample: 

551 if not datacollectionplanid: 

552 dp = self.add_datacollectionplan(no_context=True, **kwargs) 

553 datacollectionplanid = dp["datacollectionplanid"] 

554 

555 cq = ( 

556 ses.query(self.ContainerQueue.containerqueueid) 

557 .join( 

558 self.BLSample, 

559 self.BLSample.containerid == self.ContainerQueue.containerid, 

560 ) 

561 .join( 

562 self.BLSubSample, 

563 self.BLSample.blsampleid == self.BLSubSample.blsampleid, 

564 ) 

565 .filter(self.BLSubSample.blsubsampleid == subsampleid) 

566 .first() 

567 ) 

568 

569 if not cq: 

570 cq = self.ContainerQueue( 

571 containerid=subsample["containerid"], 

572 personid=g.user["personid"], 

573 ) 

574 ses.add(cq) 

575 ses.commit() 

576 

577 cqs = self.ContainerQueueSample( 

578 containerqueueid=cq.containerqueueid, 

579 datacollectionplanid=datacollectionplanid, 

580 blsubsampleid=subsampleid, 

581 ) 

582 

583 ses.add(cqs) 

584 ses.commit() 

585 

586 return cqs.containerqueuesampleid, datacollectionplanid 

587 

588 def unqueue_subsample(self, subsampleid, **kwargs): 

589 with self.session_scope() as ses: 

590 if not kwargs.get("containerqueuesampleid"): 

591 raise AttributeError("missing containerqueuesampleid") 

592 

593 subsample = self.get_subsamples(subsampleid=subsampleid) 

594 if subsample: 

595 cqs, dp = ( 

596 ses.query(self.ContainerQueueSample, self.DiffractionPlan) 

597 .filter( 

598 self.ContainerQueueSample.datacollectionplanid 

599 == self.DiffractionPlan.diffractionplanid 

600 ) 

601 .filter( 

602 self.ContainerQueueSample.containerqueuesampleid 

603 == kwargs["containerqueuesampleid"] 

604 ) 

605 .first() 

606 ) 

607 

608 if cqs: 

609 ses.delete(cqs) 

610 ses.commit() 

611 

612 if dp: 

613 ses.delete(dp) 

614 ses.commit() 

615 

616 return True 

617 

618 def queue_sample(self, sampleid, datacollectionplanid=None, **kwargs): 

619 with self.session_scope() as ses: 

620 sample = self.get_samples(sampleid=sampleid) 

621 if sample: 

622 if not datacollectionplanid: 

623 dp = self.add_datacollectionplan(no_context=True, **kwargs) 

624 datacollectionplanid = dp["datacollectionplanid"] 

625 

626 cq = ( 

627 ses.query(self.ContainerQueue.containerqueueid) 

628 .join( 

629 self.BLSample, 

630 self.BLSample.containerid == self.ContainerQueue.containerid, 

631 ) 

632 .filter(self.BLSample.blsampleid == sampleid) 

633 .first() 

634 ) 

635 

636 if not cq: 

637 cq = self.ContainerQueue( 

638 containerid=sample["containerid"], 

639 personid=g.user["personid"], 

640 ) 

641 ses.add(cq) 

642 ses.commit() 

643 

644 cqs = self.ContainerQueueSample( 

645 containerqueueid=cq.containerqueueid, 

646 datacollectionplanid=datacollectionplanid, 

647 blsampleid=sampleid, 

648 ) 

649 

650 ses.add(cqs) 

651 ses.commit() 

652 

653 return cqs.containerqueuesampleid, datacollectionplanid 

654 

655 def unqueue_sample(self, sampleid, **kwargs): 

656 with self.session_scope() as ses: 

657 if not kwargs.get("containerqueuesampleid"): 

658 raise AttributeError("missing containerqueuesampleid") 

659 

660 sample = self.get_samples( 

661 sampleid=sampleid, no_context=kwargs.get("no_context") 

662 ) 

663 if sample: 

664 cqs, dp = ( 

665 ses.query(self.ContainerQueueSample, self.DiffractionPlan) 

666 .filter( 

667 self.ContainerQueueSample.datacollectionplanid 

668 == self.DiffractionPlan.diffractionplanid 

669 ) 

670 .filter( 

671 self.ContainerQueueSample.containerqueuesampleid 

672 == kwargs["containerqueuesampleid"] 

673 ) 

674 .first() 

675 ) 

676 

677 if cqs: 

678 ses.delete(cqs) 

679 ses.commit() 

680 

681 if dp: 

682 sample_has_datacollectionplan = ( 

683 ses.query(self.BLSample_has_DataCollectionPlan) 

684 .filter( 

685 self.BLSample_has_DataCollectionPlan.datacollectionplanid 

686 == dp.diffractionplanid, 

687 ) 

688 .first() 

689 ) 

690 

691 # If this plan is associated to a sample it was created using the plans routes 

692 # rather than automatically, unqueuing should preserve the plan 

693 if not sample_has_datacollectionplan: 

694 ses.delete(dp) 

695 ses.commit() 

696 

697 return True 

698 

699 def get_sample_tags(self, type: str = None): 

700 with self.session_scope() as ses: 

701 sample_tags = [] 

702 print("sample tags", type) 

703 if (type and type.value == "sample") or type is None: 

704 sample_tags = ( 

705 ses.query( 

706 func.json_extract(self.BLSample.extrametadata, "$.tags").label( 

707 "tags" 

708 ) 

709 ) 

710 .join(self.Crystal) 

711 .join(self.Protein) 

712 .join(self.Proposal) 

713 .filter(self.Proposal.proposalid == g.blsession.get("proposalid")) 

714 .group_by(self.BLSample.blsampleid) 

715 ) 

716 sample_tags = [r._asdict() for r in sample_tags.all()] 

717 

718 subsample_tags = [] 

719 if (type and type.value == "subsample") or type is None: 

720 subsample_tags = ( 

721 ses.query( 

722 func.json_extract( 

723 self.BLSubSample.extrametadata, "$.tags" 

724 ).label("tags") 

725 ) 

726 .join( 

727 self.BLSample, 

728 self.BLSample.blsampleid == self.BLSubSample.blsampleid, 

729 ) 

730 .join(self.Crystal) 

731 .join(self.Protein) 

732 .join(self.Proposal) 

733 .filter(self.Proposal.proposalid == g.blsession.get("proposalid")) 

734 .group_by(self.BLSubSample.blsubsampleid) 

735 ) 

736 

737 subsample_tags = [r._asdict() for r in subsample_tags.all()] 

738 

739 tag_list = [] 

740 for tags in [sample_tags, subsample_tags]: 

741 for tag in tags: 

742 if tag["tags"]: 

743 tag_list.extend(json.loads(tag["tags"])) 

744 

745 tag_list.sort() 

746 return {"tags": list(set(tag_list))} 

747 

748 def get_sampleimages(self, sampleimageid=None, **kwargs): 

749 with self.session_scope() as ses: 

750 sampleimages = ( 

751 ses.query( 

752 self.BLSampleImage.blsampleimageid.label("sampleimageid"), 

753 self.BLSampleImage.imagefullpath.label("file"), 

754 (self.BLSampleImage.micronsperpixelx * 1000).label("scalex"), 

755 (self.BLSampleImage.micronsperpixely * 1000).label("scaley"), 

756 self.BLSampleImage.offsetx, 

757 (self.BLSampleImage.offsety * -1).label("offsety"), 

758 func.concat( 

759 f"/{self._base_url}/samples/images/", 

760 self.BLSampleImage.blsampleimageid, 

761 ).label("url"), 

762 func.group_concat( 

763 distinct( 

764 func.concat( 

765 self.Positioner.positioner, ":", self.Positioner.value 

766 ) 

767 ) 

768 ).label("positions"), 

769 ) 

770 .join(self.BLSample) 

771 .join(self.Container) 

772 .join(self.Crystal) 

773 .join(self.Protein) 

774 .outerjoin(self.BLSampleImage_has_Positioner) 

775 .outerjoin( 

776 self.Positioner, 

777 self.BLSampleImage_has_Positioner.positionerid 

778 == self.Positioner.positionerid, 

779 ) 

780 .group_by(self.BLSampleImage.blsampleimageid) 

781 ) 

782 

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

784 sampleimages = sampleimages.filter( 

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

786 ) 

787 

788 if kwargs.get("containerid"): 

789 sampleimages = sampleimages.filter( 

790 self.Container.containerid == kwargs.get("containerid") 

791 ) 

792 

793 if kwargs.get("sampleid"): 

794 sampleimages = sampleimages.filter( 

795 self.BLSampleImage.blsampleid == kwargs.get("sampleid") 

796 ) 

797 

798 if sampleimageid: 

799 sampleimages = sampleimages.filter( 

800 self.BLSampleImage.blsampleimageid == sampleimageid 

801 ) 

802 sampleimage = sampleimages.first() 

803 if sampleimage: 

804 si = sampleimage._asdict() 

805 si["positions"] = self._pos_to_dict(si["positions"]) 

806 return si 

807 

808 else: 

809 sampleimages = [r._asdict() for r in sampleimages.all()] 

810 for s in sampleimages: 

811 s["positions"] = self._pos_to_dict(s["positions"]) 

812 if os.path.exists(s["file"]): 

813 image = Image.open(s["file"]) 

814 s["width"], s["height"] = image.size 

815 

816 return {"total": len(sampleimages), "rows": sampleimages} 

817 

818 def add_sampleimage(self, **kwargs): 

819 with self.session_scope() as ses: 

820 sample = self.get_samples( 

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

822 ) 

823 if sample: 

824 if "containerinspectionid" not in kwargs: 

825 containerinspectionid = self._ensure_default_inspection( 

826 sample["containerid"] 

827 ) 

828 else: 

829 containerinspectionid = kwargs["containerinspectionid"] 

830 

831 sampleimage = self.BLSampleImage( 

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

833 bltimestamp=datetime.now(), 

834 imagefullpath=kwargs.get("file"), 

835 micronsperpixelx=kwargs.get("scalex") / 1000, 

836 micronsperpixely=kwargs.get("scaley") / 1000, 

837 offsetx=kwargs.get("offsetx"), 

838 offsety=-1 * kwargs.get("offsety"), 

839 containerinspectionid=containerinspectionid, 

840 ) 

841 

842 ses.add(sampleimage) 

843 ses.commit() 

844 

845 if kwargs.get("positions"): 

846 for k, v in kwargs["positions"].items(): 

847 positioner = self.Positioner(positioner=k, value=v) 

848 ses.add(positioner) 

849 ses.commit() 

850 

851 blshasimagepos = self.BLSampleImage_has_Positioner( 

852 blsampleimageid=sampleimage.blsampleimageid, 

853 positionerid=positioner.positionerid, 

854 value=v, 

855 ) 

856 ses.add(blshasimagepos) 

857 ses.commit() 

858 

859 return self.get_sampleimages( 

860 sampleimageid=sampleimage.blsampleimageid, 

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

862 ) 

863 

864 def add_container_inspection(self, containerid): 

865 with self.session_scope() as ses: 

866 containerinspection = self.ContainerInspection( 

867 containerid=containerid, 

868 inspectiontypeid=1, 

869 manual=1, 

870 scheduledtimestamp=datetime.now(), 

871 completedtimestamp=datetime.now(), 

872 ) 

873 

874 ses.add(containerinspection) 

875 ses.commit() 

876 

877 return containerinspection.containerinspectionid 

878 

879 def _ensure_default_inspection(self, containerid): 

880 with self.session_scope() as ses: 

881 containerinspection = ( 

882 ses.query(self.ContainerInspection.containerinspectionid) 

883 .filter(self.ContainerInspection.containerid == containerid) 

884 .first() 

885 ) 

886 

887 if not containerinspection: 

888 return self.add_container_inspection(containerid) 

889 

890 return containerinspection.containerinspectionid 

891 

892 def _ensure_default_container(self): 

893 with self.session_scope() as ses: 

894 shipment_name = g.blsession.get("session") + "_Shipment1" 

895 shipment = ( 

896 ses.query(self.Shipping.shippingid) 

897 .filter(self.Shipping.proposalid == g.blsession.get("proposalid")) 

898 .filter(self.Shipping.shippingname == shipment_name) 

899 .first() 

900 ) 

901 

902 if not shipment: 

903 shipment = self.Shipping( 

904 shippingname=shipment_name, 

905 proposalid=g.blsession.get("proposalid"), 

906 creationdate=datetime.now(), 

907 ) 

908 

909 ses.add(shipment) 

910 ses.commit() 

911 

912 dewar_name = g.blsession.get("session") + "_Dewar1" 

913 dewar = ( 

914 ses.query(self.Dewar.dewarid) 

915 .filter(self.Dewar.shippingid == shipment.shippingid) 

916 .filter(self.Dewar.code == dewar_name) 

917 .first() 

918 ) 

919 

920 if not dewar: 

921 dewar = self.Dewar( 

922 shippingid=shipment.shippingid, 

923 code=dewar_name, 

924 dewarstatus="processing", 

925 ) 

926 ses.add(dewar) 

927 ses.commit() 

928 

929 container_name = g.blsession.get("session") + "_Container1" 

930 container = ( 

931 ses.query(self.Container.containerid) 

932 .filter(self.Container.dewarid == dewar.dewarid) 

933 .filter(self.Container.code == container_name) 

934 .first() 

935 ) 

936 

937 if not container: 

938 container = self.Container( 

939 dewarid=dewar.dewarid, 

940 code=container_name, 

941 containertype="Box", 

942 capacity=self._config.get("meta_capacity", 25), 

943 beamlinelocation=self._config["meta_beamline"], 

944 samplechangerlocation=1, 

945 bltimestamp=datetime.now(), 

946 containerstatus="processing", 

947 ) 

948 ses.add(container) 

949 ses.commit() 

950 

951 containerhistory = self.ContainerHistory( 

952 containerid=container.containerid, 

953 status="processing", 

954 location=1, 

955 beamlinename=self._config["meta_beamline"], 

956 ) 

957 

958 ses.add(containerhistory) 

959 ses.commit() 

960 

961 return container.containerid 

962 

963 def _ensure_default_component(self): 

964 with self.session_scope() as ses: 

965 component_name = g.blsession.get("session") + "_Component1" 

966 component_acronym = g.blsession.get("session") + "_c1" 

967 

968 component = ( 

969 ses.query(self.Protein.proteinid) 

970 .filter(self.Protein.proposalid == g.blsession.get("proposalid")) 

971 .filter(self.Protein.name == component_name) 

972 .first() 

973 ) 

974 

975 if not component: 

976 component = self.Protein( 

977 name=component_name, 

978 acronym=component_acronym, 

979 proposalid=g.blsession.get("proposalid"), 

980 ) 

981 

982 ses.add(component) 

983 ses.commit() 

984 

985 return component.proteinid