diff --git a/client/src/js/modules/dc/views/apstatussummary.js b/client/src/js/modules/dc/views/apstatussummary.js index 0dda4c8ce..e0c3e7d9b 100644 --- a/client/src/js/modules/dc/views/apstatussummary.js +++ b/client/src/js/modules/dc/views/apstatussummary.js @@ -50,7 +50,6 @@ define(['marionette', initialize: function(options) { APItemCell.__super__.initialize.call(this,options) this.listenTo(this.model, 'change reset', this.render, this) - this.template = '' }, render: function() { @@ -65,15 +64,16 @@ define(['marionette', ''] var label = this.column.get('program') - this.$el.html(_.template(this.template)) if (this.model.get('APLOADED') == true) { - var ap = res[this.column.get('group')][label] - var ress = {} - _.each(ap, function(a) { - if (!(a in ress)) ress[a] = 0 - ress[a]++ - }) - this.$el.html(_.map(ress, function(c, st) { return c > 1 ? ''+c+'x '+val[st] : val[st]})) + if (this.column.get('group') in res) { + var ap = res[this.column.get('group')][label] + var ress = {} + _.each(ap, function(a) { + if (!(a in ress)) ress[a] = 0 + ress[a]++ + }) + this.$el.html(_.map(ress, function(c, st) { return c > 1 ? ''+c+'x '+val[st] : val[st]})) + } } this.delegateEvents(); return this; @@ -119,7 +119,7 @@ define(['marionette', updateColumns: function() { var states = this.statuses.pluck('STATES') - var group_names = _.keys(states[0]) + var group_names = _.chain(states).map(_.keys).flatten().uniq().value(); var groups = {} _.each(group_names, function(group) { groups[group] = _.unique(_.map(states, function(state) { return _.keys(state[group]) }).flat()) @@ -159,4 +159,4 @@ define(['marionette', this.wrap.show(this.table) } }) -}) \ No newline at end of file +}) diff --git a/client/src/js/modules/dc/views/dc.js b/client/src/js/modules/dc/views/dc.js index 9227e3600..175de6e90 100644 --- a/client/src/js/modules/dc/views/dc.js +++ b/client/src/js/modules/dc/views/dc.js @@ -11,7 +11,7 @@ define(['marionette', 'modules/dc/views/apstatusitem', 'modules/dc/views/apmessagestatusitem', 'modules/dc/views/apmessages', - 'modules/dc/views/reprocess2', + 'modules/dc/views/reprocess', 'modules/dc/views/dcbase', 'templates/dc/dc.html', 'backbone-validation'], function(Marionette, DialogView, TabView, DCDISTLView, diff --git a/client/src/js/modules/dc/views/reprocess.js b/client/src/js/modules/dc/views/reprocess.js index d0bfbd5dd..27a8a5b6d 100644 --- a/client/src/js/modules/dc/views/reprocess.js +++ b/client/src/js/modules/dc/views/reprocess.js @@ -1,12 +1,22 @@ -define(['marionette', - 'backbone', - 'views/dialog', +define(['backbone', 'marionette', 'views/dialog', 'collections/datacollections', 'models/datacollection', 'modules/mc/views/dcdistl', - 'templates/dc/reprocess.html', 'templates/dc/reprocess_dc.html'], function(Marionette, Backbone, DialogView, + 'models/reprocessing', + 'models/reprocessingparameter', + 'collections/reprocessingparameters', + 'models/reprocessingimagesweep', + 'collections/reprocessingimagesweeps', + 'utils/kvcollection', + 'templates/dc/reprocess.html', 'templates/dc/reprocess_dc.html', 'collections/spacegroups' +], function(Backbone, Marionette, DialogView, DataCollections, DataCollection, DCDistlView, - template, dctemplate) { + Reprocessing, + ReprocessingParameter, ReprocessingParameters, + ReprocessingImageSweep, ReprocessingImageSweeps, + KVCollection, + template, dctemplate, Spacegroups + ) { var IDDataCollection = DataCollection.extend({ @@ -24,16 +34,20 @@ define(['marionette', ui: { cells: '.cells', - a: '.a', - b: '.bb', - c: '.c', - al: '.al', - be: '.be', - ga: '.ga', + a: 'input[name=CELL_A]', + b: 'input[name=CELL_B]', + c: 'input[name=CELL_C]', + al: 'input[name=CELL_ALPHA]', + be: 'input[name=CELL_BETA]', + ga: 'input[name=CELL_GAMMA]', cell: 'div.cell', st: 'input[name=start]', en: 'input[name=end]', ind: 'div.ind', + pipeline: 'select[name=pipeline]', + res: 'input[name=res]', + lowres: 'input[name=lowres]', + sg: 'input[name=SG]' }, events: { @@ -46,10 +60,49 @@ define(['marionette', 'keyup @ui.st': 'updateSelection', 'keyup @ui.en': 'updateSelection', 'click a.all': 'selectAll', + 'click a.apcell': '_setCell', + + 'change @ui.a': 'updateCell', + 'change @ui.b': 'updateCell', + 'change @ui.c': 'updateCell', + 'change @ui.al': 'updateCell', + 'change @ui.be': 'updateCell', + 'change @ui.ga': 'updateCell', + 'change @ui.sg': 'updateSG', + + 'change @ui.pipeline': 'updatePipeline', + 'change @ui.res': 'updateRes', + 'change @ui.lowres': 'updateLowRes', }, + + updateRes: function() { + this.model.set('RES', this.ui.res.val()) + }, + + updateLowRes: function() { + this.model.set('LOWRES', this.ui.lowres.val()) + }, + + updatePipeline: function() { + this.model.set('PIPELINE', this.ui.pipeline.val()) + }, + + updateSG: function() { + this.model.set('SG', this.ui.sg.val()) + }, + + + updateCell: function(e) { + var attr = $(e.target).attr('name') + var val = $(e.target).val() + + this.model.set(attr, val) + }, + + selectAll: function(e) { - e.preventDefault() + if (e) e.preventDefault() var si = parseInt(this.model.get('SI')) var ni = parseInt(this.model.get('NUMIMG')) @@ -63,13 +116,19 @@ define(['marionette', var si = parseInt(this.model.get('SI')) var ni = parseInt(this.model.get('NUMIMG')) - if (this.ui.st.val() > (si+ni)) this.ui.st.val(si+ni) - 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)) this.ui.en.val(si+ni) - 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) { @@ -79,15 +138,30 @@ define(['marionette', setInd: function(ind) { ind ? this.ui.ind.show() : this.ui.ind.hide() - ind ? this.$el.find('ul').removeClass('half') : this.$el.find('ul').addClass('half') }, onRender: function() { - // DCDistlView.prototype.onRender.apply(this, arguments) this.ui.cell.hide() this.ui.ind.hide() this.$el.find('ul').addClass('half') this.$el.find('li input[type="text"]').css('width', '25%') + this.$el.find('div input[type="text"]').css('width', '50px') + this.ui.pipeline.html(this.getOption('pipelines').opts()) + this.model.set('PIPELINE', this.ui.pipeline.val()) + + // check .distl element has been created before selecting all + const callback = (mutationList, observer) => { + for (const mutation of mutationList) { + for (const node of mutation.addedNodes) { + if (node.nodeType === Node.ELEMENT_NODE && node.querySelector('.distl')) { + this.selectAll() + observer.disconnect() + } + } + } + } + const observer = new MutationObserver(callback) + observer.observe(document.getElementById("dialog"), { childList: true, subtree: true }) }, toggleSG: function(e) { @@ -105,17 +179,21 @@ define(['marionette', this.trigger('clone:dc', this.model) }, - setCell: function() { + _setCell: function(e) { + if (e) e.preventDefault() + if (this.aps.length) { - var e = this.aps.at(0) - var c = e.get('CELL') - - this.ui.a.text(c['CELL_A']) - this.ui.b.text(c['CELL_B']) - this.ui.c.text(c['CELL_C']) - this.ui.al.text(c['CELL_AL']) - this.ui.be.text(c['CELL_BE']) - this.ui.ga.text(c['CELL_GA']) + const a = this.aps.at(0) + const c = a.get('CELL') + + this.ui.a.val(c['CELL_A']).trigger('change') + this.ui.b.val(c['CELL_B']).trigger('change') + this.ui.c.val(c['CELL_C']).trigger('change') + this.ui.al.val(c['CELL_AL']).trigger('change') + this.ui.be.val(c['CELL_BE']).trigger('change') + this.ui.ga.val(c['CELL_GA']).trigger('change') + + this.ui.sg.val(a.get('SG')).trigger('change') } }, @@ -135,8 +213,33 @@ define(['marionette', var DCDistlsView = Marionette.CollectionView.extend({ childView: DCDistlViewLarge, + childViewOptions: function() { + return { + pipelines: this.getOption('pipelines'), + spacegroups: this.getOption('spacegroups') + } + } }) + + var Pipelines = Backbone.Collection.extend(_.extend({ + keyAttribute: 'NAME', + valueAttribute: 'VALUE', + }, KVCollection)) + + + var IndexingMethods = Backbone.Collection.extend(_.extend({ + keyAttribute: 'NAME', + valueAttribute: 'VALUE', + }, KVCollection)) + + + var AbsorptionLevels = Backbone.Collection.extend(_.extend({ + keyAttribute: 'NAME', + valueAttribute: 'VALUE', + }, KVCollection)) + + return ReprocessView = DialogView.extend({ template: template, dialog: true, @@ -147,13 +250,23 @@ define(['marionette', dcr: '.dcs', }, - ui: { cell: 'div.cell', opts: 'div.options', ind: 'input[name=individual]', mul: 'span.multi', pipeline: 'select[name=pipeline]', + com: 'input[type=comments]', + sg: 'select[name=sg]', + sm: 'input[name=sm]', + indexingMethod: 'select[name=method]', + absorptionLevel: 'select[name=absorption_level]', + a: 'input[name=a]', + b: 'input[name=b]', + c: 'input[name=c]', + al: 'input[name=alpha]', + be: 'input[name=beta]', + ga: 'input[name=gamma]', }, buttons: { @@ -165,6 +278,9 @@ define(['marionette', 'click a.sgm': 'toggleCell', 'click a.opt': 'toggleOpts', 'click @ui.ind': 'toggleIndividual', + 'change @ui.pipeline': 'updatePipeline', + 'change @ui.indexingMethod': 'updateIndexingMethod', + 'click a.multicrystal': 'closeDialog', }, templateHelpers: function() { @@ -173,11 +289,44 @@ define(['marionette', } }, + updatePipeline: function() { + const isXia2 = this.ui.pipeline.val().startsWith("xia2") + this.ui.sm.prop('disabled', !isXia2) + for (param of this.xia2params()) { + this.$el.find('input[name='+param+'], select[name='+param+']').prop('disabled', !isXia2) + } + }, + + updateIndexingMethod: function() { + if (this.ui.indexingMethod.val() === 'real_space_grid_search') { + if (this.ui.ind.is(':checked')) { + let shouldAlert = true + this.distlview.children.each((v) => { + shouldAlert = this.checkIfUnitCellPopulated(v, shouldAlert) + }) + } else { + this.checkIfUnitCellPopulated(this, true) + } + } + }, + + checkIfUnitCellPopulated: function(v, shouldAlert) { + let cellPopulated = v.ui.a.val() && v.ui.b.val() && v.ui.c.val() && v.ui.al.val() && v.ui.be.val() && v.ui.ga.val() + if (!cellPopulated) { + v.ui.cell.show() + this.ui.indexingMethod.val('') + if (shouldAlert) app.alert({ message: 'Real Space Grid Search requires unit cell to be populated' }) + } + return cellPopulated + }, + toggleIndividual: function(e) { - var st = this.ui.ind.is(':checked') + const st = this.ui.ind.is(':checked') st ? this.ui.mul.hide() : this.ui.mul.show() + if (st) this.ui.cell.hide() this.distlview.children.each(function(v) { v.setInd(st) + if (!st) v.ui.cell.hide() }) }, @@ -186,45 +335,233 @@ define(['marionette', this.ui.cell.slideToggle() }, + showSpaceGroups: async function() { + this.spacegroups = new Spacegroups(null, { state: { pageSize: 9999 } }) + await this.spacegroups.fetch(); + this.ui.sg.html(''+this.spacegroups.opts()) + }, + toggleOpts: function(e) { e.preventDefault() this.ui.opts.slideToggle() }, + xia2params: function() { + return ['cc_half', 'isigma', 'misigma', 'sigma_strong', 'method', 'absorption_level'] + }, + integrate: function(e) { e.preventDefault() var s = this.collection.where({ selected: true }) if (!s.length) { - app.alert({ message: 'Please selected some data sets to integrate' }) + app.alert({ message: 'Please select some data sets to integrate' }) return } - var data = { visit: this.getOption('visit'), multi: 1 } - var integrate = [] - _.each(s, function(s, i) { - integrate.push([s.get('ID'), s.get('selection')[0], s.get('selection')[1]]) - }) + var self = this + var reqs = [] + // Integrate individually + if (this.ui.ind.is(':checked')) { + var jobs = 0 + + var rps = [] + _.each(s, function(sw) { + var p = this.pipelines.findWhere({ VALUE: sw.get('PIPELINE') }) + + var reprocessing = new Reprocessing({ + DATACOLLECTIONID: sw.get('ID'), + DISPLAYNAME: p.get('NAME'), + RECIPE: sw.get('PIPELINE'), + }) + + rps.push(reprocessing) + + reqs.push(reprocessing.save({}, { + success: function() { + var reprocessingparams = new ReprocessingParameters() + + if (sw.get('RES')) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: 'd_min', + PARAMETERVALUE: sw.get('RES') + })) + + if (sw.get('LOWRES')) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: 'd_max', + PARAMETERVALUE: sw.get('LOWRES') + })) + + var hascell = true + var cell = [] + _.each(['CELL_A', 'CELL_B', 'CELL_C', 'CELL_ALPHA', 'CELL_BETA', 'CELL_GAMMA'], function(f, i) { + var val = self.$el.find('input[name='+f+']').val().replace(/\s/g, '') + if (!val) hascell = false + else cell.push(val) + }) + + if (hascell) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: 'unit_cell', + PARAMETERVALUE: cell.join(',') + })) + + if (sw.get('SG')) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: 'spacegroup', + PARAMETERVALUE: sw.get('SG') + })) + + var sm = self.$el.find('input[name=sm]').is(':checked') + if (sm) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: 'small_molecule', + PARAMETERVALUE: 'true' + })) + + for (param of self.xia2params()) { + var val = self.$el.find('input[name='+param+'], select[name='+param+']').val().replace(/\s/g, '') + if (val) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: param, + PARAMETERVALUE: val + })) + } + + if (reprocessingparams.length) reqs.push(reprocessingparams.save()) + + var reprocessingsweep = new ReprocessingImageSweep({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + DATACOLLECTIONID: sw.get('ID'), + STARTIMAGE: sw.get('selection')[0], + ENDIMAGE: sw.get('selection')[1], + }) + reqs.push(reprocessingsweep.save()) + jobs++ + }, + + error: function() { + app.alert({ message: 'Something went wrong starting that reprocessing run' }) + } + })) + + }, this) + + $.when.apply($, reqs).done(function() { + app.message({ message: jobs+' reprocessing job(s) successfully submitted'}) + _.each(rps, function(rp) { + self._enqueue({ PROCESSINGJOBID: rp.get('PROCESSINGJOBID') }) + }) + }) + + + // Integrate sets togther + } else { + var p = this.pipelines.findWhere({ VALUE: self.ui.pipeline.val() }) + var reprocessing = new Reprocessing({ + DATACOLLECTIONID: s[0].get('ID'), + COMMENTS: this.ui.com.val(), + DISPLAYNAME: p.get('NAME'), + RECIPE: self.ui.pipeline.val(), + }) + + if (s.length > 1 && self.ui.pipeline.val() == 'fast_dp') { + app.alert({ message: 'Fast DP can only integrate a single sweep' }) + return + } - data.int = integrate - data.recipes = this.ui.pipeline.val() + reqs.push(reprocessing.save({}, { + success: function() { + var reprocessingparams = new ReprocessingParameters() + + var res = self.$el.find('input[name=res]').val() + if (res) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: 'd_min', + PARAMETERVALUE: res + })) + + var lowres = self.$el.find('input[name=lowres]').val() + if (lowres) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: 'd_max', + PARAMETERVALUE: lowres + })) + + var hascell = true + var cell = [] + _.each(['a', 'b', 'c', 'alpha', 'beta', 'gamma'], function(f, i) { + var val = self.$el.find('input[name='+f+']').val().replace(/\s/g, '') + if (!val) hascell = false + else cell.push(val) + }) + + if (hascell) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: 'unit_cell', + PARAMETERVALUE: cell.join(',') + })) + + var sg = self.$el.find('select[name=sg]').val().replace(/\s/g, '') + if (sg) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: 'spacegroup', + PARAMETERVALUE: sg + })) + + var sm = self.$el.find('input[name=sm]').is(':checked') + if (sm) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: 'small_molecule', + PARAMETERVALUE: 'true' + })) + + for (param of self.xia2params()) { + var val = self.$el.find('input[name='+param+'], select[name='+param+']').val().replace(/\s/g, '') + if (val) reprocessingparams.add(new ReprocessingParameter({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + PARAMETERKEY: param, + PARAMETERVALUE: val + })) + } + + if (reprocessingparams.length) reqs.push(reprocessingparams.save()) + + var sweeps = [] + _.each(s, function(sw) { + sweeps.push({ + PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), + DATACOLLECTIONID: sw.get('ID'), + STARTIMAGE: sw.get('selection')[0], + ENDIMAGE: sw.get('selection')[1], + }) + }) + var reprocessingsweeps = new ReprocessingImageSweeps(sweeps) + reqs.push(reprocessingsweeps.save()) + + $.when.apply($, reqs).done(function() { + app.message({ message: '1 reprocessing job successfully submitted'}) + self._enqueue({ PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID') }) + }) + }, + + error: function() { + app.alert({ message: 'Something went wrong starting that reprocessing run' }) + } + })) - _.each(['a', 'b', 'c', 'alpha', 'beta', 'gamma', 'res', 'sg'], function(f, i) { - data[f] = this.$el.find('input[name='+f+']').val().replace(/\s/g, '') - }, this) + } + }, - Backbone.ajax({ - url: app.apiurl+'/mc/integrate', - data: data, - type: 'POST', - success: function() { - app.alert({ message: s.length+' job(s) successfully submitted'}) + _enqueue: function(options) { + Backbone.ajax({ + url: app.apiurl+'/process/enqueue', + method: 'POST', + data: { + PROCESSINGJOBID: options.PROCESSINGJOBID }, - - error: function() { - app.alert({ message: 'Something went wrong submitting these jobs, please try again'}) - } }) }, @@ -239,23 +576,53 @@ define(['marionette', }, onRender: function() { - this.ui.opts.hide() + if (app.type != 'sm') this.ui.opts.hide() this.ui.cell.hide() + this.$el.find('span input[type="text"]').css('width', '55px') - _.each(['xia2 3dii', 'xia2 dials'], function(t,i) { - this.ui.pipeline.append('') - }, this) + const uspls = app.options.get('upstream_reprocessing_pipelines') + if (uspls) { + const pls = uspls[app.type] ?? uspls['default']; + this.pipelines = new Pipelines(pls) + this.ui.pipeline.html(this.pipelines.opts()) + } - this.distlview = new DCDistlsView({ collection: this.collection }) + this.indexingMethods = new IndexingMethods([ + { NAME: '-', VALUE: '' }, + { NAME: 'fft3d', VALUE: 'fft3d' }, + { NAME: 'fft1d', VALUE: 'fft1d' }, + { NAME: 'Real space grid search', VALUE: 'real_space_grid_search' }, + ]) + + this.ui.indexingMethod.html(this.indexingMethods.opts()) + + this.absorptionLevels = new AbsorptionLevels([ + { NAME: '-', VALUE: '' }, + { NAME: 'Low', VALUE: 'low' }, + { NAME: 'Medium', VALUE: 'medium' }, + { NAME: 'High', VALUE: 'high' }, + ]) + + this.ui.absorptionLevel.html(this.absorptionLevels.opts()) + + // asynchronously load space groups into the select menu + this.showSpaceGroups() + + this.distlview = new DCDistlsView({ collection: this.collection, pipelines: this.pipelines, spacegroups: this.spacegroups }) this.dcr.show(this.distlview) - this.listenTo(this.distlview, 'childview:set:cell', this.setCell, this) + // this.listenTo(this.distlview, 'childview:set:cell', this.setCell, this) this.listenTo(this.distlview, 'childview:clone:dc', this.cloneDC, this) + + if (app.type == 'sm') this.$el.find('input[name=sm]').prop('checked', true) + + }, cloneDC: function(e, model) { var nm = model.clone() nm.set('CID', this.collection.length+1) this.collection.add(nm) + this.toggleIndividual() }, setCell: function(view, ap) { @@ -264,7 +631,7 @@ define(['marionette', this.$el.find('input[name='+f+']').val(ap.get('CELL')['CELL_'+k.toUpperCase()]) }, this) - this.$el.find('input[name=sg]').val(ap.get('SG')) + this.$el.find('select[name=sg]').val(ap.get('SG')) }, }) diff --git a/client/src/js/modules/dc/views/reprocess2.js b/client/src/js/modules/dc/views/reprocess2.js deleted file mode 100644 index 6ee085e9a..000000000 --- a/client/src/js/modules/dc/views/reprocess2.js +++ /dev/null @@ -1,643 +0,0 @@ -define(['backbone', 'marionette', 'views/dialog', - 'collections/datacollections', - 'models/datacollection', - 'modules/mc/views/dcdistl', - 'models/reprocessing', - 'models/reprocessingparameter', - 'collections/reprocessingparameters', - 'models/reprocessingimagesweep', - 'collections/reprocessingimagesweeps', - 'utils/kvcollection', - 'templates/dc/reprocess.html', 'templates/dc/reprocess_dc.html', 'collections/spacegroups' -], function(Backbone, Marionette, DialogView, - DataCollections, DataCollection, DCDistlView, - Reprocessing, - ReprocessingParameter, ReprocessingParameters, - ReprocessingImageSweep, ReprocessingImageSweeps, - KVCollection, - template, dctemplate, Spacegroups - ) { - - - var IDDataCollection = DataCollection.extend({ - idAttribute: 'CID', - }) - - var IDDataCollections = DataCollections.extend({ - model: IDDataCollection, - }) - - var DCDistlViewLarge = DCDistlView.extend({ - template: dctemplate, - intStatus: false, - className: 'data_collection', - - ui: { - cells: '.cells', - a: 'input[name=CELL_A]', - b: 'input[name=CELL_B]', - c: 'input[name=CELL_C]', - al: 'input[name=CELL_ALPHA]', - be: 'input[name=CELL_BETA]', - ga: 'input[name=CELL_GAMMA]', - cell: 'div.cell', - st: 'input[name=start]', - en: 'input[name=end]', - ind: 'div.ind', - pipeline: 'select[name=pipeline]', - res: 'input[name=res]', - lowres: 'input[name=lowres]', - sg: 'input[name=SG]' - }, - - events: { - 'click .cells': 'sendCell', - 'click a.add': 'clone', - 'click a.rem': 'rem', - 'click a.sg': 'toggleSG', - 'change @ui.st': 'updateSelection', - 'change @ui.en': 'updateSelection', - 'keyup @ui.st': 'updateSelection', - 'keyup @ui.en': 'updateSelection', - 'click a.all': 'selectAll', - 'click a.apcell': '_setCell', - - 'change @ui.a': 'updateCell', - 'change @ui.b': 'updateCell', - 'change @ui.c': 'updateCell', - 'change @ui.al': 'updateCell', - 'change @ui.be': 'updateCell', - 'change @ui.ga': 'updateCell', - 'change @ui.sg': 'updateSG', - - 'change @ui.pipeline': 'updatePipeline', - 'change @ui.res': 'updateRes', - 'change @ui.lowres': 'updateLowRes', - }, - - - updateRes: function() { - this.model.set('RES', this.ui.res.val()) - }, - - updateLowRes: function() { - this.model.set('LOWRES', this.ui.lowres.val()) - }, - - updatePipeline: function() { - this.model.set('PIPELINE', this.ui.pipeline.val()) - }, - - updateSG: function() { - this.model.set('SG', this.ui.sg.val()) - }, - - - updateCell: function(e) { - var attr = $(e.target).attr('name') - var val = $(e.target).val() - - this.model.set(attr, val) - }, - - - selectAll: function(e) { - if (e) e.preventDefault() - - var si = parseInt(this.model.get('SI')) - var ni = parseInt(this.model.get('NUMIMG')) - - this.ui.st.val(si) - this.ui.en.val(si+ni-1) - this.updateSelection() - }, - - updateSelection: function() { - var si = parseInt(this.model.get('SI')) - var ni = parseInt(this.model.get('NUMIMG')) - - 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()) { - 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.st.val() && this.ui.en.val()) { - this.plotview.setSelection(parseInt(this.ui.st.val()), parseInt(this.ui.en.val())) - } - }, - - initialize: function(options) { - DCDistlView.prototype.initialize.apply(this, arguments) - this.updateSelection = _.debounce(this.updateSelection, 500) - }, - - setInd: function(ind) { - ind ? this.ui.ind.show() : this.ui.ind.hide() - }, - - onRender: function() { - this.ui.cell.hide() - this.ui.ind.hide() - this.$el.find('ul').addClass('half') - this.$el.find('li input[type="text"]').css('width', '25%') - this.$el.find('div input[type="text"]').css('width', '50px') - this.ui.pipeline.html(this.getOption('pipelines').opts()) - this.model.set('PIPELINE', this.ui.pipeline.val()) - - // check .distl element has been created before selecting all - const callback = (mutationList, observer) => { - for (const mutation of mutationList) { - for (const node of mutation.addedNodes) { - if (node.nodeType === Node.ELEMENT_NODE && node.querySelector('.distl')) { - this.selectAll() - observer.disconnect() - } - } - } - } - const observer = new MutationObserver(callback) - observer.observe(document.getElementById("dialog"), { childList: true, subtree: true }) - }, - - toggleSG: function(e) { - e.preventDefault() - this.ui.cell.slideToggle() - }, - - rem: function(e) { - e.preventDefault() - this.model.collection.remove(this.model) - }, - - clone: function(e) { - e.preventDefault() - this.trigger('clone:dc', this.model) - }, - - _setCell: function(e) { - if (e) e.preventDefault() - - if (this.aps.length) { - const a = this.aps.at(0) - const c = a.get('CELL') - - this.ui.a.val(c['CELL_A']).trigger('change') - this.ui.b.val(c['CELL_B']).trigger('change') - this.ui.c.val(c['CELL_C']).trigger('change') - this.ui.al.val(c['CELL_AL']).trigger('change') - this.ui.be.val(c['CELL_BE']).trigger('change') - this.ui.ga.val(c['CELL_GA']).trigger('change') - - this.ui.sg.val(a.get('SG')).trigger('change') - } - }, - - setSelection: function(x1, x2) { - DCDistlView.prototype.setSelection.apply(this, arguments) - this.ui.st.val(x1) - this.ui.en.val(x2) - }, - - setDeselection: function() { - DCDistlView.prototype.setDeselection.apply(this) - this.ui.st.val('') - this.ui.en.val('') - } - }) - - - var DCDistlsView = Marionette.CollectionView.extend({ - childView: DCDistlViewLarge, - childViewOptions: function() { - return { - pipelines: this.getOption('pipelines'), - spacegroups: this.getOption('spacegroups') - } - } - }) - - - var Pipelines = Backbone.Collection.extend(_.extend({ - keyAttribute: 'NAME', - valueAttribute: 'VALUE', - }, KVCollection)) - - - var IndexingMethods = Backbone.Collection.extend(_.extend({ - keyAttribute: 'NAME', - valueAttribute: 'VALUE', - }, KVCollection)) - - - var AbsorptionLevels = Backbone.Collection.extend(_.extend({ - keyAttribute: 'NAME', - valueAttribute: 'VALUE', - }, KVCollection)) - - - return ReprocessView = DialogView.extend({ - template: template, - dialog: true, - title: 'Reprocess Data', - className: 'rp content', - - regions: { - dcr: '.dcs', - }, - - ui: { - cell: 'div.cell', - opts: 'div.options', - ind: 'input[name=individual]', - mul: 'span.multi', - pipeline: 'select[name=pipeline]', - com: 'input[type=comments]', - sg: 'select[name=sg]', - sm: 'input[name=sm]', - indexingMethod: 'select[name=method]', - absorptionLevel: 'select[name=absorption_level]', - a: 'input[name=a]', - b: 'input[name=b]', - c: 'input[name=c]', - al: 'input[name=alpha]', - be: 'input[name=beta]', - ga: 'input[name=gamma]', - }, - - buttons: { - Integrate: 'integrate', - Close: 'closeDialog', - }, - - events: { - 'click a.sgm': 'toggleCell', - 'click a.opt': 'toggleOpts', - 'click @ui.ind': 'toggleIndividual', - 'change @ui.pipeline': 'updatePipeline', - 'change @ui.indexingMethod': 'updateIndexingMethod', - 'click a.multicrystal': 'closeDialog', - }, - - templateHelpers: function() { - return { - VISIT: this.getOption('visit') - } - }, - - updatePipeline: function() { - const isXia2 = this.ui.pipeline.val().startsWith("xia2") - this.ui.sm.prop('disabled', !isXia2) - for (param of this.xia2params()) { - this.$el.find('input[name='+param+'], select[name='+param+']').prop('disabled', !isXia2) - } - }, - - updateIndexingMethod: function() { - if (this.ui.indexingMethod.val() == 'real_space_grid_search') { - const st = this.ui.ind.is(':checked') - if (st) { - let eachHasCell = true - this.distlview.children.each(function(v) { - if (!v.ui.a.val() || !v.ui.b.val() || !v.ui.c.val() || - !v.ui.al.val() || !v.ui.be.val() || !v.ui.ga.val()) { - v.ui.cell.show() - eachHasCell = false - } - }) - if (!eachHasCell) { - app.alert({ message: 'Real Space Grid Search requires unit cell to be populated' }) - this.ui.indexingMethod.val('') - } - } else { - if (!this.ui.a.val() || !this.ui.b.val() || !this.ui.c.val() || - !this.ui.al.val() || !this.ui.be.val() || !this.ui.ga.val()) { - app.alert({ message: 'Real Space Grid Search requires unit cell to be populated' }) - this.ui.indexingMethod.val('') - this.ui.cell.show() - } - } - } - }, - - toggleIndividual: function(e) { - const st = this.ui.ind.is(':checked') - st ? this.ui.mul.hide() : this.ui.mul.show() - if (st) this.ui.cell.hide() - this.distlview.children.each(function(v) { - v.setInd(st) - if (!st) v.ui.cell.hide() - }) - }, - - toggleCell: function(e) { - e.preventDefault() - this.ui.cell.slideToggle() - }, - - showSpaceGroups: async function() { - this.spacegroups = new Spacegroups(null, { state: { pageSize: 9999 } }) - await this.spacegroups.fetch(); - this.ui.sg.html(''+this.spacegroups.opts()) - }, - - toggleOpts: function(e) { - e.preventDefault() - this.ui.opts.slideToggle() - }, - - xia2params: function() { - return ['cc_half', 'isigma', 'misigma', 'sigma_strong', 'method', 'absorption_level'] - }, - - integrate: function(e) { - e.preventDefault() - var s = this.collection.where({ selected: true }) - - if (!s.length) { - app.alert({ message: 'Please select some data sets to integrate' }) - return - } - - var self = this - var reqs = [] - // Integrate individually - if (this.ui.ind.is(':checked')) { - var jobs = 0 - - var rps = [] - _.each(s, function(sw) { - var p = this.pipelines.findWhere({ VALUE: sw.get('PIPELINE') }) - - var reprocessing = new Reprocessing({ - DATACOLLECTIONID: sw.get('ID'), - DISPLAYNAME: p.get('NAME'), - RECIPE: sw.get('PIPELINE'), - }) - - rps.push(reprocessing) - - reqs.push(reprocessing.save({}, { - success: function() { - var reprocessingparams = new ReprocessingParameters() - - if (sw.get('RES')) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: 'd_min', - PARAMETERVALUE: sw.get('RES') - })) - - if (sw.get('LOWRES')) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: 'd_max', - PARAMETERVALUE: sw.get('LOWRES') - })) - - var hascell = true - var cell = [] - _.each(['CELL_A', 'CELL_B', 'CELL_C', 'CELL_ALPHA', 'CELL_BETA', 'CELL_GAMMA'], function(f, i) { - var val = self.$el.find('input[name='+f+']').val().replace(/\s/g, '') - if (!val) hascell = false - else cell.push(val) - }) - - if (hascell) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: 'unit_cell', - PARAMETERVALUE: cell.join(',') - })) - - if (sw.get('SG')) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: 'spacegroup', - PARAMETERVALUE: sw.get('SG') - })) - - var sm = self.$el.find('input[name=sm]').is(':checked') - if (sm) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: 'small_molecule', - PARAMETERVALUE: 'true' - })) - - for (param of self.xia2params()) { - var val = self.$el.find('input[name='+param+'], select[name='+param+']').val().replace(/\s/g, '') - if (val) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: param, - PARAMETERVALUE: val - })) - } - - if (reprocessingparams.length) reqs.push(reprocessingparams.save()) - - var reprocessingsweep = new ReprocessingImageSweep({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - DATACOLLECTIONID: sw.get('ID'), - STARTIMAGE: sw.get('selection')[0], - ENDIMAGE: sw.get('selection')[1], - }) - reqs.push(reprocessingsweep.save()) - jobs++ - }, - - error: function() { - app.alert({ message: 'Something went wrong starting that reprocessing run' }) - } - })) - - }, this) - - $.when.apply($, reqs).done(function() { - app.message({ message: jobs+' reprocessing job(s) successfully submitted'}) - _.each(rps, function(rp) { - self._enqueue({ PROCESSINGJOBID: rp.get('PROCESSINGJOBID') }) - }) - }) - - - // Integrate sets togther - } else { - var p = this.pipelines.findWhere({ VALUE: self.ui.pipeline.val() }) - var reprocessing = new Reprocessing({ - DATACOLLECTIONID: s[0].get('ID'), - COMMENTS: this.ui.com.val(), - DISPLAYNAME: p.get('NAME'), - RECIPE: self.ui.pipeline.val(), - }) - - if (s.length > 1 && self.ui.pipeline.val() == 'fast_dp') { - app.alert({ message: 'Fast DP can only integrate a single sweep' }) - return - } - - reqs.push(reprocessing.save({}, { - success: function() { - var reprocessingparams = new ReprocessingParameters() - - var res = self.$el.find('input[name=res]').val() - if (res) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: 'd_min', - PARAMETERVALUE: res - })) - - var lowres = self.$el.find('input[name=lowres]').val() - if (lowres) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: 'd_max', - PARAMETERVALUE: lowres - })) - - var hascell = true - var cell = [] - _.each(['a', 'b', 'c', 'alpha', 'beta', 'gamma'], function(f, i) { - var val = self.$el.find('input[name='+f+']').val().replace(/\s/g, '') - if (!val) hascell = false - else cell.push(val) - }) - - if (hascell) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: 'unit_cell', - PARAMETERVALUE: cell.join(',') - })) - - var sg = self.$el.find('select[name=sg]').val().replace(/\s/g, '') - if (sg) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: 'spacegroup', - PARAMETERVALUE: sg - })) - - var sm = self.$el.find('input[name=sm]').is(':checked') - if (sm) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: 'small_molecule', - PARAMETERVALUE: 'true' - })) - - for (param of self.xia2params()) { - var val = self.$el.find('input[name='+param+'], select[name='+param+']').val().replace(/\s/g, '') - if (val) reprocessingparams.add(new ReprocessingParameter({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - PARAMETERKEY: param, - PARAMETERVALUE: val - })) - } - - if (reprocessingparams.length) reqs.push(reprocessingparams.save()) - - var sweeps = [] - _.each(s, function(sw) { - sweeps.push({ - PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID'), - DATACOLLECTIONID: sw.get('ID'), - STARTIMAGE: sw.get('selection')[0], - ENDIMAGE: sw.get('selection')[1], - }) - }) - var reprocessingsweeps = new ReprocessingImageSweeps(sweeps) - reqs.push(reprocessingsweeps.save()) - - $.when.apply($, reqs).done(function() { - app.message({ message: '1 reprocessing job successfully submitted'}) - self._enqueue({ PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID') }) - }) - }, - - error: function() { - app.alert({ message: 'Something went wrong starting that reprocessing run' }) - } - })) - - } - }, - - - _enqueue: function(options) { - Backbone.ajax({ - url: app.apiurl+'/process/enqueue', - method: 'POST', - data: { - PROCESSINGJOBID: options.PROCESSINGJOBID - }, - }) - }, - - - initialize: function(options) { - this.collection = new IDDataCollections() - if (options.model) { - var nm = new IDDataCollection(options.model.toJSON()) - nm.set('CID', this.collection.length+1) - this.collection.add(nm) - } - }, - - onRender: function() { - if (app.type != 'sm') this.ui.opts.hide() - this.ui.cell.hide() - this.$el.find('span input[type="text"]').css('width', '55px') - - const uspls = app.options.get('upstream_reprocessing_pipelines') - if (uspls) { - const pls = uspls[app.type] ?? uspls['default']; - this.pipelines = new Pipelines(pls) - this.ui.pipeline.html(this.pipelines.opts()) - } - - this.indexingMethods = new IndexingMethods([ - { NAME: '-', VALUE: '' }, - { NAME: 'fft3d', VALUE: 'fft3d' }, - { NAME: 'fft1d', VALUE: 'fft1d' }, - { NAME: 'Real space grid search', VALUE: 'real_space_grid_search' }, - ]) - - this.ui.indexingMethod.html(this.indexingMethods.opts()) - - this.absorptionLevels = new AbsorptionLevels([ - { NAME: '-', VALUE: '' }, - { NAME: 'Low', VALUE: 'low' }, - { NAME: 'Medium', VALUE: 'medium' }, - { NAME: 'High', VALUE: 'high' }, - ]) - - this.ui.absorptionLevel.html(this.absorptionLevels.opts()) - - // asynchronously load space groups into the select menu - this.showSpaceGroups() - - this.distlview = new DCDistlsView({ collection: this.collection, pipelines: this.pipelines, spacegroups: this.spacegroups }) - this.dcr.show(this.distlview) - // this.listenTo(this.distlview, 'childview:set:cell', this.setCell, this) - this.listenTo(this.distlview, 'childview:clone:dc', this.cloneDC, this) - - if (app.type == 'sm') this.$el.find('input[name=sm]').prop('checked', true) - - - }, - - cloneDC: function(e, model) { - var nm = model.clone() - nm.set('CID', this.collection.length+1) - this.collection.add(nm) - this.toggleIndividual() - }, - - setCell: function(view, ap) { - console.log('set cell main view', view, ap) - _.each({'a': 'a', 'b': 'b', 'c': 'c', 'alpha': 'al', 'beta': 'be', 'gamma': 'ga'}, function(k,f) { - this.$el.find('input[name='+f+']').val(ap.get('CELL')['CELL_'+k.toUpperCase()]) - }, this) - - this.$el.find('select[name=sg]').val(ap.get('SG')) - }, - - }) - -}) diff --git a/client/src/js/modules/shipment/views/containerplate.js b/client/src/js/modules/shipment/views/containerplate.js index b01c6c898..817f15e85 100644 --- a/client/src/js/modules/shipment/views/containerplate.js +++ b/client/src/js/modules/shipment/views/containerplate.js @@ -31,7 +31,7 @@ define(['marionette', 'collections/datacollections', 'modules/dc/views/getdcview', - 'modules/dc/views/reprocess2', + 'modules/dc/views/reprocess', 'views/dialog', 'modules/imaging/collections/autoscoreschemas',