From 0b7e49b0f4777b245e3c7d6c62094b881aed3b17 Mon Sep 17 00:00:00 2001 From: Mark Williams Date: Fri, 29 Nov 2024 10:19:49 +0000 Subject: [PATCH 1/2] LIMS-1550: Add 'Mark Dispensing' button to plate well view --- api/src/Page/Sample.php | 40 ++++++++++++++-- client/src/js/models/sample.js | 20 +++++++- .../js/modules/imaging/views/imageviewer.js | 36 +++++++++++++- .../modules/shipment/views/containerplate.js | 47 ++++++++++++++++++- .../shipment/containerplateimage.html | 2 + 5 files changed, 137 insertions(+), 8 deletions(-) diff --git a/api/src/Page/Sample.php b/api/src/Page/Sample.php index d1a051493..21eec3164 100644 --- a/api/src/Page/Sample.php +++ b/api/src/Page/Sample.php @@ -93,8 +93,8 @@ class Sample extends Page 'scid' => '\d+-\d+', 'BLSAMPLEID' => '\d+', - 'X' => '\d+(.\d+)?', - 'Y' => '\d+(.\d+)?', + 'X' => '\d*(.\d+)?', + 'Y' => '\d*(.\d+)?', 'Z' => '\d+(.\d+)?', 'X2' => '\d+(.\d+)?', 'Y2' => '\d+(.\d+)?', @@ -792,6 +792,9 @@ private function get_sub_samples_query($where, $first_inner_select_where = '', $ po2.posx as x2, po2.posy as y2, po2.posz as z2, + po3.posx as dispensex, + po3.posy as dispensey, + po3.posz as dispensez, IF(cqs.containerqueuesampleid IS NOT NULL AND cqs.containerqueueid IS NULL, 1, 0) as readyforqueue, cq.containerqueueid, count(distinct IF(dc.overlap != 0, @@ -817,7 +820,7 @@ private function get_sub_samples_query($where, $first_inner_select_where = '', $ INNER JOIN shipping sh ON sh.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = sh.proposalid - + LEFT OUTER JOIN position po3 ON po3.positionid = s.positionid LEFT OUTER JOIN containerqueuesample cqs ON cqs.blsubsampleid = ss.blsubsampleid LEFT OUTER JOIN containerqueue cq ON cqs.containerqueueid = cq.containerqueueid AND cq.completedtimestamp IS NULL @@ -1895,7 +1898,7 @@ function _update_sample() if (!$this->has_arg('sid')) $this->_error('No sampleid specified'); - $samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid + $samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid,b.positionid FROM blsample b INNER JOIN crystal cr ON cr.crystalid = b.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid @@ -1972,6 +1975,35 @@ function _update_sample() } } } + + if ($this->has_arg('X') && $this->has_arg('Y')) { + $z = $this->has_arg('Z') ? $this->arg('Z') : null; + $pid = $samp['POSITIONID']; + if ($this->arg('X') == '' && $this->arg('Y') == '') { + if (!empty($pid)) { + $this->db->pq( + "UPDATE position SET posx=null, posy=null, posz=null, recordtimestamp=CURRENT_TIMESTAMP WHERE positionid=:1", + array($pid) + ); + } + } else { + if (empty($pid)) { + $this->db->pq( + "INSERT INTO position (positionid, posx, posy, posz, recordtimestamp) + VALUES (s_position.nextval, :1, :2, :3, CURRENT_TIMESTAMP) RETURNING positionid INTO :id", + array($this->arg('X'), $this->arg('Y'), $z) + ); + $pid = $this->db->id(); + $this->db->pq("UPDATE blsample SET positionid=:1 WHERE blsampleid=:2", array($pid, $samp['BLSAMPLEID'])); + } else { + $this->db->pq( + "UPDATE position SET posx=:1, posy=:2, posz=:3, recordtimestamp=CURRENT_TIMESTAMP WHERE positionid=:4", + array($this->arg('X'), $this->arg('Y'), $z, $pid) + ); + } + } + $this->_output(array('POSITIONID' => $pid)); + } } diff --git a/client/src/js/models/sample.js b/client/src/js/models/sample.js index e0c5f1309..e0267e61c 100644 --- a/client/src/js/models/sample.js +++ b/client/src/js/models/sample.js @@ -18,11 +18,17 @@ define(['backbone', 'collections/components', this.listenTo(this, 'change:RADIATIONSENSITIVITY', this.updateRadSen) this.updateRadSen() + this.listenTo(this, 'change:X', this.updatePosition) + this.listenTo(this, 'change', this.updateHasData) this.updateHasData() }, + updatePosition: function() { + this.save(this.changedAttributes(), { patch: true }) + }, + updateHasData: function() { var hasData = this.get('DC') > 0 || this.get('GR') > 0 || this.get('SC') > 0 if (hasData !== this.get('HASDATA')) this.set('HASDATA', hasData) @@ -98,7 +104,10 @@ define(['backbone', 'collections/components', VOLUME: '', INITIALSAMPLEGROUP: '', COMPONENTIDS: [], - COMPONENTAMOUNTS: [] + COMPONENTAMOUNTS: [], + X: null, + Y: null, + Z: null, }, validation: { @@ -225,6 +234,15 @@ define(['backbone', 'collections/components', required: false, pattern: 'word' }, + X: { + required: false + }, + Y: { + required: false + }, + Z: { + required: false + }, COMPONENTAMOUNTS: function(from_ui, attr, all_values) { var values = all_values.components.pluck('ABUNDANCE') diff --git a/client/src/js/modules/imaging/views/imageviewer.js b/client/src/js/modules/imaging/views/imageviewer.js index 6751db344..c2cd88a43 100644 --- a/client/src/js/modules/imaging/views/imageviewer.js +++ b/client/src/js/modules/imaging/views/imageviewer.js @@ -2,6 +2,7 @@ define(['marionette', 'backbone', 'modules/imaging/collections/inspectionimagescores', + 'models/sample', 'models/subsample', 'collections/subsamples', @@ -18,7 +19,7 @@ define(['marionette', 'backbone-validation', ], function(Marionette, Backbone, - ImageScores, Subsample, Subsamples, ImageHistory, InspectionImage, Attachments, + ImageScores, Sample, Subsample, Subsamples, ImageHistory, InspectionImage, Attachments, Editable, utils, XHRImage, HeatMap, template) { @@ -175,6 +176,10 @@ define(['marionette', }, }) } + + if (this.add_dispensing) { + this.editDispensing(x, y) + } }, setAddSubsample: function(state) { @@ -184,7 +189,21 @@ define(['marionette', setAddSubsampleRegion: function(state) { this.add_region = state }, - + + setAddDispensing: function(state) { + this.add_dispensing = state + }, + + deleteDispensing: function() { + this.editDispensing('', '') + }, + + editDispensing: function(x, y) { + var s = new Sample({ BLSAMPLEID: this.model.get('BLSAMPLEID') }) + s.set({ X: x, Y: y }) + this.trigger('finishdispensing') + this.subsamples.fetch() + }, remSubsample: function() { this.draw() @@ -204,6 +223,7 @@ define(['marionette', initialize: function(options) { this.add_object = false this.add_region = false + this.add_dispensing = false this.plotObjects = _.debounce(this.plotObjects, 200) this.drawDebounce = _.debounce(this.draw, 10) @@ -1004,6 +1024,18 @@ define(['marionette', this.ctx.fillStyle = options.o.get('isSelected') ? 'turquoise' : options.o.get('SOURCE') === 'auto' ? 'darkblue' : 'red' this.ctx.font = parseInt(14*m)+'px Arial' this.ctx.fillText(parseInt(options.o.get('RID'))+1,x-(m*15), y-(m*6)) + + if (options.o.get('DISPENSEX') && options.o.get('DISPENSEY')) { + var disx = parseInt(options.o.get('DISPENSEX')) + var disy = parseInt(options.o.get('DISPENSEY')) + this.ctx.strokeStyle = 'white' + this.ctx.beginPath() + this.ctx.arc(disx, disy, 50, 0, 2*Math.PI) + this.ctx.stroke() + this.ctx.fillStyle = 'white' + this.ctx.fillText('D',disx-5*m, disy+5*m) + this.ctx.closePath() + } }, drawBeam: function(o) { diff --git a/client/src/js/modules/shipment/views/containerplate.js b/client/src/js/modules/shipment/views/containerplate.js index aa5fd157f..456a26428 100644 --- a/client/src/js/modules/shipment/views/containerplate.js +++ b/client/src/js/modules/shipment/views/containerplate.js @@ -243,6 +243,8 @@ define(['marionette', add: '.add_image', ads: 'a.add_point', adr: 'a.add_region', + addis: 'a.add_dispensing', + deldis: 'a.del_dispensing', drop: '.dropimage', prog: '.progress', @@ -272,6 +274,8 @@ define(['marionette', 'change @ui.ins': 'selectInspection', 'click @ui.ads': 'setAddSubsamplePoint', 'click @ui.adr': 'setAddSubsampleRegion', + 'click @ui.addis': 'setAddDispensing', + 'click @ui.deldis': 'deleteDispensing', 'click a.add_inspection': 'showAddInspection', 'click a.view_sched': 'showViewSchedule', 'click @ui.play': 'playInspection', @@ -488,8 +492,12 @@ define(['marionette', } this.ui.adr.removeClass('button-highlight') + this.ui.addis.removeClass('button-highlight') this.image.setAddSubsampleRegion(false) + this.image.setAddDispensing(false) this.ui.adr.find('span').text('Mark Region') + this.ui.addis.find('span').text('Mark Dispensing') + this.ui.deldis.hide() }, @@ -508,8 +516,43 @@ define(['marionette', } this.ui.ads.removeClass('button-highlight') + this.ui.addis.removeClass('button-highlight') this.image.setAddSubsample(false) + this.image.setAddDispensing(false) this.ui.ads.find('span').text('Mark Point') + this.ui.addis.find('span').text('Mark Dispensing') + this.ui.deldis.hide() + }, + + setAddDispensing: function(e) { + if (e) e.preventDefault() + + if (this.ui.addis.hasClass('button-highlight')) { + this.ui.addis.removeClass('button-highlight') + this.image.setAddDispensing(false) + this.ui.addis.find('span').text('Mark Dispensing') + this.ui.deldis.hide() + + } else { + this.ui.addis.addClass('button-highlight') + this.image.setAddDispensing(true) + this.ui.addis.find('span').text('Cancel') + if (this.subsamples.findWhere({ BLSAMPLEID: this.getSample() }).get('DISPENSEX')) { + this.ui.deldis.show() + } + } + + this.ui.ads.removeClass('button-highlight') + this.ui.adr.removeClass('button-highlight') + this.image.setAddSubsample(false) + this.image.setAddSubsampleRegion(false) + this.ui.ads.find('span').text('Mark Point') + this.ui.adr.find('span').text('Mark Region') + }, + + deleteDispensing: function(e) { + e.preventDefault() + this.image.deleteDispensing() }, @@ -539,7 +582,7 @@ define(['marionette', var i = this.inspectionimages.at(n) if (this.caching && i) { var xhr = new XHRImage() - console.log('caching', i.urlFor('hd')) + //console.log('caching', i.urlFor('hd')) xhr.load(i.urlFor('full'), function() { self.plateView.drawPlate() @@ -890,12 +933,14 @@ define(['marionette', this.listenTo(this.image, 'image:prev', this.prevImage, this) this.listenTo(this.image, 'image:first', this.firstImage, this) this.listenTo(this.image, 'image:last', this.lastImage, this) + this.listenTo(this.image, 'finishdispensing', this.setAddDispensing, this) if (this.getOption('params').iid) this.ui.ins.val(this.getOption('params').iid) this.selectInspection() this.ui.prog.hide() this.ui.prog.progressbar({ value: 0 }) + this.ui.deldis.hide() this.img.show(this.image) this.sten.show(new ImageHistoryView({ historyimages: this.startendimages, embed: true })) diff --git a/client/src/js/templates/shipment/containerplateimage.html b/client/src/js/templates/shipment/containerplateimage.html index 028a8988d..ebe31534a 100644 --- a/client/src/js/templates/shipment/containerplateimage.html +++ b/client/src/js/templates/shipment/containerplateimage.html @@ -91,6 +91,8 @@

Marked Sub Samples

Mark Point Mark Region + Mark Dispensing + Delete
From 5dafe3b933569c64d4f38645c9bb78cd97ad495c Mon Sep 17 00:00:00 2001 From: Mark Williams Date: Fri, 29 Nov 2024 10:46:20 +0000 Subject: [PATCH 2/2] LIMS-1550: Dont throw an error if no subsamples exist --- client/src/js/modules/shipment/views/containerplate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/js/modules/shipment/views/containerplate.js b/client/src/js/modules/shipment/views/containerplate.js index 456a26428..6697095f5 100644 --- a/client/src/js/modules/shipment/views/containerplate.js +++ b/client/src/js/modules/shipment/views/containerplate.js @@ -537,7 +537,7 @@ define(['marionette', this.ui.addis.addClass('button-highlight') this.image.setAddDispensing(true) this.ui.addis.find('span').text('Cancel') - if (this.subsamples.findWhere({ BLSAMPLEID: this.getSample() }).get('DISPENSEX')) { + if (this.subsamples.length && this.subsamples.findWhere({ BLSAMPLEID: this.getSample() }).get('DISPENSEX')) { this.ui.deldis.show() } }