diff --git a/api/src/Page/Download.php b/api/src/Page/Download.php index a046297b2..3fa806b69 100644 --- a/api/src/Page/Download.php +++ b/api/src/Page/Download.php @@ -188,9 +188,9 @@ function __get_autoproc_attachments() array_push($args, $this->arg('FILENAME')); } - if ($this->has_arg('FILETYPE')) { + if ($this->has_arg('filetype')) { $where .= ' AND appa.filetype =:' . (sizeof($args) + 1); - array_push($args, $this->arg('FILETYPE')); + array_push($args, $this->arg('filetype')); } if ($this->has_arg('AUTOPROCPROGRAMID')) { diff --git a/api/src/Page/Process.php b/api/src/Page/Process.php index d26ccd61b..b41cb22d9 100644 --- a/api/src/Page/Process.php +++ b/api/src/Page/Process.php @@ -11,6 +11,7 @@ class Process extends Page public static $arg_list = array( 'PROCESSINGJOBID' => '\d+', 'DATACOLLECTIONID' => '\d+', + 'SCALINGID' => '\d+', 'DISPLAYNAME' => '([\w\s\-])+', 'COMMENTS' => '.*', @@ -45,6 +46,7 @@ class Process extends Page array('/sweeps', 'post', '_add_reprocessing_sweeps'), array('/enqueue', 'post', '_enqueue'), + array('/enqueue/downstream', 'post', '_enqueue_downstream'), array('/pipelines', 'get', '_pipelines'), ); @@ -384,6 +386,36 @@ function _enqueue() $this->_output(new \stdClass); } + function _enqueue_downstream() + { + if (!$this->has_arg('DATACOLLECTIONID')) $this->_error('No data collection id specified'); + if (!$this->has_arg('SCALINGID')) $this->_error('No scaling id specified'); + if (!$this->has_arg('RECIPE')) $this->_error('No recipe specified'); + + $chk = $this->db->pq("SELECT dc.datacollectionid, aps.autoprocscalingid + FROM datacollection dc + INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid + INNER JOIN blsession s ON s.sessionid = dcg.sessionid + INNER JOIN proposal p ON p.proposalid = s.proposalid + INNER JOIN processingjob rp ON rp.datacollectionid = dc.datacollectionid + INNER JOIN autoprocprogram app ON app.processingjobid = rp.processingjobid + INNER JOIN autoproc ap ON ap.autoprocprogramid = app.autoprocprogramid + INNER JOIN autoprocscaling aps ON aps.autoprocid = ap.autoprocid + WHERE p.proposalid = :1 AND dc.datacollectionid = :2 AND aps.autoprocscalingid = :3", + array($this->proposalid, $this->arg('DATACOLLECTIONID'), $this->arg('SCALINGID'))); + + if (!sizeof($chk)) $this->_error('No such data collection id or scaling id'); + + $parameters = array( + 'ispyb_dcid' => intval($this->arg('DATACOLLECTIONID')), + 'scaling_id' => intval($this->arg('SCALINGID')), + ); + + $this->_submit_zocalo_recipe($this->arg('RECIPE'), $parameters); + + $this->_output(new \stdClass); + } + /* * Controller method for /process/pipelines * Returns list of processing pipelines that meet query parameters diff --git a/api/src/Page/Processing.php b/api/src/Page/Processing.php index 24753f690..947d02f2a 100644 --- a/api/src/Page/Processing.php +++ b/api/src/Page/Processing.php @@ -968,6 +968,7 @@ private function _autoprocessing_query_builder($where, $group, $order = '') { apss.multiplicity, apss.meanioversigi as isigi, apss.resioversigi2 as resisigi, + aps.autoprocscalingid, ap.spacegroup as sg, ap.refinedcell_a as cell_a, ap.refinedcell_b as cell_b, @@ -1149,6 +1150,7 @@ private function _format_auto_processing_result($table_rows, $messages_result) { $formatted_result[$row['AUTOPROCPROGRAMID']]['TYPE'] = $row['TYPE']; $formatted_result[$row['AUTOPROCPROGRAMID']]['AID'] = $row['AUTOPROCPROGRAMID']; + $formatted_result[$row['AUTOPROCPROGRAMID']]['SCALINGID'] = $row['AUTOPROCSCALINGID']; $formatted_result[$row['AUTOPROCPROGRAMID']]['MESSAGES'] = array_key_exists($row['AUTOPROCPROGRAMID'], $messages_result) ? $messages_result[$row['AUTOPROCPROGRAMID']] : array(); diff --git a/api/src/Page/Sample.php b/api/src/Page/Sample.php index 7a79235f0..624c66250 100644 --- a/api/src/Page/Sample.php +++ b/api/src/Page/Sample.php @@ -1714,9 +1714,6 @@ function _proteins() } - $tot = $this->db->pq("SELECT count(distinct pr.proteinid) as tot FROM protein pr INNER JOIN proposal p ON p.proposalid = pr.proposalid $join WHERE $where", $args); - $tot = intval($tot[0]['TOT']); - if ($this->has_arg('s')) { $st = sizeof($args) + 1; $where .= " AND (lower(pr.name) LIKE lower(CONCAT(CONCAT('%',:" . $st . "), '%')) OR lower(pr.acronym) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')))"; @@ -1724,6 +1721,19 @@ function _proteins() array_push($args, $this->arg('s')); } + if ($this->has_arg('sid')) { + $where .= ' AND b.blsampleid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('sid')); + $join .= ' LEFT JOIN diffractionplan dp ON dp.diffractionplanid = b.diffractionplanid'; + $extc = 'dp.anomalousscatterer, '; + } + + $tot = $this->db->pq("SELECT count(distinct pr.proteinid) as tot FROM protein pr + INNER JOIN proposal p ON p.proposalid = pr.proposalid + LEFT OUTER JOIN crystal cr ON cr.proteinid = pr.proteinid + LEFT OUTER JOIN blsample b ON b.crystalid = cr.crystalid + $join WHERE $where", $args); + $tot = intval($tot[0]['TOT']); $start = 0; $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; diff --git a/client/src/js/modules/dc/views/autointegration.js b/client/src/js/modules/dc/views/autointegration.js index 6c72ff0a5..9f35870a6 100644 --- a/client/src/js/modules/dc/views/autointegration.js +++ b/client/src/js/modules/dc/views/autointegration.js @@ -8,6 +8,7 @@ define(['marionette', 'modules/dc/views/aiplots', 'modules/dc/views/autoprocattachments', 'modules/dc/views/apmessages', + 'modules/dc/views/downstreamreprocess', 'views/log', 'views/table', @@ -16,6 +17,7 @@ define(['marionette', 'templates/dc/dc_autoproc.html'], function(Marionette, Backbone, Backgrid, TabView, AutoProcAttachments, AutoIntegrations, RDPlotView, AIPlotsView, AutoProcAttachmentsView, APMessagesView, + DownstreamView, LogView, TableView, table, utils, template) { @@ -30,6 +32,7 @@ define(['marionette', 'click .rd': 'showRD', 'click .plot': 'showPlots', 'click a.apattach': 'showAttachments', + 'click a.downstream': 'downstream', 'click .dll': utils.signHandler, }, @@ -57,6 +60,11 @@ define(['marionette', this.listenTo(this.attachments, 'file:uploaded', this.showAttachments, this) }, + downstream: function(e) { + e.preventDefault() + app.dialog.show(new DownstreamView({ model: this.getOption('templateHelpers').PARENT, scalingid: this.model.get('SCALINGID'), autoprocprogramid: this.model.get('AID'), type: this.model.get('TYPE')})) + }, + showLog: function(e) { e.preventDefault() var url = $(e.target).attr('href') @@ -98,6 +106,7 @@ define(['marionette', DCID: dcId, APIURL: app.apiurl, PROPOSAL_TYPE: app.type, + PARENT: this.options.parent, } } }, @@ -170,6 +179,7 @@ define(['marionette', collection: this.collection, id: this.getOption('id'), el: this.$el.find('.res'), + parent: this.options.parent, })) } diff --git a/client/src/js/modules/dc/views/dc.js b/client/src/js/modules/dc/views/dc.js index dbb796e47..ec885864c 100644 --- a/client/src/js/modules/dc/views/dc.js +++ b/client/src/js/modules/dc/views/dc.js @@ -152,7 +152,8 @@ define(['marionette', this.ap = new DCAutoIntegrationView({ id: this.model.get('ID'), dcPurgedProcessedData: this.model.get('PURGEDPROCESSEDDATA'), - el: this.$el.find('div.autoproc') + el: this.$el.find('div.autoproc'), + parent: this.model }) } else this.ap.$el.slideToggle() }, diff --git a/client/src/js/modules/dc/views/downstreamreprocess.js b/client/src/js/modules/dc/views/downstreamreprocess.js new file mode 100644 index 000000000..4154a2c68 --- /dev/null +++ b/client/src/js/modules/dc/views/downstreamreprocess.js @@ -0,0 +1,204 @@ +define(['backbone', 'marionette', 'views/dialog', + 'collections/datacollections', + 'collections/proteins', + 'collections/autoprocattachments', + 'models/datacollection', + 'modules/mc/views/dcdistl_downstream', + 'modules/samples/collections/pdbs', + 'utils/kvcollection', + 'templates/dc/downstream.html', 'templates/dc/downstream_dc.html' +], function(Backbone, Marionette, DialogView, + DataCollections, Proteins, AutoProcAttachments, + DataCollection, DCDistlView, + PDBs, + KVCollection, + template, dctemplate + ) { + + + var IDDataCollection = DataCollection.extend({ + idAttribute: 'CID', + }) + + var IDDataCollections = DataCollections.extend({ + model: IDDataCollection, + }) + + var DCDistlViewLarge = DCDistlView.extend({ + template: dctemplate, + intStatus: false, + className: 'data_collection', + + initialize: function(options) { + DCDistlView.prototype.initialize.apply(this, arguments) + }, + + onRender: function() { + this.$el.find('ul').addClass('half') + this.$el.find('li input[type="text"]').css('width', '25%') + }, + + }) + + + var DCDistlsView = Marionette.CollectionView.extend({ + childView: DCDistlViewLarge, + childViewOptions: function() { + return { + pipelines: this.getOption('pipelines'), + parent: this.model, + } + } + }) + + + var Pipelines = Backbone.Collection.extend(_.extend({ + keyAttribute: 'NAME', + valueAttribute: 'VALUE', + }, KVCollection)) + + + return ReprocessView = DialogView.extend({ + template: template, + dialog: true, + title: 'Rerun downstream processing', + className: 'rp content', + dOptions: { + width: '1000px', + }, + + regions: { + dcr: '.dcs', + }, + + ui: { + pipeline: 'select[name=pipeline]', + warning: '#warning', + }, + + buttons: { + Submit: 'submit', + Close: 'closeDialog', + }, + + disabledButtons: { + Close: 'closeDialog', + }, + + events: { + 'change @ui.pipeline': 'updatePipeline', + }, + + templateHelpers: function() { + return { + VISIT: this.getOption('visit') + } + }, + + updatePipeline: function() { + this.model.set('PIPELINE', this.ui.pipeline.val()) + this.model.set('PIPELINENAME', this.ui.pipeline.find('option:selected').text()) + var btns = this.buttons + var warning = '' + if (['MrBUMP', 'Dimple'].includes(this.model.get('PIPELINENAME')) && this.type.includes('autoPROC')) { + warning = ' Cannot rerun ' + this.model.get('PIPELINENAME') + ' on ' + this.type + ' results' + btns = this.disabledButtons + } + if (['MrBUMP', 'Big EP'].includes(this.model.get('PIPELINENAME')) && this.type.includes('fast_dp')) { + warning = ' Cannot rerun ' + this.model.get('PIPELINENAME') + ' on ' + this.type + ' results' + btns = this.disabledButtons + } + if (this.model.get('PIPELINENAME') === 'Dimple' && this.pdbs.length === 0) { + warning = ' Cannot run Dimple as no PDBs defined' + btns = this.disabledButtons + } + if (this.model.get('PIPELINENAME') === 'MrBUMP' && this.model.get('HASSEQ') === 'No') { + warning = ' Cannot run MrBUMP as no sequence defined' + btns = this.disabledButtons + } + if (this.model.get('PIPELINENAME') === 'Fast EP' && (!this.model.get('ANOMALOUSSCATTERER'))) { + warning = ' Cannot run Fast EP as no anomalous scatterer defined' + btns = this.disabledButtons + } + if (this.model.get('PIPELINENAME') === 'Big EP' && this.model.get('HASSEQ') === 'No') { + warning = ' Cannot run Big EP as no sequence defined' + btns = this.disabledButtons + } + if (this.model.get('PIPELINENAME') === 'Big EP' && (!this.model.get('ANOMALOUSSCATTERER'))) { + warning = ' Cannot run Big EP as no anomalous scatterer defined' + btns = this.disabledButtons + } + this.setButtons(btns) + this.ui.warning.html(warning) + }, + + submit: function(e) { + e.preventDefault() + + this._enqueue({ RECIPE: this.model.get('PIPELINE'), DATACOLLECTIONID: this.model.get('ID'), SCALINGID: this.scalingid }) + app.message({ message: 'Downstream processing job successfully submitted'}) + + }, + + + _enqueue: function(options) { + Backbone.ajax({ + url: app.apiurl+'/process/enqueue/downstream', + method: 'POST', + data: { + DATACOLLECTIONID: options.DATACOLLECTIONID, + SCALINGID: options.SCALINGID, + RECIPE: options.RECIPE + }, + }) + }, + + + initialize: function(options) { + this.proteins = new Proteins(null, { queryParams: { sid: options.model.get('BLSAMPLEID') } }) + this._ready = this.proteins.fetch() + this._ready.done(this.doOnReady.bind(this)) + this.scalingid = options.scalingid + this.type = options.type + this.attachments = new AutoProcAttachments() + this.attachments.queryParams = { + AUTOPROCPROGRAMID: options.autoprocprogramid, + filetype: 'Result', + } + this.attachments.fetch() + }, + + onRender: function() { + + this.pipelines = new Pipelines([ + { NAME: 'Dimple', VALUE: 'trigger-dimple' }, + { NAME: 'Fast EP', VALUE: 'trigger-fastep' }, + { NAME: 'Big EP', VALUE: 'trigger-bigep' }, + { NAME: 'MrBUMP', VALUE: 'trigger-mrbump' }, + ]) + + this.ui.pipeline.html(this.pipelines.opts()) + }, + + doOnReady: function() { + var protein = this.proteins.at(0) + var self = this + _.each(['ACRONYM','PROTEINID','HASSEQ','PDBS','ANOMALOUSSCATTERER'], function(k) { + self.model.set(k, protein.get(k)) + }) + this.pdbs = new PDBs(null, { pid: this.model.get('PROTEINID') }) + this.pdbs.fetch().done(this.updatePipeline.bind(this)) + this.collection = new IDDataCollections() + if (this.model) { + var nm = new IDDataCollection(this.model.toJSON()) + nm.set('CID', this.collection.length+1) + this.collection.add(nm) + } + this.distlview = new DCDistlsView({ collection: this.collection, pipelines: this.pipelines }) + this.dcr.show(this.distlview) + } + + + }) + +}) diff --git a/client/src/js/modules/mc/views/dcdistl_downstream.js b/client/src/js/modules/mc/views/dcdistl_downstream.js new file mode 100644 index 000000000..fec873852 --- /dev/null +++ b/client/src/js/modules/mc/views/dcdistl_downstream.js @@ -0,0 +1,23 @@ +define(['marionette', + 'modules/dc/collections/autointegrations', + 'modules/dc/views/distl', + 'templates/mc/datacollection.html', + ], function(Marionette, AutoIntegrations, DISTLView, dctemplate) { + + + return Marionette.ItemView.extend({ + template: dctemplate, + className: 'dc', + + onShow: function() { + var w = 0.175*$(window).width()*0.95 + var h = $(window).width() > 1280 ? w : ($(window).width() > 800 ? w*1.3 : (w*1.65)) + $('.distl', this.$el).height(h*($(window).width() > 800 ? 0.4 : 1.2)) + this.plotview = new DISTLView({ selection: true, parent: this.model, el: this.$el.find('.distl') }) + }, + + onDestroy: function() { + this.plotview.destroy() + }, + }) +}) diff --git a/client/src/js/templates/dc/dc_autoproc.html b/client/src/js/templates/dc/dc_autoproc.html index 1cf78fa24..493f3781d 100644 --- a/client/src/js/templates/dc/dc_autoproc.html +++ b/client/src/js/templates/dc/dc_autoproc.html @@ -32,6 +32,7 @@ <% } %> Lookup Cell + Downstream Processing

diff --git a/client/src/js/templates/dc/downstream.html b/client/src/js/templates/dc/downstream.html new file mode 100644 index 000000000..fb6886e99 --- /dev/null +++ b/client/src/js/templates/dc/downstream.html @@ -0,0 +1,11 @@ +
+ +
+ + + Pipeline : + + +
+
+ diff --git a/client/src/js/templates/dc/downstream_dc.html b/client/src/js/templates/dc/downstream_dc.html new file mode 100644 index 000000000..4a0bbaa27 --- /dev/null +++ b/client/src/js/templates/dc/downstream_dc.html @@ -0,0 +1,21 @@ +

+ <%-IMP%>_<%-RUN%> - <%-DIR%> +

+ +
+ + +