Skip to content

Commit

Permalink
LIMS-1099: Limit what can be done with red proteins (#827)
Browse files Browse the repository at this point in the history
* LIMS-1099: Limit what can be done with red proteins

* LIMS-1099: Revert shipment safety level if new value is not valid

---------

Co-authored-by: Mark Williams <[email protected]>
  • Loading branch information
ndg63276 and Mark Williams authored Sep 17, 2024
1 parent 9a20f4c commit f58aef5
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 52 deletions.
17 changes: 11 additions & 6 deletions api/src/Page/Sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -1819,12 +1819,17 @@ function _distinct_proteins()
$where .= ' AND pr.externalid IS NOT NULL';
}

$has_safety_level = $this->has_arg('SAFETYLEVEL');
if ($has_safety_level && $this->arg('SAFETYLEVEL') === 'ALL') {
$where .= ' AND pr.safetyLevel IS NOT NULL';
} else if ($has_safety_level && $this->arg('SAFETYLEVEL') !== 'ALL') {
$where .= ' AND pr.safetyLevel=:' . (sizeof($args) + 1);
array_push($args, $this->arg('SAFETYLEVEL'));
if ($this->has_arg('SAFETYLEVEL')) {
if ($this->arg('SAFETYLEVEL') === 'ALL') {
$where .= ' AND pr.safetyLevel IS NOT NULL';
} else if (strtolower($this->arg('SAFETYLEVEL')) === 'yellow') {
$where .= " AND pr.safetyLevel in ('green','yellow')";
} else if (strtolower($this->arg('SAFETYLEVEL')) === 'red') {
$where .= " AND pr.safetyLevel in ('green','yellow','red')";
} else {
$where .= ' AND pr.safetyLevel=:' . (sizeof($args) + 1);
array_push($args, $this->arg('SAFETYLEVEL'));
}
}

if ($this->has_arg('term')) {
Expand Down
39 changes: 38 additions & 1 deletion api/src/Page/Shipment.php
Original file line number Diff line number Diff line change
Expand Up @@ -1626,7 +1626,7 @@ function _get_dewars()
$order = $cols[$this->arg('sort_by')] . ' ' . $dir;
}

$dewars = $this->db->paginate("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number) as firstexperiment, r.labcontactid, se.beamlineoperator as localcontact, se.beamlinename, TO_CHAR(se.startdate, 'HH24:MI DD-MM-YYYY') as firstexperimentst, d.firstexperimentid, s.shippingid, s.shippingname, d.facilitycode, count(c.containerid) as ccount, (case when se.visit_number > 0 then (CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number)) else '' end) as exp, d.code, d.barcode, d.storagelocation, d.dewarstatus, d.dewarid, d.trackingnumbertosynchrotron, d.trackingnumberfromsynchrotron, d.externalShippingIdFromSynchrotron, s.deliveryagent_agentname, d.weight, d.deliveryagent_barcode, GROUP_CONCAT(c.code SEPARATOR ', ') as containers, s.sendinglabcontactid, s.returnlabcontactid, pe.givenname, pe.familyname
$dewars = $this->db->paginate("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number) as firstexperiment, r.labcontactid, se.beamlineoperator as localcontact, se.beamlinename, TO_CHAR(se.startdate, 'HH24:MI DD-MM-YYYY') as firstexperimentst, d.firstexperimentid, s.shippingid, s.shippingname, d.facilitycode, count(c.containerid) as ccount, (case when se.visit_number > 0 then (CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number)) else '' end) as exp, d.code, d.barcode, d.storagelocation, d.dewarstatus, d.dewarid, d.trackingnumbertosynchrotron, d.trackingnumberfromsynchrotron, d.externalShippingIdFromSynchrotron, s.deliveryagent_agentname, d.weight, d.deliveryagent_barcode, GROUP_CONCAT(c.code SEPARATOR ', ') as containers, s.sendinglabcontactid, s.returnlabcontactid, pe.givenname, pe.familyname, s.safetylevel as shippingsafetylevel
FROM dewar d
LEFT OUTER JOIN container c ON c.dewarid = d.dewarid
INNER JOIN shipping s ON d.shippingid = s.shippingid
Expand Down Expand Up @@ -1700,6 +1700,39 @@ function _add_dewar()
$this->_output(array('DEWARID' => $id));
}

# Check new safety level is ok
function _check_safety_level()
{
$ship = $this->db->pq("SELECT cq.containerqueueid, p.safetylevel FROM dewar d
LEFT OUTER JOIN container c on c.dewarid = d.dewarid
LEFT OUTER JOIN containerqueue cq on cq.containerid = c.containerid
LEFT OUTER JOIN blsample b ON b.containerid = c.containerid
LEFT OUTER JOIN crystal cr ON cr.crystalid = b.crystalid
LEFT OUTER JOIN protein p ON p.proteinid = cr.proteinid
WHERE d.shippingid = :1", array($this->arg('sid')));
if (strtolower($this->arg('SAFETYLEVEL')) == 'green') {
foreach ($ship as $s) {
if (strtolower($s['SAFETYLEVEL']) == 'yellow')
$this->_error('Cannot set safety level to green as one or more samples are yellow.');
if (strtolower($s['SAFETYLEVEL']) == 'red')
$this->_error('Cannot set safety level to green as one or more samples are red.');
}
} else if (strtolower($this->arg('SAFETYLEVEL')) == 'yellow') {
foreach ($ship as $s) {
if ($s['CONTAINERQUEUEID'] != null)
$this->_error('Cannot set safety level to yellow as one or more containers are queued.');
if (strtolower($s['SAFETYLEVEL']) == 'red')
$this->_error('Cannot set safety level to yellow as one or more samples are red.');
}
} else {
foreach ($ship as $s) {
if ($s['CONTAINERQUEUEID'] != null)
$this->_error('Cannot set safety level to red as one or more containers are queued.');
}
}
}


# Update shipment
function _update_shipment()
{
Expand All @@ -1721,6 +1754,9 @@ function _update_shipment()
if (in_array($f, array('DELIVERYAGENT_DELIVERYDATE', 'DELIVERYAGENT_SHIPPINGDATE'))) {
$fl = "TO_DATE(:1, 'DD-MM-YYYY')";
}
if ($f == 'SAFETYLEVEL') {
$this->_check_safety_level();
}

$this->db->pq("UPDATE shipping SET $f=$fl WHERE shippingid=:2", array($this->arg($f), $this->arg('sid')));

Expand Down Expand Up @@ -2120,6 +2156,7 @@ function _get_all_containers()
}
// $this->db->set_debug(True);
$rows = $this->db->paginate("SELECT round(TIMESTAMPDIFF('HOUR', min(ci.bltimestamp), CURRENT_TIMESTAMP)/24,1) as age, case when count(ci2.containerinspectionid) > 1 then 0 else 1 end as allow_adhoc, c.scheduleid, c.screenid, sc.name as screen, c.imagerid, i.temperature as temperature, i.name as imager, TO_CHAR(max(ci.bltimestamp), 'HH24:MI DD-MM-YYYY') as lastinspection, round(TIMESTAMPDIFF('HOUR', max(ci.bltimestamp), CURRENT_TIMESTAMP)/24,1) as lastinspectiondays, count(distinct ci.containerinspectionid) as inspections, CONCAT(p.proposalcode, p.proposalnumber) as prop, c.bltimestamp, c.samplechangerlocation, c.beamlinelocation, d.dewarstatus, c.containertype, c.capacity, c.containerstatus, c.containerid, c.code as name, d.code as dewar, sh.shippingname as shipment, d.dewarid, sh.shippingid, count(distinct s.blsampleid) as samples, cq.containerqueueid, TO_CHAR(cq.createdtimestamp, 'DD-MM-YYYY HH24:MI') as queuedtimestamp, CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit, ses.beamlinename, c.requestedreturn, c.requestedimagerid, c.comments, c.experimenttype, c.storagetemperature, c.barcode, reg.barcode as registry, reg.containerregistryid,
sh.safetylevel as shippingsafetylevel,
(SELECT sch.name FROM schedule sch WHERE sch.scheduleid = c.scheduleid) as schedule,
(SELECT i2.name FROM imager i2 WHERE i2.imagerid = c.requestedimagerid) as requestedimager,
(SELECT count(distinct ss.blsubsampleid) FROM blsubsample ss RIGHT OUTER JOIN blsample s2 ON s2.blsampleid = ss.blsampleid WHERE s2.containerid = c.containerid AND ss.source='manual') as subsamples,
Expand Down
5 changes: 5 additions & 0 deletions client/src/js/models/shipment.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ define(['backbone'], function (Backbone) {
pattern: 'fcode',
},

FIRSTEXPERIMENTID: {
required: false,
pattern: 'number',
},

REMOTEORMAILIN: {
required: false,
},
Expand Down
2 changes: 1 addition & 1 deletion client/src/js/modules/shipment/views/shipment.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ define(['marionette',

var edit = new Editable({ model: this.model, el: this.$el })
edit.create('SHIPPINGNAME', 'textlong')
edit.create('SAFETYLEVEL', 'select', { data: {'Green': 'Green', 'Yellow':'Yellow', 'Red': 'Red'} })
edit.create('SAFETYLEVEL', 'select', { data: {'Green': 'Green', 'Yellow':'Yellow', 'Red': 'Red'}, alert: true, revert: true })
edit.create('COMMENTS', 'textarea')
edit.create('DELIVERYAGENT_AGENTNAME', 'text')
edit.create('DELIVERYAGENT_AGENTCODE', 'text')
Expand Down
19 changes: 17 additions & 2 deletions client/src/js/modules/shipment/views/shipmentadd.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ define(['marionette', 'views/form',
'click a.add_lc': 'addLC',
'click @ui.noexp': 'updateFirstExp',
'click @ui.dynamic': 'updateDynamicSchedule',
'change select[name^=SAFETYLEVEL]': 'changeSafetyLevel',
},

ui: {
Expand All @@ -73,6 +74,8 @@ define(['marionette', 'views/form',
noexp: 'input[name=noexp]',
dynamic: 'input[name=DYNAMIC]', // A checkbox to indicate dynamic/remote mail-in scheduling
comments: 'textarea[name=COMMENTS]', // We need this so we can prefill comments to aid users
safetylevel: 'select[name^=SAFETYLEVEL]',
udcresponsive: '.udcresponsive',
},

addLC: function(e) {
Expand All @@ -97,12 +100,24 @@ define(['marionette', 'views/form',
app.alert({ message: 'Something went wrong registering this shipment, please try again'})
},

changeSafetyLevel: function() {
if (this.ui.safetylevel.val() === 'Green') {
this.ui.udcresponsive.show()
} else {
this.ui.noexp.prop('checked', false)
this.updateFirstExp()
this.ui.dynamic.prop('checked', false)
this.updateDynamicSchedule()
this.ui.udcresponsive.hide()
}
},

updateFirstExp: function() {
if (this.ui.noexp.is(':checked')) {
this.ui.first.html('<option value=""> - </option>')
this.ui.dynamic.prop('checked', false)
} else {
this.ui.first.html(this.visits.opts())
this.ui.first.html('<option value="!">Please select one</option>'+this.visits.opts())
}
},

Expand All @@ -121,7 +136,7 @@ define(['marionette', 'views/form',
}
} else {
this.model.validation.REMOTEORMAILIN.required = false
this.ui.first.html(this.visits.opts())
this.ui.first.html('<option value="!">Please select one</option>'+this.visits.opts())
this.$el.find(".remoteform").hide()
if (industrial_visit) {
this.$el.find(".remoteormailin").hide()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export default {
proteinsCollection.queryParams.external = 1
}

proteinsCollection.queryParams.SAFETYLEVEL = 'ALL'
proteinsCollection.queryParams.SAFETYLEVEL = this.shippingSafetyLevel

const result = await this.$store.dispatch('getCollection', proteinsCollection)
this.proteins = result.toJSON()
Expand Down
23 changes: 6 additions & 17 deletions client/src/js/modules/types/mx/shipment/views/mx-container-add.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,13 @@
<div class="tw-mb-2 tw-py-2">
<label>Queue For UDC</label>
<base-input-checkbox
v-if="shippingSafetyLevel === 'Green'"
v-model="QUEUEFORUDC"
name="Queue For UDC"
/>
<span v-else>
Cannot queue containers in {{ shippingSafetyLevel }} shipments
</span>
</div>
</div>

Expand Down Expand Up @@ -461,6 +465,7 @@ export default {
// The dewar that this container will belong to
dewar: null,
shippingSafetyLevel: null,
processingPipeline: '',
processingPipelines: [],
Expand Down Expand Up @@ -542,23 +547,6 @@ export default {
}
}
},
AUTOMATED: {
immediate: true,
handler: function(newVal) {
const proteinsCollection = new DistinctProteins()
// If now on, add safety level to query
// Automated collections limited to GREEN Low risk samples
if (newVal) {
proteinsCollection.queryParams.SAFETYLEVEL = 'GREEN';
} else {
proteinsCollection.queryParams.SAFETYLEVEL = 'ALL';
}
this.$store.dispatch('getCollection', proteinsCollection).then( (result) => {
this.proteins = result.toJSON()
})
app.trigger('samples:automated', newVal)
}
},
CONTAINERREGISTRYID: {
immediate: true,
handler: function(newVal) {
Expand Down Expand Up @@ -593,6 +581,7 @@ export default {
created: function() {
this.containerType = INITIAL_CONTAINER_TYPE
this.dewar = this.options.dewar.toJSON()
this.shippingSafetyLevel = this.dewar.SHIPPINGSAFETYLEVEL
this.DEWARID = this.dewar.DEWARID
this.resetSamples(this.containerType.CAPACITY)
Expand Down
39 changes: 22 additions & 17 deletions client/src/js/modules/types/mx/shipment/views/mx-container-view.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,24 +106,25 @@
@click="onUnQueueContainer"
><i class="fa fa-times" /> Unqueue</a>
</span>
<span v-else-if="shippingSafetyLevel != 'Green'">
Cannot queue containers in {{ shippingSafetyLevel }} shipments
</span>
<span v-else-if="containerQueueError">
There was an error submitting the container to the queue. Please fix any errors in the samples table.
<a
class="tw-cursor-pointer button tryagainqueue"
@click="onTryAgainQueueContainer"
><i class="fa fa-check" /> Try again</a>
<a
class="tw-cursor-pointer button cancelqueue"
@click="onCancelQueueContainer"
><i class="fa fa-times" /> Cancel</a>
</span>
<span v-else>
<span v-if="containerQueueError">
There was an error submitting the container to the queue. Please fix any errors in the samples table.
<a
class="tw-cursor-pointer button tryagainqueue"
@click="onTryAgainQueueContainer"
><i class="fa fa-check" /> Try again</a>
<a
class="tw-cursor-pointer button cancelqueue"
@click="onCancelQueueContainer"
><i class="fa fa-times" /> Cancel</a>
</span>
<span v-else>
<a
class="tw-cursor-pointer button queue"
@click="onQueueContainer"
><i class="fa fa-plus" /> Queue</a> this container for Auto Collect
</span>
<a
class="tw-cursor-pointer button queue"
@click="onQueueContainer"
><i class="fa fa-plus" /> Queue</a> this container for Auto Collect
</span>
</li>

Expand Down Expand Up @@ -324,6 +325,7 @@ export default {
dewarsCollection: null,
selectedDewarId: null,
selectedShipmentId: null,
shippingSafetyLevel: null,
editingSampleLocation: null
}
},
Expand Down Expand Up @@ -354,6 +356,7 @@ export default {
loadContainerData() {
this.container = Object.assign({}, this.containerModel.toJSON())
this.containerId = this.containerModel.get('CONTAINERID')
this.shippingSafetyLevel = this.containerModel.get('SHIPPINGSAFETYLEVEL')
this.containerQueueId = this.containerModel.get('CONTAINERQUEUEID')
if (this.containerQueueId) this.QUEUEFORUDC = true
},
Expand Down Expand Up @@ -477,6 +480,7 @@ export default {
})
this.$nextTick(() => {
this.loadContainerData()
this.getProteins()
// TODO: Toggle Auto in the samples table
})
} catch (error) {
Expand All @@ -497,6 +501,7 @@ export default {
this.$emit('update-container-state', { CONTAINERQUEUEID: null })
this.$nextTick(() => {
this.loadContainerData()
this.getProteins()
// TODO: Toggle Auto in the samples table
})
} catch (error) {
Expand Down
14 changes: 8 additions & 6 deletions client/src/js/templates/shipment/shipmentadd.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ <h1>Add New Shipment</h1>
</label>
<div class="tw-flex">
<select class="tw-h-6 tw-mr-2" name="FIRSTEXPERIMENTID" data-testid="add-shipment-experiment"></select>
<label class="secondary tw-mr-2">
<input type="checkbox" name="noexp" data-testid="add-shipment-automated"> UDC / Automated / Imager
</label>
<label class="secondary tw-mr-2">
<input type="checkbox" name="DYNAMIC" data-testid="add-shipment-dynamic"> Responsive / Mail-in
</label>
<span class="udcresponsive">
<label class="secondary tw-mr-2">
<input type="checkbox" name="noexp" data-testid="add-shipment-automated"> UDC / Automated / Imager
</label>
<label class="secondary tw-mr-2">
<input type="checkbox" name="DYNAMIC" data-testid="add-shipment-dynamic"> Responsive / Mail-in
</label>
</span>
</div>
</li>

Expand Down
9 changes: 8 additions & 1 deletion client/src/js/utils/editable.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,13 @@ define(['marionette',
*/
create: function(attr, type, options, refetch) {
var submit = function(value, settings) {
prevValue = this.model.get(attr)
this.model.set(attr, value)
console.log('valid', this.model.isValid(true), attr, 'changed', this.model.changedAttributes())
var self = this
toReturn = refetch ? '' : _.escape(value)
this.model.save(this.model.changedAttributes(), { patch: true, validate: false,
async: !(options && options.revert),
success: function() {
if (refetch) self.model.fetch()
},
Expand All @@ -177,10 +180,14 @@ define(['marionette',
if (json.message) app.alert({ message: json.message })
else app.alert({ message: 'Something went wrong' })
}
if(options && options.revert) {
self.model.set(attr, prevValue)
toReturn = prevValue
}
}
})

return refetch ? '' : _.escape(value)
return toReturn
}

var onsubmit = function(settings, td) {
Expand Down

0 comments on commit f58aef5

Please sign in to comment.