Skip to content

Commit 1343bff

Browse files
ndg63276Mark Williams
andauthored
LIMS-1550: Add 'Mark Dispensing' button to plate well view (#869)
* LIMS-1550: Add 'Mark Dispensing' button to plate well view * LIMS-1550: Dont throw an error if no subsamples exist * LIMS-1550: Use new BLSamplePosition table * LIMS-1550: Label positions as 'dispensing' * LIMS-1550: Change plus signs to crosses when the button text changes --------- Co-authored-by: Mark Williams <mark.williams@diamond.ac.uk>
1 parent 90d96d1 commit 1343bff

File tree

6 files changed

+149
-7
lines changed

6 files changed

+149
-7
lines changed

api/src/Database/Type/MySQL.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class MySQL extends DatabaseParent {
7575
'DewarReport',
7676
'CourierTermsAccepted',
7777

78+
'BLSamplePosition',
7879
'BLSubSample',
7980
'PDB',
8081
'Protein_has_PDB',

api/src/Page/Sample.php

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ class Sample extends Page
9393
'scid' => '\d+-\d+',
9494

9595
'BLSAMPLEID' => '\d+',
96-
'X' => '\d+(.\d+)?',
97-
'Y' => '\d+(.\d+)?',
96+
'X' => '\d*(.\d+)?',
97+
'Y' => '\d*(.\d+)?',
9898
'Z' => '\d+(.\d+)?',
9999
'X2' => '\d+(.\d+)?',
100100
'Y2' => '\d+(.\d+)?',
@@ -799,6 +799,9 @@ private function get_sub_samples_query($where, $first_inner_select_where = '', $
799799
po2.posx as x2,
800800
po2.posy as y2,
801801
po2.posz as z2,
802+
bsp.posx as dispensex,
803+
bsp.posy as dispensey,
804+
bsp.posz as dispensez,
802805
IF(cqs.containerqueuesampleid IS NOT NULL AND cqs.containerqueueid IS NULL, 1, 0) as readyforqueue,
803806
cq.containerqueueid,
804807
count(distinct IF(dc.overlap != 0,
@@ -824,7 +827,7 @@ private function get_sub_samples_query($where, $first_inner_select_where = '', $
824827
INNER JOIN shipping sh ON sh.shippingid = d.shippingid
825828
INNER JOIN proposal p ON p.proposalid = sh.proposalid
826829
827-
830+
LEFT OUTER JOIN blsampleposition bsp ON bsp.blsampleid = s.blsampleid
828831
LEFT OUTER JOIN containerqueuesample cqs ON cqs.blsubsampleid = ss.blsubsampleid
829832
LEFT OUTER JOIN containerqueue cq ON cqs.containerqueueid = cq.containerqueueid AND cq.completedtimestamp IS NULL
830833
@@ -1903,11 +1906,12 @@ function _update_sample()
19031906
if (!$this->has_arg('sid'))
19041907
$this->_error('No sampleid specified');
19051908

1906-
$samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid
1909+
$samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid,bsp.blsamplepositionid
19071910
FROM blsample b
19081911
INNER JOIN crystal cr ON cr.crystalid = b.crystalid
19091912
INNER JOIN protein pr ON pr.proteinid = cr.proteinid
19101913
LEFT OUTER JOIN diffractionplan dp on dp.diffractionplanid = b.diffractionplanid
1914+
LEFT OUTER JOIN blsampleposition bsp ON bsp.blsampleid = b.blsampleid AND bsp.positiontype='dispensing'
19111915
WHERE pr.proposalid = :1 AND b.blsampleid = :2", array($this->proposalid, $this->arg('sid')));
19121916

19131917
if (!sizeof($samp))
@@ -1980,6 +1984,34 @@ function _update_sample()
19801984
}
19811985
}
19821986
}
1987+
1988+
if ($this->has_arg('X') && $this->has_arg('Y')) {
1989+
$z = $this->has_arg('Z') ? $this->arg('Z') : null;
1990+
$pid = $samp['BLSAMPLEPOSITIONID'];
1991+
if ($this->arg('X') == '' && $this->arg('Y') == '') {
1992+
if (!empty($pid)) {
1993+
$this->db->pq(
1994+
"UPDATE blsampleposition SET posx=null, posy=null, posz=null, recordtimestamp=CURRENT_TIMESTAMP WHERE blsamplepositionid=:1",
1995+
array($pid)
1996+
);
1997+
}
1998+
} else {
1999+
if (empty($pid)) {
2000+
$this->db->pq(
2001+
"INSERT INTO blsampleposition (blsampleid, posx, posy, posz, positiontype, recordtimestamp)
2002+
VALUES (:1, :2, :3, :4, 'dispensing', CURRENT_TIMESTAMP) RETURNING blsamplepositionid INTO :id",
2003+
array($this->arg('sid'), $this->arg('X'), $this->arg('Y'), $z)
2004+
);
2005+
$pid = $this->db->id();
2006+
} else {
2007+
$this->db->pq(
2008+
"UPDATE blsampleposition SET posx=:1, posy=:2, posz=:3, recordtimestamp=CURRENT_TIMESTAMP WHERE blsamplepositionid=:4",
2009+
array($this->arg('X'), $this->arg('Y'), $z, $pid)
2010+
);
2011+
}
2012+
}
2013+
$this->_output(array('BLSAMPLEPOSITIONID' => $pid));
2014+
}
19832015
}
19842016

19852017

client/src/js/models/sample.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,17 @@ define(['backbone', 'collections/components',
1818
this.listenTo(this, 'change:RADIATIONSENSITIVITY', this.updateRadSen)
1919
this.updateRadSen()
2020

21+
this.listenTo(this, 'change:X', this.updatePosition)
22+
2123
this.listenTo(this, 'change', this.updateHasData)
2224
this.updateHasData()
2325

2426
},
2527

28+
updatePosition: function() {
29+
this.save(this.changedAttributes(), { patch: true })
30+
},
31+
2632
updateHasData: function() {
2733
var hasData = this.get('DC') > 0 || this.get('GR') > 0 || this.get('SC') > 0
2834
if (hasData !== this.get('HASDATA')) this.set('HASDATA', hasData)
@@ -98,7 +104,10 @@ define(['backbone', 'collections/components',
98104
VOLUME: '',
99105
INITIALSAMPLEGROUP: '',
100106
COMPONENTIDS: [],
101-
COMPONENTAMOUNTS: []
107+
COMPONENTAMOUNTS: [],
108+
X: null,
109+
Y: null,
110+
Z: null,
102111
},
103112

104113
validation: {
@@ -225,6 +234,15 @@ define(['backbone', 'collections/components',
225234
required: false,
226235
pattern: 'word'
227236
},
237+
X: {
238+
required: false
239+
},
240+
Y: {
241+
required: false
242+
},
243+
Z: {
244+
required: false
245+
},
228246

229247
COMPONENTAMOUNTS: function(from_ui, attr, all_values) {
230248
var values = all_values.components.pluck('ABUNDANCE')

client/src/js/modules/imaging/views/imageviewer.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ define(['marionette',
22
'backbone',
33
'modules/imaging/collections/inspectionimagescores',
44

5+
'models/sample',
56
'models/subsample',
67
'collections/subsamples',
78

@@ -18,7 +19,7 @@ define(['marionette',
1819
'backbone-validation',
1920
], function(Marionette,
2021
Backbone,
21-
ImageScores, Subsample, Subsamples, ImageHistory, InspectionImage, Attachments,
22+
ImageScores, Sample, Subsample, Subsamples, ImageHistory, InspectionImage, Attachments,
2223
Editable, utils, XHRImage, HeatMap,
2324
template) {
2425

@@ -175,6 +176,10 @@ define(['marionette',
175176
},
176177
})
177178
}
179+
180+
if (this.add_dispensing) {
181+
this.editDispensing(x, y)
182+
}
178183
},
179184

180185
setAddSubsample: function(state) {
@@ -184,7 +189,21 @@ define(['marionette',
184189
setAddSubsampleRegion: function(state) {
185190
this.add_region = state
186191
},
187-
192+
193+
setAddDispensing: function(state) {
194+
this.add_dispensing = state
195+
},
196+
197+
deleteDispensing: function() {
198+
this.editDispensing('', '')
199+
},
200+
201+
editDispensing: function(x, y) {
202+
var s = new Sample({ BLSAMPLEID: this.model.get('BLSAMPLEID') })
203+
s.set({ X: x, Y: y })
204+
this.trigger('finishdispensing')
205+
this.subsamples.fetch()
206+
},
188207

189208
remSubsample: function() {
190209
this.draw()
@@ -204,6 +223,7 @@ define(['marionette',
204223
initialize: function(options) {
205224
this.add_object = false
206225
this.add_region = false
226+
this.add_dispensing = false
207227

208228
this.plotObjects = _.debounce(this.plotObjects, 200)
209229
this.drawDebounce = _.debounce(this.draw, 10)
@@ -1006,6 +1026,18 @@ define(['marionette',
10061026
this.ctx.fillStyle = options.o.get('isSelected') ? 'turquoise' : options.o.get('SOURCE') === 'auto' ? 'darkblue' : 'red'
10071027
this.ctx.font = parseInt(14*m)+'px Arial'
10081028
this.ctx.fillText(parseInt(options.o.get('RID'))+1,x-(m*15), y-(m*6))
1029+
1030+
if (options.o.get('DISPENSEX') && options.o.get('DISPENSEY')) {
1031+
var disx = parseInt(options.o.get('DISPENSEX'))
1032+
var disy = parseInt(options.o.get('DISPENSEY'))
1033+
this.ctx.strokeStyle = 'white'
1034+
this.ctx.beginPath()
1035+
this.ctx.arc(disx, disy, 50, 0, 2*Math.PI)
1036+
this.ctx.stroke()
1037+
this.ctx.fillStyle = 'white'
1038+
this.ctx.fillText('D',disx-5*m, disy+5*m)
1039+
this.ctx.closePath()
1040+
}
10091041
},
10101042

10111043
drawBeam: function(o) {

client/src/js/modules/shipment/views/containerplate.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ define(['marionette',
242242
add: '.add_image',
243243
ads: 'a.add_point',
244244
adr: 'a.add_region',
245+
addis: 'a.add_dispensing',
246+
deldis: 'a.del_dispensing',
245247

246248
drop: '.dropimage',
247249
prog: '.progress',
@@ -275,6 +277,8 @@ define(['marionette',
275277
'change @ui.ins': 'selectInspection',
276278
'click @ui.ads': 'setAddSubsamplePoint',
277279
'click @ui.adr': 'setAddSubsampleRegion',
280+
'click @ui.addis': 'setAddDispensing',
281+
'click @ui.deldis': 'deleteDispensing',
278282
'click a.add_inspection': 'showAddInspection',
279283
'click a.view_sched': 'showViewSchedule',
280284
'click @ui.play': 'playInspection',
@@ -483,16 +487,24 @@ define(['marionette',
483487
this.ui.ads.removeClass('button-highlight')
484488
this.image.setAddSubsample(false)
485489
this.ui.ads.find('span').text('Mark Point')
490+
this.ui.ads.find('i').removeClass('fa-times').addClass('fa-plus')
486491

487492
} else {
488493
this.ui.ads.addClass('button-highlight')
489494
this.image.setAddSubsample(true)
490495
this.ui.ads.find('span').text('Finish')
496+
this.ui.ads.find('i').removeClass('fa-plus').addClass('fa-times')
491497
}
492498

493499
this.ui.adr.removeClass('button-highlight')
500+
this.ui.addis.removeClass('button-highlight')
494501
this.image.setAddSubsampleRegion(false)
502+
this.image.setAddDispensing(false)
495503
this.ui.adr.find('span').text('Mark Region')
504+
this.ui.adr.find('i').removeClass('fa-times').addClass('fa-plus')
505+
this.ui.addis.find('span').text('Mark Dispensing')
506+
this.ui.addis.find('i').removeClass('fa-times').addClass('fa-plus')
507+
this.ui.deldis.hide()
496508
},
497509

498510

@@ -503,16 +515,59 @@ define(['marionette',
503515
this.ui.adr.removeClass('button-highlight')
504516
this.image.setAddSubsampleRegion(false)
505517
this.ui.adr.find('span').text('Mark Region')
518+
this.ui.adr.find('i').removeClass('fa-times').addClass('fa-plus')
506519

507520
} else {
508521
this.ui.adr.addClass('button-highlight')
509522
this.image.setAddSubsampleRegion(true)
510523
this.ui.adr.find('span').text('Finish')
524+
this.ui.adr.find('i').removeClass('fa-plus').addClass('fa-times')
511525
}
512526

513527
this.ui.ads.removeClass('button-highlight')
528+
this.ui.addis.removeClass('button-highlight')
514529
this.image.setAddSubsample(false)
530+
this.image.setAddDispensing(false)
515531
this.ui.ads.find('span').text('Mark Point')
532+
this.ui.ads.find('i').removeClass('fa-times').addClass('fa-plus')
533+
this.ui.addis.find('span').text('Mark Dispensing')
534+
this.ui.addis.find('i').removeClass('fa-times').addClass('fa-plus')
535+
this.ui.deldis.hide()
536+
},
537+
538+
setAddDispensing: function(e) {
539+
if (e) e.preventDefault()
540+
541+
if (this.ui.addis.hasClass('button-highlight')) {
542+
this.ui.addis.removeClass('button-highlight')
543+
this.image.setAddDispensing(false)
544+
this.ui.addis.find('span').text('Mark Dispensing')
545+
this.ui.addis.find('i').removeClass('fa-times').addClass('fa-plus')
546+
this.ui.deldis.hide()
547+
548+
} else {
549+
this.ui.addis.addClass('button-highlight')
550+
this.image.setAddDispensing(true)
551+
this.ui.addis.find('span').text('Cancel')
552+
this.ui.addis.find('i').removeClass('fa-plus').addClass('fa-times')
553+
if (this.subsamples.length && this.subsamples.findWhere({ BLSAMPLEID: this.getSample() }).get('DISPENSEX')) {
554+
this.ui.deldis.show()
555+
}
556+
}
557+
558+
this.ui.ads.removeClass('button-highlight')
559+
this.ui.adr.removeClass('button-highlight')
560+
this.image.setAddSubsample(false)
561+
this.image.setAddSubsampleRegion(false)
562+
this.ui.ads.find('span').text('Mark Point')
563+
this.ui.ads.find('i').removeClass('fa-times').addClass('fa-plus')
564+
this.ui.adr.find('span').text('Mark Region')
565+
this.ui.adr.find('i').removeClass('fa-times').addClass('fa-plus')
566+
},
567+
568+
deleteDispensing: function(e) {
569+
e.preventDefault()
570+
this.image.deleteDispensing()
516571
},
517572

518573

@@ -905,12 +960,14 @@ define(['marionette',
905960
this.listenTo(this.image, 'image:prev', this.prevImage, this)
906961
this.listenTo(this.image, 'image:first', this.firstImage, this)
907962
this.listenTo(this.image, 'image:last', this.lastImage, this)
963+
this.listenTo(this.image, 'finishdispensing', this.setAddDispensing, this)
908964

909965
if (this.getOption('params').iid) this.ui.ins.val(this.getOption('params').iid)
910966
this.selectInspection()
911967

912968
this.ui.prog.hide()
913969
this.ui.prog.progressbar({ value: 0 })
970+
this.ui.deldis.hide()
914971

915972
this.img.show(this.image)
916973
this.sten.show(new ImageHistoryView({ historyimages: this.startendimages, embed: true }))

client/src/js/templates/shipment/containerplateimage.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ <h2>Marked Sub Samples</h2>
8989
<div class="ra">
9090
<a href="#" class="button add_point"><i class="fa fa-plus"></i> <span>Mark Point</span></a>
9191
<a href="#" class="button add_region"><i class="fa fa-plus"></i> <span>Mark Region</span></a>
92+
<a href="#" class="button add_dispensing"><i class="fa fa-plus"></i> <span>Mark Dispensing</span></a>
93+
<a href="#" class="button del_dispensing"><i class="fa fa-trash"></i> <span>Delete</span></a>
9294
</div>
9395
<div class="subs"></div>
9496
</div>

0 commit comments

Comments
 (0)