diff --git a/api/composer.json b/api/composer.json index 8101fed18..8ec82129d 100644 --- a/api/composer.json +++ b/api/composer.json @@ -21,12 +21,13 @@ "mpdf/mpdf": "8.1.2", "ralouphie/getallheaders": "2.0.5", "slim/slim": "2.6.2", - "stomp-php/stomp-php": "3.0.6", + "php-amqplib/php-amqplib": "^2.0", "symfony/http-foundation": "^5.4", "symfony/filesystem": "^5.4", "mpdf/qrcode": "^1.2", "mtcmedia/dhl-api": "dev-master#9b4b6315", - "maennchen/zipstream-php": "2.1.0" + "maennchen/zipstream-php": "2.1.0", + "phpseclib/bcmath_compat": "^2.0" }, "autoload": { "psr-4": { diff --git a/api/src/Page.php b/api/src/Page.php index c9f293507..f098868f9 100644 --- a/api/src/Page.php +++ b/api/src/Page.php @@ -1109,14 +1109,16 @@ function _submit_zocalo_recipe($recipe, $parameters, $error_code = 500) } - function _send_zocalo_message($zocalo_queue, $zocalo_message, $error_code = 500) + function _send_zocalo_message($rabbitmq_zocalo_vhost, $zocalo_message, $error_code = 500) { global - $zocalo_server, - $zocalo_username, - $zocalo_password; + $rabbitmq_zocalo_host, + $rabbitmq_zocalo_port, + $rabbitmq_zocalo_username, + $rabbitmq_zocalo_password, + $rabbitmq_zocalo_routing_key; - if (empty($zocalo_server) || empty($zocalo_queue)) + if (empty($rabbitmq_zocalo_host) || empty($rabbitmq_zocalo_vhost)) { $message = 'Zocalo server or queue not specified.'; error_log($message); @@ -1129,8 +1131,8 @@ function _send_zocalo_message($zocalo_queue, $zocalo_message, $error_code = 500) try { error_log("Sending message" . var_export($zocalo_message, true)); - $queue = new Queue($zocalo_server, $zocalo_username, $zocalo_password); - $queue->send($zocalo_queue, $zocalo_message, true, $this->user->loginId); + $queue = new Queue($rabbitmq_zocalo_host, $rabbitmq_zocalo_port, $rabbitmq_zocalo_username, $rabbitmq_zocalo_password, $rabbitmq_zocalo_vhost); + $queue->send($zocalo_message, $rabbitmq_zocalo_routing_key); } catch (Exception $e) { diff --git a/api/src/Page/DC.php b/api/src/Page/DC.php index 33f259721..c18d0dacd 100644 --- a/api/src/Page/DC.php +++ b/api/src/Page/DC.php @@ -368,13 +368,14 @@ function _data_collections($single = null) $s = str_replace('_', '$_', $this->arg('s')); $st = sizeof($args) + 1; - $where .= " AND (lower(dc.filetemplate) LIKE lower(CONCAT(CONCAT('%',:$st),'%')) ESCAPE '$' OR lower(dc.imagedirectory) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%', :" . ($st + 2) . "), '%')) ESCAPE '$')"; - $where2 .= " AND (lower(es.comments) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 3) . "), '%')) ESCAPE '$' OR lower(es.element) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 4) . "), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 5) . "), '%')) ESCAPE '$')"; + $where .= " AND (dc.filetemplate LIKE CONCAT('%',:$st,'%') ESCAPE '$' OR dc.imagedirectory LIKE CONCAT('%',:" . ($st + 1) . ",'%') ESCAPE '$' OR smp.name LIKE CONCAT('%', :" . ($st + 2) . ",'%') ESCAPE '$')"; + $where2 .= " AND (es.comments LIKE CONCAT('%',:" . ($st + 3) . ",'%') ESCAPE '$' OR es.element LIKE CONCAT('%',:" . ($st + 4) . ",'%') ESCAPE '$' OR smp.name LIKE CONCAT('%',:" . ($st + 5) . ",'%') ESCAPE '$')"; $where3 .= ' AND r.robotactionid < 0'; - $where4 .= " AND (lower(xrf.filename) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 6) . "), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 7) . "), '%')) ESCAPE '$')"; + $where4 .= " AND (xrf.filename LIKE CONCAT('%',:" . ($st + 6) . ",'%') ESCAPE '$' OR smp.name LIKE CONCAT('%',:" . ($st + 7) . ",'%') ESCAPE '$')"; for ($i = 0; $i < 8; $i++) array_push($args, $s); + } # Set Count field @@ -440,7 +441,7 @@ function _data_collections($single = null) dc.c2lens, dc.objaperture, dc.magnification, - dc.totalexposeddose as totaldose, + dose.total as totaldose, CAST(dc.totalabsorbeddose AS DECIMAL(5, 2)) as totalabsdose, d.detectorpixelsizehorizontal, d.detectorpixelsizevertical, @@ -511,9 +512,9 @@ function _data_collections($single = null) // $this->db->set_debug(True); // will want to support these too at some point - $where2 = ' AND es.energyscanid < 0'; - $where3 = ' AND r.robotactionid < 0'; - $where4 = ' AND xrf.xfefluorescencespectrumid < 0'; + $where2 .= ' AND es.energyscanid < 0'; + $where3 .= ' AND r.robotactionid < 0'; + $where4 .= ' AND xrf.xfefluorescencespectrumid < 0'; if ($this->has_arg('dcg')) { $where .= ' AND dc.datacollectiongroupid=:' . (sizeof($args) + 1); @@ -574,7 +575,7 @@ function _data_collections($single = null) max(dc.c2lens) as c2lens, max(dc.objaperture) as objaperture, max(dc.magnification) as magnification, - sum(dc.totalabsorbeddose) as totaldose, + dose.total as totaldose, CAST(dc.totalabsorbeddose AS DECIMAL(5, 2)) as totalabsdose, max(d.detectormanufacturer) as detectormanufacturer, max(d.detectormodel) as detectormodel, @@ -660,6 +661,9 @@ function _data_collections($single = null) SELECT $extcg $fields FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid + INNER JOIN ( + select datacollectiongroupid, sum(totalabsorbeddose) as total from datacollection group by datacollectiongroupid) dose + ON dc.datacollectiongroupid = dose.datacollectiongroupid INNER JOIN blsession ses ON ses.sessionid = dcg.sessionid LEFT OUTER JOIN experimenttype et on dcg.experimenttypeid = et.experimenttypeid $sample_joins[0] diff --git a/api/src/Page/Download.php b/api/src/Page/Download.php index debeaeb45..a1ed29bca 100644 --- a/api/src/Page/Download.php +++ b/api/src/Page/Download.php @@ -56,6 +56,7 @@ class Download extends Page array('/ap/attachments(/:AUTOPROCPROGRAMATTACHMENTID)(/dl/:download)', 'get', '_get_autoproc_attachments'), array('/ap/archive/:AUTOPROCPROGRAMID', 'get', '_get_autoproc_archive'), + array('/ap/log/:AUTOPROCPROGRAMID', 'get', '_get_autoproc_log'), ); @@ -220,7 +221,7 @@ function __get_autoproc_attachments() INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid WHERE s.proposalid=:1 $where - ORDER BY importancerank" + ORDER BY importancerank, filename" ), $args); return $rows; @@ -413,6 +414,48 @@ function _get_attachment() } } + # ------------------------------------------------------------------------ + # Get the most important log of an autoprocessing job + function _get_autoproc_log() + { + + if (!$this->has_arg('prop')) { + $this->_error('No proposal specified'); + } + $args = array($this->proposalid, $this->arg('AUTOPROCPROGRAMID')); + + $rows = $this->db->union(array( + "SELECT app.autoprocprogramid, appa.filename, appa.filepath + FROM autoprocintegration api + INNER JOIN autoprocprogram app ON api.autoprocprogramid = app.autoprocprogramid + INNER JOIN autoprocprogramattachment appa ON appa.autoprocprogramid = app.autoprocprogramid + INNER JOIN datacollection dc ON dc.datacollectionid = api.datacollectionid + INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid + INNER JOIN blsession s ON s.sessionid = dcg.sessionid + WHERE s.proposalid=:1 + AND app.autoprocprogramid=:2 + AND appa.importancerank=1 + AND appa.filetype='Log'", + "SELECT app.autoprocprogramid, appa.filename, appa.filepath + FROM autoprocprogram app + INNER JOIN processingjob pj on pj.processingjobid = app.processingjobid + INNER JOIN autoprocprogramattachment appa ON appa.autoprocprogramid = app.autoprocprogramid + INNER JOIN datacollection dc ON dc.datacollectionid = pj.datacollectionid + INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid + INNER JOIN blsession s ON s.sessionid = dcg.sessionid + WHERE s.proposalid=:1 + AND app.autoprocprogramid=:2 + AND appa.importancerank=1 + AND appa.filetype='Log'" + ), $args); + + if (!sizeof($rows)) { + return $this->_error('No log file found'); + } + $this->_get_file($rows[0]['AUTOPROCPROGRAMID'], $rows[0]); + + } + # ------------------------------------------------------------------------ # Get an archive of an autoproc diff --git a/api/src/Page/Imaging.php b/api/src/Page/Imaging.php index 4d09aeb8a..02d1451b1 100644 --- a/api/src/Page/Imaging.php +++ b/api/src/Page/Imaging.php @@ -376,6 +376,11 @@ function _get_inspections() $where .= " AND i.state='Completed'"; } + if ($this->has_arg('s')) { + $where .= " AND (CONCAT(p.proposalcode, p.proposalnumber) LIKE CONCAT('%', :" . (sizeof($args) + 1) . ", '%') OR c.code LIKE CONCAT('%', :" . (sizeof($args) + 2) . ", '%'))"; + array_push($args, $this->arg('s'), $this->arg('s')); + } + $tot = $this->db->pq("SELECT count(i.containerinspectionid) as tot FROM containerinspection i INNER JOIN container c ON c.containerid = i.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid @@ -422,12 +427,6 @@ function _get_inspections() $order = $cols[$this->arg('sort_by')] . ' ' . $dir; } - if ($this->has_arg('s')) { - $where .= " AND (LOWER(CONCAT(p.proposalcode, p.proposalnumber)) LIKE LOWER(CONCAT(CONCAT('%', :" . (sizeof($args) + 1) . "), '%')) OR LOWER(c.code) LIKE LOWER(CONCAT(CONCAT('%', :" . (sizeof($args) + 2) . "), '%')))"; - array_push($args, $this->arg('s')); - array_push($args, $this->arg('s')); - } - if ($this->has_arg('ty')) { if ($this->arg('ty') == 'COMPLETED') $where .= " AND i.state = 'Completed' AND i.manual=0"; diff --git a/api/src/Page/Process.php b/api/src/Page/Process.php index 70fadb66d..d26ccd61b 100644 --- a/api/src/Page/Process.php +++ b/api/src/Page/Process.php @@ -355,7 +355,7 @@ function _add_reprocessing_sweep($args) { function _enqueue() { - global $zocalo_mx_reprocess_queue; + global $rabbitmq_zocalo_vhost; if (!$this->has_arg('PROCESSINGJOBID')) $this->_error('No processing job specified'); @@ -379,7 +379,7 @@ function _enqueue() 'ispyb_process' => intval($this->arg('PROCESSINGJOBID')), ) ); - $this->_send_zocalo_message($zocalo_mx_reprocess_queue, $message); + $this->_send_zocalo_message($rabbitmq_zocalo_vhost, $message); $this->_output(new \stdClass); } diff --git a/api/src/Page/Proposal.php b/api/src/Page/Proposal.php index ab719e2e7..f8207ed2f 100644 --- a/api/src/Page/Proposal.php +++ b/api/src/Page/Proposal.php @@ -456,6 +456,7 @@ function _get_visits($visit = null, $output = true) s.beamlineoperator AS lc, s.comments, s.scheduled, + s.riskrating, st.typename AS sessiontype, DATE_FORMAT(s.startdate, '%d-%m-%Y %H:%i') AS startdate, DATE_FORMAT(s.enddate, '%d-%m-%Y %H:%i') AS enddate, diff --git a/api/src/Page/Sample.php b/api/src/Page/Sample.php index 0b06ccd6d..3ec903b72 100644 --- a/api/src/Page/Sample.php +++ b/api/src/Page/Sample.php @@ -1835,12 +1835,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')) { diff --git a/api/src/Page/Shipment.php b/api/src/Page/Shipment.php index 6b3f16f4a..fdb44b725 100644 --- a/api/src/Page/Shipment.php +++ b/api/src/Page/Shipment.php @@ -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 @@ -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() { @@ -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'))); @@ -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, diff --git a/api/src/Queue.php b/api/src/Queue.php index 44b334099..4440d45a7 100644 --- a/api/src/Queue.php +++ b/api/src/Queue.php @@ -2,42 +2,34 @@ namespace SynchWeb; -use Stomp\Exception\StompException; -use Stomp\Stomp; +use PhpAmqpLib\Connection\AMQPStreamConnection; +use PhpAmqpLib\Message\AMQPMessage; class Queue { - private $server, $username, $password; + private $host, $port, $username, $password, $vhost; - function __construct($server, $username, $password) + function __construct($host, $port, $username, $password, $vhost) { - $this->server = $server; + $this->host = $host; + $this->port = $port; $this->username = $username; $this->password = $password; + $this->vhost = $vhost; } - function send($queue, array $message, $persistent = false, $login = null) + function send(array $message, $routing_key) { - try { - $connection = new Stomp($this->server); - - $connection->connect($this->username, $this->password); - - $connection->send( - $queue, - json_encode($message, JSON_UNESCAPED_SLASHES), - array( - 'persistent' => ($persistent === true), - 'synchweb.host' => gethostname(), - 'synchweb.user' => $login, - ) - ); - - $connection->disconnect(); - } catch (StompException $e) { - /** @noinspection PhpUnhandledExceptionInspection */ - - throw $e; - } + $connection = new AMQPStreamConnection($this->host, $this->port, $this->username, $this->password, $this->vhost); + $channel = $connection->channel(); + + $msg = new AMQPMessage( + json_encode($message, JSON_UNESCAPED_SLASHES) + ); + + $channel->basic_publish($msg, null, $routing_key); + + $channel->close(); + $connection->close(); } } diff --git a/client/src/js/models/shipment.js b/client/src/js/models/shipment.js index e1849b9bf..eccef8c27 100644 --- a/client/src/js/models/shipment.js +++ b/client/src/js/models/shipment.js @@ -18,6 +18,11 @@ define(['backbone'], function (Backbone) { pattern: 'fcode', }, + FIRSTEXPERIMENTID: { + required: false, + pattern: 'number', + }, + REMOTEORMAILIN: { required: false, }, diff --git a/client/src/js/modules/calendar/views/components/calendar-day-events.vue b/client/src/js/modules/calendar/views/components/calendar-day-events.vue index 7c2b70a20..319978312 100644 --- a/client/src/js/modules/calendar/views/components/calendar-day-events.vue +++ b/client/src/js/modules/calendar/views/components/calendar-day-events.vue @@ -21,7 +21,12 @@ class="tw-no-underline tw-text-content-page-color" > {{ session['VISIT'] }} - ({{ session['LEN'] }}) + + 🟢 + 🟡 + 🔴 + + ({{ session['LEN'] }})

- {{ session['BEAMLINEOPERATOR'] }} @@ -105,4 +110,4 @@ export default { } } - \ No newline at end of file + diff --git a/client/src/js/modules/dc/views/distl.js b/client/src/js/modules/dc/views/distl.js index e21dd9bd4..76496c70f 100644 --- a/client/src/js/modules/dc/views/distl.js +++ b/client/src/js/modules/dc/views/distl.js @@ -27,7 +27,7 @@ define(['marionette', 'modules/dc/models/distl', 'utils', }, plotSelected: function(e, ranges) { - this.trigger('plot:select', Math.floor(ranges.xaxis.from), Math.ceil(ranges.xaxis.to)) + this.trigger('plot:select', Math.round(ranges.xaxis.from), Math.round(ranges.xaxis.to)) }, plotUnselected: function(e) { @@ -112,4 +112,4 @@ define(['marionette', 'modules/dc/models/distl', 'utils', } }) -}) \ No newline at end of file +}) diff --git a/client/src/js/modules/dc/views/reprocess2.js b/client/src/js/modules/dc/views/reprocess2.js index 53ebb4d54..6dee03148 100644 --- a/client/src/js/modules/dc/views/reprocess2.js +++ b/client/src/js/modules/dc/views/reprocess2.js @@ -110,13 +110,19 @@ define(['backbone', 'marionette', 'views/dialog', var si = parseInt(this.model.get('SI')) var ni = parseInt(this.model.get('NUMIMG')) - if (this.ui.st.val() > (si+ni-1)) this.ui.st.val(si+ni-1) - if (this.ui.st.val() < si) this.ui.st.val(si) + if (this.ui.st.val()) { + if (this.ui.st.val() > (si+ni-1)) this.ui.st.val(si+ni-1) + if (this.ui.st.val() < si) this.ui.st.val(si) + } - if (this.ui.en.val() > (si+ni-1)) this.ui.en.val(si+ni-1) - if (this.ui.en.val() < si) this.ui.en.val(si) + if (this.ui.en.val()) { + if (this.ui.en.val() > (si+ni-1)) this.ui.en.val(si+ni-1) + if (this.ui.en.val() < si) this.ui.en.val(si) + } - this.plotview.setSelection(parseInt(this.ui.st.val()), parseInt(this.ui.en.val())) + if (this.ui.st.val() && this.ui.en.val()) { + this.plotview.setSelection(parseInt(this.ui.st.val()), parseInt(this.ui.en.val())) + } }, initialize: function(options) { @@ -377,7 +383,7 @@ define(['backbone', 'marionette', 'views/dialog', }, this) $.when.apply($, reqs).done(function() { - app.alert({ message: jobs+' reprocessing job(s) successfully submitted'}) + app.message({ message: jobs+' reprocessing job(s) successfully submitted'}) _.each(rps, function(rp) { self._enqueue({ PROCESSINGJOBID: rp.get('PROCESSINGJOBID') }) }) @@ -463,7 +469,7 @@ define(['backbone', 'marionette', 'views/dialog', reqs.push(reprocessingsweeps.save()) $.when.apply($, reqs).done(function() { - app.alert({ message: '1 reprocessing job successfully submitted'}) + app.message({ message: '1 reprocessing job successfully submitted'}) self._enqueue({ PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID') }) }) }, diff --git a/client/src/js/modules/imaging/views/queuecontainer.js b/client/src/js/modules/imaging/views/queuecontainer.js index 4369c4448..c3652dc2c 100644 --- a/client/src/js/modules/imaging/views/queuecontainer.js +++ b/client/src/js/modules/imaging/views/queuecontainer.js @@ -101,7 +101,7 @@ define(['marionette', var self = this p.save({}, { success: function() { - app.alert({ message: 'Preset successfully saved' }) + app.message({ message: 'Preset successfully saved' }) self.column.get('plans').add(p) }, error: function() { @@ -130,8 +130,7 @@ define(['marionette', render: function() { this.$el.empty() - if (!this.model.get('CONTAINERQUEUEID')) - this.$el.html('\ + this.$el.html('\ \ ') @@ -495,6 +494,9 @@ define(['marionette', xtal: '.xtalpreview', nodata: 'input[name=nodata]', notcompleted: 'input[name=notcompleted]', + queuebutton: '.queuebutton', + unqueuebutton: '.unqueuebutton', + queuelength: '.queuelength', }, @@ -590,7 +592,7 @@ define(['marionette', }, success: function(resp) { _.each(resp, function (r) { - let ss = self.qsubsamples.fullCollection.findWhere({ BLSUBSAMPLEID: r.BLSUBSAMPLEID }) + let ss = self.typeselector.shadowCollection.findWhere({ BLSUBSAMPLEID: r.BLSUBSAMPLEID }) ss.set({ READYFORQUEUE: '0' }) }) }, @@ -606,12 +608,13 @@ define(['marionette', e.preventDefault() utils.confirm({ title: 'Unqueue Container?', - content: 'Are you sure you want to remove this container from the queue? You will loose your current place', + content: 'Are you sure you want to remove this container from the queue? You will lose your current place', callback: this.doUnqueueContainer.bind(this) }) }, doUnqueueContainer: function() { + var self = this Backbone.ajax({ url: app.apiurl+'/shipment/containers/queue', data: { @@ -619,7 +622,10 @@ define(['marionette', UNQUEUE: 1, }, success: function() { - app.alert({ message: 'Container Successfully Unqueued' }) + app.message({ message: 'Container Successfully Unqueued' }) + self.ui.unqueuebutton.hide() + self.ui.queuebutton.show() + self.model.set('CONTAINERQUEUEID', null) }, error: function() { app.alert({ message: 'Something went wrong unqueuing this container' }) @@ -666,12 +672,12 @@ define(['marionette', queueContainer: function(e) { e.preventDefault() - if (!this.qsubsamples.fullCollection.length) { + if (!this.typeselector.shadowCollection.length) { app.alert({ message: 'Please add some samples before queuing this container' }) return } - // need to validate all models here again incase they havnt been rendered + // need to validate all models here again in case they haven't been rendered this.qsubsamples.fullCollection.each(function(qs) { if (qs.get('_valid') !== undefined) return @@ -680,7 +686,7 @@ define(['marionette', console.log({ experimentCell: val }) }, this) - var invalid = this.qsubsamples.fullCollection.where({ '_valid': false }) + var invalid = this.typeselector.shadowCollection.where({ '_valid': false }) console.log('queue', invalid, invalid.length > 0) if (invalid.length > 0) { app.alert({ message: 'There are '+invalid.length+' sub samples with invalid experimental plans, please either correct or remove these from the queue' }) @@ -688,13 +694,17 @@ define(['marionette', if (inv) inv.set({ isSelected: true }) } else { + var self = this Backbone.ajax({ url: app.apiurl+'/shipment/containers/queue', data: { CONTAINERID: this.model.get('CONTAINERID') }, - success: function() { - app.alert({ message: 'Container Successfully Queued' }) + success: function(json) { + app.message({ message: 'Container Successfully Queued' }) + self.ui.unqueuebutton.show() + self.ui.queuebutton.hide() + self.model.set('CONTAINERQUEUEID', json.CONTAINERQUEUEID) }, error: function() { app.alert({ message: 'Something went wrong queuing this container' }) @@ -738,6 +748,7 @@ define(['marionette', this.platetypes = new PlateTypes() this.type = this.platetypes.findWhere({ name: this.model.get('CONTAINERTYPE') }) + this.unfilteredSubsamples = null this.subsamples = new SubSamples() this.subsamples.queryParams.cid = this.model.get('CONTAINERID') if (this.params.s) this.subsamples.queryParams.s = this.params.s @@ -807,6 +818,31 @@ define(['marionette', this.ui.preset.html(this.plans.opts()) }, + onSubsamplesReady: function() { + this.unfilteredSubsamples = this.subsamples.fullCollection.clone() + this.getInspectionImages() + this.refreshQSubSamples() + this.listenTo(this.subsamples, 'change:isSelected', this.selectSubSample, this) + this.listenTo(this.unfilteredSubsamples, 'sync add remove change:READYFORQUEUE', this.refreshQSubSamples, this) + this.listenTo(this.unfilteredSubsamples, 'change', this.updateQueueLength) + this.listenTo(this.model, 'change:CONTAINERQUEUEID', this.onContainerQueueIdChange) + }, + + updateQueueLength: function() { + var n = this.typeselector.shadowCollection.length + this.ui.queuelength.html(`(${n} data collection${n===1 ? '' : 's'})`) + }, + + onContainerQueueIdChange: function() { + if (!this.model.get('CONTAINERQUEUEID')) { + var models = this.unfilteredSubsamples.filter(function(m) { return m.get('CONTAINERQUEUEID') }) + _.each(models, function(model) { + model.set('READYFORQUEUE', '1') + }, this) + } + this.doOnRender() + }, + getInspectionImages: function() { this.inspectionimages.queryParams.iid = this.inspections.at(0).get('CONTAINERINSPECTIONID') this.inspectionimages.fetch().done(this.selectSample.bind(this)) @@ -818,8 +854,8 @@ define(['marionette', refreshQSubSamples: function() { if (this.model.get('CONTAINERQUEUEID')) { - this.qsubsamples.fullCollection.reset(this.subsamples.fullCollection.where({ CONTAINERQUEUEID: this.model.get('CONTAINERQUEUEID') })) - } else this.qsubsamples.fullCollection.reset(this.subsamples.fullCollection.where({ READYFORQUEUE: '1' })) + this.qsubsamples.fullCollection.reset(this.unfilteredSubsamples.where({ CONTAINERQUEUEID: this.model.get('CONTAINERQUEUEID') })) + } else this.qsubsamples.fullCollection.reset(this.unfilteredSubsamples.where({ READYFORQUEUE: '1' })) }, selectSubSample: function() { @@ -834,6 +870,7 @@ define(['marionette', onRender: function() { + this.ui.unqueuebutton.hide() this.subsamples.queryParams.nodata = this.getNoData.bind(this) this.subsamples.queryParams.notcompleted = this.getNotCompleted.bind(this) this.subsamples.queryParams.s = this.getSearch.bind(this) @@ -883,7 +920,6 @@ define(['marionette', { name: '_valid', label: 'Valid', cell: table.TemplateCell, editable: false, test: '_valid', template: '' }, { name: '', cell: table.StatusCell, editable: false }, { label: '', cell: SnapshotCell, editable: false, inspectionimages: this.inspectionimages }, - { label: '', cell: ActionsCell, editable: false, plans: this.plans }, ] if (app.mobile()) { @@ -907,8 +943,15 @@ define(['marionette', if (this.model.get('CONTAINERQUEUEID')) { this.ui.rpreset.hide() + this.ui.queuebutton.hide() + this.ui.unqueuebutton.show() queuedSubSamples.push({ label: '', cell: table.StatusCell, editable: false }) queuedSubSamples.push({ label: '', cell: table.TemplateCell, editable: false, template: '' }) + } else { + this.ui.rpreset.show() + this.ui.queuebutton.show() + this.ui.unqueuebutton.hide() + queuedSubSamples.push({ label: '', cell: ActionsCell, editable: false, plans: this.plans }) } this.table2 = new TableView({ diff --git a/client/src/js/modules/shipment/views/plate.js b/client/src/js/modules/shipment/views/plate.js index 39d88b2b9..473b62aae 100644 --- a/client/src/js/modules/shipment/views/plate.js +++ b/client/src/js/modules/shipment/views/plate.js @@ -259,7 +259,7 @@ define(['marionette', 'backbone', 'utils', 'backbone-validation'], function(Mari this.ctx.fillStyle = sample.get(this.rankOption.value) ? utils.rainbow(val/4) - : (sample.get(this.rankOption.check) > 0 ? 'yellow' : '#dfdfdf') + : '#dfdfdf' this.ctx.fill() } else { diff --git a/client/src/js/modules/shipment/views/shipment.js b/client/src/js/modules/shipment/views/shipment.js index 6932f8645..bbde464f3 100644 --- a/client/src/js/modules/shipment/views/shipment.js +++ b/client/src/js/modules/shipment/views/shipment.js @@ -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') diff --git a/client/src/js/modules/shipment/views/shipmentadd.js b/client/src/js/modules/shipment/views/shipmentadd.js index a5fea270b..602e33487 100644 --- a/client/src/js/modules/shipment/views/shipmentadd.js +++ b/client/src/js/modules/shipment/views/shipmentadd.js @@ -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: { @@ -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) { @@ -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('') this.ui.dynamic.prop('checked', false) } else { - this.ui.first.html(this.visits.opts()) + this.ui.first.html(''+this.visits.opts()) } }, @@ -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(''+this.visits.opts()) this.$el.find(".remoteform").hide() if (industrial_visit) { this.$el.find(".remoteormailin").hide() diff --git a/client/src/js/modules/types/mx/shipment/views/container-mixin.js b/client/src/js/modules/types/mx/shipment/views/container-mixin.js index f622ac683..e29c60b4d 100644 --- a/client/src/js/modules/types/mx/shipment/views/container-mixin.js +++ b/client/src/js/modules/types/mx/shipment/views/container-mixin.js @@ -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() diff --git a/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue b/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue index f08642bef..9f2e59ee1 100644 --- a/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue +++ b/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue @@ -90,9 +90,13 @@

+ + Cannot queue containers in {{ shippingSafetyLevel }} shipments +
@@ -461,6 +465,7 @@ export default { // The dewar that this container will belong to dewar: null, + shippingSafetyLevel: null, processingPipeline: '', processingPipelines: [], @@ -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) { @@ -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) diff --git a/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue b/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue index 20c5b46ac..9de2394dc 100644 --- a/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue +++ b/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue @@ -106,24 +106,25 @@ @click="onUnQueueContainer" > Unqueue + + Cannot queue containers in {{ shippingSafetyLevel }} shipments + + + There was an error submitting the container to the queue. Please fix any errors in the samples table. + Try again + Cancel + - - There was an error submitting the container to the queue. Please fix any errors in the samples table. - Try again - Cancel - - - Queue this container for Auto Collect - + Queue this container for Auto Collect @@ -324,6 +325,7 @@ export default { dewarsCollection: null, selectedDewarId: null, selectedShipmentId: null, + shippingSafetyLevel: null, editingSampleLocation: null } }, @@ -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 }, @@ -477,6 +480,7 @@ export default { }) this.$nextTick(() => { this.loadContainerData() + this.getProteins() // TODO: Toggle Auto in the samples table }) } catch (error) { @@ -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) { diff --git a/client/src/js/modules/visits/views/visit_list.vue b/client/src/js/modules/visits/views/visit_list.vue index 5b1abbf84..c3feef51a 100644 --- a/client/src/js/modules/visits/views/visit_list.vue +++ b/client/src/js/modules/visits/views/visit_list.vue @@ -57,7 +57,10 @@ - + 🟢 + 🟡 + 🔴 +
@@ -134,6 +137,10 @@ export default { key: "BEAMLINENAME", title: 'Beamline' }, + { + key: "ERA", + title: 'ERA' + }, { key: "DEWARS", title: 'Dewar(s)' diff --git a/client/src/js/templates/dc/dc_autoproc.html b/client/src/js/templates/dc/dc_autoproc.html index cde85c1f9..94c69e5d9 100644 --- a/client/src/js/templates/dc/dc_autoproc.html +++ b/client/src/js/templates/dc/dc_autoproc.html @@ -22,6 +22,7 @@ <% } %> + Processing Log Plots Archive Logs & Files diff --git a/client/src/js/templates/imaging/queuecontainer.html b/client/src/js/templates/imaging/queuecontainer.html index 9d219c67f..a6e10b578 100644 --- a/client/src/js/templates/imaging/queuecontainer.html +++ b/client/src/js/templates/imaging/queuecontainer.html @@ -2,8 +2,7 @@

Prepare Container for Data Collection

This pages allows you to queue samples in a container for data collection

-<% if (CONTAINERQUEUEID) { %> -

+

<% if (['disposed', 'in_storage', null].indexOf(CONTAINERSTATUS) > -1) { %> This container is queued for data collection, you can not modify it without unqueuing it Unqueue @@ -11,7 +10,6 @@

Prepare Container for Data Collection

This container is en route for data collection, it cannot be unqueued. <% } %>

-<% } %>
@@ -65,10 +63,12 @@

Queued Samples

-<% if (!CONTAINERQUEUEID) { %> +

Queue Plate

Queuing the container will make the queued items immutable, please make sure you have added all sub samples, and that they have valid experimental plans

+
-<% } %> +
+ diff --git a/client/src/js/templates/shipment/containerli.html b/client/src/js/templates/shipment/containerli.html index b3f73d39a..c93be9f2b 100644 --- a/client/src/js/templates/shipment/containerli.html +++ b/client/src/js/templates/shipment/containerli.html @@ -1,10 +1,13 @@ <%- NAME %> (<%-SAMPLES%> samples) <% if (REQUESTEDIMAGERID) { %> - [Req: <%-REQUESTEDIMAGER%>] + [Req: <%-REQUESTEDIMAGER%>] <% } %> <% if (IMAGERID) { %> - [Img: <%-IMAGER%>] + [Img: <%-IMAGER%>] + <% } %> + <% if (VISIT) { %> + (<%-VISIT%>) <% } %> Print Container Report diff --git a/client/src/js/templates/shipment/shipmentadd.html b/client/src/js/templates/shipment/shipmentadd.html index 3866fe012..927b68a39 100644 --- a/client/src/js/templates/shipment/shipmentadd.html +++ b/client/src/js/templates/shipment/shipmentadd.html @@ -52,12 +52,14 @@

Add New Shipment

- - + + + +
diff --git a/client/src/js/utils/editable.js b/client/src/js/utils/editable.js index 3515c210d..622779b54 100644 --- a/client/src/js/utils/editable.js +++ b/client/src/js/utils/editable.js @@ -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() }, @@ -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) { diff --git a/client/src/js/views/log.js b/client/src/js/views/log.js index 88db1ab96..54d25ae47 100644 --- a/client/src/js/views/log.js +++ b/client/src/js/views/log.js @@ -7,8 +7,7 @@ define(['marionette', 'views/dialog', 'utils'], function(Marionette, DialogView, initialize: function(options) { this.url = options.url this.load() - this.iframe = $( - ``) + this.iframe = $(``) }, // Override existing dialogOptions of Dialog View @@ -117,11 +116,10 @@ define(['marionette', 'views/dialog', 'utils'], function(Marionette, DialogView, onRender: function() { this.$el.append(this.iframe) - this.$el.find('iframe').css('width', $(window).width()*(app.mobile() ? 0.8 : 0.5)) - this.$el.find('iframe').css('height', $(window).height()*(app.mobile() ? 0.8 : 0.5)) - + this.$el.find('iframe').css('width', "99%") + this.$el.find('iframe').css('height', "98%") } }) -}) \ No newline at end of file +})