diff --git a/api/src/Database/Type/MySQL.php b/api/src/Database/Type/MySQL.php
index fda8c2ecb..4a9103588 100644
--- a/api/src/Database/Type/MySQL.php
+++ b/api/src/Database/Type/MySQL.php
@@ -135,6 +135,10 @@ class MySQL extends DatabaseParent {
'BLSampleImageAutoScoreClass',
'BLSampleImage_has_AutoScoreClass',
+ 'Ligand',
+ 'BLSample_has_Ligand',
+ 'Ligand_has_PDB',
+
// Queuing
'ContainerQueueSample',
'ContainerQueue',
diff --git a/api/src/Page/DC.php b/api/src/Page/DC.php
index 945d59a95..b3e0d4d22 100644
--- a/api/src/Page/DC.php
+++ b/api/src/Page/DC.php
@@ -19,6 +19,7 @@ class DC extends Page
'aid' => '\d+',
'pjid' => '\d+',
'pid' => '\d+',
+ 'lid' => '\d+',
'h' => '\d\d',
'dmy' => '\d\d\d\d\d\d\d\d',
'ssid' => '\d+',
@@ -259,6 +260,16 @@ function _data_collections($single = null)
array_push($args, $this->arg('pid'));
}
+ # Ligands
+ } else if ($this->has_arg('lid')) {
+ $info = $this->db->pq("SELECT ligandid FROM ligand l WHERE l.ligandid=:1", array($this->arg('lid')));
+
+ foreach (array('dc', 'es', 'r', 'xrf') as $i => $t) {
+ $extj[$i] .= " INNER JOIN blsample_has_ligand bhl ON bhl.blsampleid = smp.blsampleid";
+ $sess[$i] = 'bhl.ligandid=:' . (sizeof($args) + 1);
+ array_push($args, $this->arg('lid'));
+ }
+
# Processing job
} else if ($this->has_arg('PROCESSINGJOBID')) {
$info = $this->db->pq('SELECT processingjobid
@@ -1264,7 +1275,16 @@ function _dc_strategies($id)
{
global $strat_align;
- $rows = $this->db->pq("SELECT s.programversion, st.rankingresolution as rankres, ssw.wedgenumber, sssw.subwedgenumber, ssw.chi, ssw.kappa, ssw.phi, dc.datacollectionid as dcid, s.comments, dc.transmission as dctrn, dc.wavelength as lam, dc.imagedirectory imd, dc.imageprefix as imp, dc.comments as dcc, dc.blsampleid as sid, sl.spacegroup as sg, sl.unitcell_a as a, sl.unitcell_b as b, sl.unitcell_c as c, sl.unitcell_alpha as al, sl.unitcell_beta as be, sl.unitcell_gamma as ga, CONCAT(CONCAT(IF(sssw.comments, sssw.comments, IF(ssw.comments, ssw.comments, s.shortcomments)), ' Wedge'), IFNULL(ssw.wedgenumber, '')) as com, sssw.axisstart as st, sssw.exposuretime as time, sssw.transmission as tran, sssw.oscillationrange as oscran, sssw.resolution as res, sssw.numberofimages as nimg, det.numberofpixelsx, det.detectorpixelsizehorizontal, sssw.rotationaxis
+ $rows = $this->db->pq("SELECT s.programversion, s.comments,
+ st.rankingresolution as rankres,
+ ssw.wedgenumber, ssw.chi, ssw.kappa, ssw.phi, ssw.comments as sswcomments,
+ sssw.subwedgenumber, sssw.axisstart as st, sssw.exposuretime as time, sssw.transmission as tran,
+ sssw.oscillationrange as oscran, sssw.resolution as res, sssw.numberofimages as nimg, sssw.rotationaxis,
+ dc.datacollectionid as dcid, dc.transmission as dctrn, dc.wavelength as lam, dc.imagedirectory imd,
+ dc.imageprefix as imp, dc.comments as dcc, dc.blsampleid as sid,
+ sl.spacegroup as sg, sl.unitcell_a as a, sl.unitcell_b as b, sl.unitcell_c as c, sl.unitcell_alpha as al, sl.unitcell_beta as be, sl.unitcell_gamma as ga,
+ det.numberofpixelsx, det.detectorpixelsizehorizontal,
+ CONCAT(IF(sssw.comments, sssw.comments, IF(ssw.comments, ssw.comments, s.shortcomments)), ' Wedge', IFNULL(ssw.wedgenumber, '')) as com
FROM screeningstrategy st
INNER JOIN screeningoutput so on st.screeningoutputid = so.screeningoutputid
INNER JOIN screening s on so.screeningid = s.screeningid
@@ -1292,6 +1312,11 @@ function _dc_strategies($id)
}
if ($is_align) {
+ foreach ($r as $k => &$v) {
+ if (in_array($k, $nf)) {
+ $v = number_format(floatval($v), 2);
+ }
+ }
array_push($output[$r['PROGRAMVERSION']]['STRATS'], $r);
} else {
diff --git a/api/src/Page/Processing.php b/api/src/Page/Processing.php
index 5dd43b1b9..4abff179b 100644
--- a/api/src/Page/Processing.php
+++ b/api/src/Page/Processing.php
@@ -148,22 +148,27 @@ function _screening_status($where, $ids) {
INNER JOIN datacollectiongroup dcg on dc.datacollectiongroupid = dcg.datacollectiongroupid
INNER JOIN blsession s ON s.sessionid = dcg.sessionid
INNER JOIN proposal p ON p.proposalid = s.proposalid
- WHERE $where
- GROUP BY dc.datacollectionid, sc.programversion",
+ WHERE sc.autoprocprogramid is null AND $where",
$ids
);
$statuses = array();
- foreach ($screenings as $screening) {
- if (!array_key_exists($screening['DATACOLLECTIONID'], $statuses)) {
- $statuses[$screening['DATACOLLECTIONID']][
- 'screening'
- ] = array();
+ foreach ($screenings as $s) {
+ if (!array_key_exists($s['DATACOLLECTIONID'], $statuses)) {
+ $statuses[$s['DATACOLLECTIONID']] = array();
}
-
- $statuses[$screening['DATACOLLECTIONID']]['screening'][
- $screening['PROGRAMVERSION']
- ] = $this->_map_status($screening["INDEXINGSUCCESS"]);
+ if (!array_key_exists('screening', $statuses[$s['DATACOLLECTIONID']])
+ ) {
+ $statuses[$s['DATACOLLECTIONID']]['screening'] = array();
+ }
+ if (!array_key_exists($s['PROGRAMVERSION'], $statuses[$s['DATACOLLECTIONID']]['screening'])
+ ) {
+ $statuses[$s['DATACOLLECTIONID']]['screening'][$s['PROGRAMVERSION']] = array();
+ }
+ array_push(
+ $statuses[$s['DATACOLLECTIONID']]['screening'][$s['PROGRAMVERSION']],
+ $this->_map_status($s['INDEXINGSUCCESS'])
+ );
}
return $statuses;
@@ -204,8 +209,9 @@ function _xrc_status($where, $ids) {
}
function _autoproc_status($where, $ids) {
- global $downstream_filter;
+ global $downstream_filter, $strat_align;
$filter = $downstream_filter ? implode("','", $downstream_filter) : '';
+ $screening_filter = $strat_align ? implode("','", $strat_align) : '';
$processings = $this->db->union(
array(
@@ -216,7 +222,9 @@ function _autoproc_status($where, $ids) {
INNER JOIN proposal p ON p.proposalid = s.proposalid
INNER JOIN autoprocintegration api ON api.datacollectionid = dc.datacollectionid
INNER JOIN autoprocprogram app ON api.autoprocprogramid = app.autoprocprogramid
- WHERE $where AND app.processingprograms NOT IN ('$filter')",
+ WHERE $where
+ AND app.processingprograms NOT IN ('$filter')
+ AND app.processingprograms NOT IN ('$screening_filter')",
"SELECT app.autoprocprogramid, dc.datacollectionid, app.processingprograms, app.processingstatus as status, 'downstream' as type
FROM datacollection dc
INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid
@@ -225,8 +233,22 @@ function _autoproc_status($where, $ids) {
INNER JOIN processingjob pj ON pj.datacollectionid = dc.datacollectionid
INNER JOIN autoprocprogram app ON pj.processingjobid = app.processingjobid
LEFT OUTER JOIN autoprocintegration api ON api.autoprocprogramid = app.autoprocprogramid
- WHERE $where AND api.autoprocintegrationid IS NULL
- AND app.processingprograms NOT IN ('$filter')",
+ WHERE $where
+ AND api.autoprocintegrationid IS NULL
+ AND app.processingprograms NOT IN ('$filter')
+ AND app.processingprograms NOT IN ('$screening_filter')",
+ "SELECT app.autoprocprogramid, dc.datacollectionid, app.processingprograms, app.processingstatus as status, 'screening' as type
+ 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 pj ON pj.datacollectionid = dc.datacollectionid
+ INNER JOIN autoprocprogram app ON pj.processingjobid = app.processingjobid
+ LEFT OUTER JOIN autoprocintegration api ON api.autoprocprogramid = app.autoprocprogramid
+ WHERE $where
+ AND api.autoprocintegrationid IS NULL
+ AND app.processingprograms NOT IN ('$filter')
+ AND app.processingprograms IN ('$screening_filter')",
),
$ids
);
@@ -280,6 +302,30 @@ function _get_ids() {
return array($where, $ids);
}
+ function merge_deep_arrays($a, $b) {
+ foreach ($b as $key => $value) {
+ if (isset($a[$key])) {
+ if (is_array($a[$key]) && is_array($value)) {
+ // Determine if arrays are associative or numeric
+ if (array_keys($a[$key]) === range(0, count($a[$key]) - 1) &&
+ array_keys($value) === range(0, count($value) - 1)) {
+ // Both are numeric arrays - merge them
+ $a[$key] = array_merge($a[$key], $value);
+ } else {
+ // At least one is associative - merge recursively
+ $a[$key] = $this->merge_deep_arrays($a[$key], $value);
+ }
+ } else {
+ // One is not array, overwrite with $b's value
+ $a[$key] = $value;
+ }
+ } else {
+ $a[$key] = $value;
+ }
+ }
+ return $a;
+ }
+
/**
* All screening, auto processing, and downstream statuses
*/
@@ -308,7 +354,8 @@ function _statuses() {
$xrcs = $this->_xrc_status($where, $ids);
$autoprocs = $this->_autoproc_status($where, $ids);
- $statuses = array_replace_recursive($screenings, $xrcs, $autoprocs);
+ $combined = $this->merge_deep_arrays($screenings, $autoprocs);
+ $statuses = array_replace_recursive($xrcs, $combined);
$out = array();
foreach ($ids as $id) {
@@ -667,8 +714,9 @@ function _ap_message() {
}
function _get_downstreams($dcid = null, $aid = null) {
- global $downstream_filter;
+ global $downstream_filter, $strat_align;
$filter = $downstream_filter ? implode("','", $downstream_filter) : '';
+ $screening_filter = $strat_align ? implode("','", $strat_align) : '';
$where = '';
$args = array($this->proposalid);
@@ -700,6 +748,7 @@ function _get_downstreams($dcid = null, $aid = null) {
INNER JOIN proposal p ON p.proposalid = s.proposalid
WHERE api.autoprocintegrationid IS NULL AND p.proposalid=:1 $where
AND app.processingprograms NOT IN ('$filter')
+ AND app.processingprograms NOT IN ('$screening_filter')
GROUP BY app.autoprocprogramid",
$args
);
diff --git a/api/src/Page/Proposal.php b/api/src/Page/Proposal.php
index 8cb157cf8..bfe8ed10f 100644
--- a/api/src/Page/Proposal.php
+++ b/api/src/Page/Proposal.php
@@ -37,6 +37,7 @@ class Proposal extends Page
'SHIPPINGID' => '\d+',
'LABCONTACTID' => '\d+',
'DATACOLLECTIONID' => '\d+',
+ 'LIGANDID' => '\d+',
// proposal
'PROPOSALCODE' => '\w+',
@@ -900,6 +901,7 @@ function _lookup()
'SHIPPINGID' => 'sh.shippingid',
'LABCONTACTID' => 'lc.labcontactid',
'DATACOLLECTIONID' => 'dc.datacollectionid',
+ 'LIGANDID' => 'l.ligandid',
);
$field = null;
@@ -958,6 +960,7 @@ function _lookup()
LEFT OUTER JOIN container c ON c.dewarid = d.dewarid
LEFT OUTER JOIN labcontact lc ON lc.proposalid = p.proposalid
+ LEFT OUTER JOIN ligand l ON l.proposalid = p.proposalid
$where
", $args);
diff --git a/api/src/Page/Sample.php b/api/src/Page/Sample.php
index 69b58bfbe..074eede3f 100644
--- a/api/src/Page/Sample.php
+++ b/api/src/Page/Sample.php
@@ -93,6 +93,11 @@ class Sample extends Page
'BLSAMPLETYPEID' => '\d+',
'scid' => '\d+-\d+',
+ 'LIBRARYNAME' => '^[a-zA-Z0-9\-_\.]+$',
+ 'LIBRARYBATCHNUMBER' => '^[a-zA-Z0-9\-_\.]+$',
+ 'PLATEBARCODE' => '^[a-zA-Z0-9\-_\.]+$',
+ 'SOURCEWELL' => '^[a-zA-Z0-9\-_\.]+$',
+
'BLSAMPLEID' => '\d+',
'X' => '\d*(.\d+)?',
'Y' => '\d*(.\d+)?',
@@ -191,13 +196,19 @@ class Sample extends Page
array('/proteins/lattice', 'post', '_add_protein_lattice'),
array('/proteins/lattice/:lid', 'patch', '_update_protein_lattice'),
+ array('/ligands(/:lid)', 'get', '_ligands'),
+ array('/ligands', 'post', '_add_ligand'),
+ array('/ligands/:lid', 'patch', '_update_ligand'),
+ array('/ligands/add', 'post', '_add_sample_ligand'),
+ array('/ligands/:sid/lid/:lid', 'delete', '_remove_sample_ligand'),
+
array('/crystals(/:CRYSTALID)', 'get', '_crystals'),
array('/crystals', 'post', '_add_crystal'),
array('/crystals/:CRYSTALID', 'patch', '_update_crystal'),
- array('/pdbs(/pid/:pid)', 'get', '_get_pdbs'),
+ array('/pdbs(/pid/:pid)(/lid/:lid)', 'get', '_get_pdbs'),
array('/pdbs', 'post', '_add_pdb'),
- array('/pdbs(/:pdbid)', 'delete', '_remove_pdb'),
+ array('/pdbs(/:pdbid)(/lid/:lid)', 'delete', '_remove_pdb'),
array('/pdbs/download/:pdbid', 'get', '_download_pdb'),
array('/concentrationtypes', 'get', '_concentration_types'),
@@ -504,7 +515,7 @@ function _add_simple_sample()
if ($info['extension'] == 'pdb' || $info['extension'] == 'cif') {
$file = file_get_contents($_FILES[$fileRef]['tmp_name']);
- $this->_associate_pdb($info['basename'], $file, '', $ids[$model]['PHASEID']);
+ $this->_associate_pdb($info['basename'], $file, '', $ids[$model]['PHASEID'], null);
}
$fileCount++;
}
@@ -1105,6 +1116,12 @@ function _samples()
array_push($args, $this->arg('lt'));
}
+ # For a ligand
+ if ($this->has_arg('lid')) {
+ $where .= ' AND bhl.ligandid LIKE :' . (sizeof($args) + 1);
+ array_push($args, $this->arg('lid'));
+ }
+
# For a visit
if ($this->has_arg('visit')) {
@@ -1191,6 +1208,7 @@ function _samples()
INNER JOIN dewar d ON d.dewarid = c.dewarid
LEFT OUTER JOIN datacollection dc ON b.blsampleid = dc.blsampleid
LEFT OUTER JOIN robotaction r ON r.blsampleid = b.blsampleid AND r.actiontype = 'LOAD'
+ LEFT OUTER JOIN blsample_has_ligand bhl ON bhl.blsampleid=b.blsampleid
$join WHERE $where", $args);
$tot = intval($tot[0]['TOT']);
@@ -1219,7 +1237,7 @@ function _samples()
if ($this->has_arg('sort_by')) {
- $cols = array('SAMPLEID' => 'b.blsampleid', 'NAME' => 'b.name', 'ACRONYM' => 'pr.acronym', 'SPACEGROUP' => 'cr.spacegroup', 'COMMENTS' => 'b.comments', 'SHIPMENT' => 'shipment', 'DEWAR' => 'dewar', 'CONTAINER' => 'container', 'b.blsampleid', 'SC' => 'sc', 'SCRESOLUTION' => 'scresolution', 'DC' => 'ap', 'DCRESOLUTION' => 'dcresolution', 'POSITION' => 'TO_NUMBER(b.location)', 'RECORDTIMESTAMP' => 'b.recordtimestamp');
+ $cols = array('SAMPLEID' => 'b.blsampleid', 'NAME' => 'b.name', 'ACRONYM' => 'pr.acronym', 'SPACEGROUP' => 'cr.spacegroup', 'COMMENTS' => 'b.comments', 'SHIPMENT' => 'shipment', 'DEWAR' => 'dewar', 'CONTAINER' => 'container', 'b.blsampleid', 'SC' => 'sc', 'SCRESOLUTION' => 'scresolution', 'DC' => 'dc', 'DCRESOLUTION' => 'dcresolution', 'POSITION' => 'TO_NUMBER(b.location)', 'RECORDTIMESTAMP' => 'b.recordtimestamp');
$dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC';
if (array_key_exists($this->arg('sort_by'), $cols))
$order = $cols[$this->arg('sort_by')] . ' ' . $dir;
@@ -1230,6 +1248,7 @@ function _samples()
, round(min(apss.resolutionlimithigh),2) as dcresolution, round(max(apss.completeness),1) as dccompleteness, dp.anomalousscatterer, dp.requiredresolution, cr.cell_a, cr.cell_b, cr.cell_c, cr.cell_alpha, cr.cell_beta, cr.cell_gamma, b.packingfraction, b.dimension1, b.dimension2, b.dimension3, b.shape, cr.color, cr.theoreticaldensity, cr.name as crystal, pr.name as protein, b.looptype, dp.centringmethod, dp.experimentkind, cq.containerqueueid
, TO_CHAR(cq.createdtimestamp, 'DD-MM-YYYY HH24:MI') as queuedtimestamp, b.smiles
, $cseq $sseq string_agg(cpr.name) as componentnames, string_agg(cpr.density) as componentdensities
+ , string_agg(distinct l.name) as ligandnames, string_agg(distinct l.ligandid) as ligandids
, string_agg(cpr.proteinid) as componentids, string_agg(cpr.acronym) as componentacronyms, string_agg(cpr.global) as componentglobals, string_agg(chc.abundance) as componentamounts, string_agg(ct.symbol) as componenttypesymbols, b.volume, pct.symbol,ROUND(cr.abundance,3) as abundance, TO_CHAR(b.recordtimestamp, 'DD-MM-YYYY') as recordtimestamp, dp.radiationsensitivity, dp.energy, dp.userpath, dp.strategyoption, dp.minimalresolution as minimumresolution
, count(distinct dc.dataCollectionId) as dcc, b.isinsamplechanger
@@ -1243,6 +1262,9 @@ function _samples()
LEFT OUTER JOIN protein cpr ON cpr.proteinid = chc.componentid
LEFT OUTER JOIN concentrationtype ct ON cpr.concentrationtypeid = ct.concentrationtypeid
+ LEFT OUTER JOIN blsample_has_ligand bhl ON bhl.blsampleid = b.blsampleid
+ LEFT OUTER JOIN ligand l ON l.ligandid = bhl.ligandid
+
INNER JOIN container c ON b.containerid = c.containerid
INNER JOIN dewar d ON d.dewarid = c.dewarid
INNER JOIN shipping s ON s.shippingid = d.shippingid
@@ -1282,7 +1304,7 @@ function _samples()
ORDER BY $order", $args);
foreach ($rows as &$r) {
- foreach (array('COMPONENTIDS', 'COMPONENTAMOUNTS', 'COMPONENTACRONYMS', 'COMPONENTTYPESYMBOLS', 'COMPONENTGLOBALS', 'COMPONENTNAMES', 'COMPONENTDENSITIES', 'COMPONENTSEQUENCES') as $k) {
+ foreach (array('COMPONENTIDS', 'COMPONENTAMOUNTS', 'COMPONENTACRONYMS', 'COMPONENTTYPESYMBOLS', 'COMPONENTGLOBALS', 'COMPONENTNAMES', 'COMPONENTDENSITIES', 'COMPONENTSEQUENCES', 'LIGANDNAMES', 'LIGANDIDS') as $k) {
if (array_key_exists($k, $r)) {
if ($r[$k])
$r[$k] = explode(',', $r[$k]);
@@ -1929,6 +1951,190 @@ function _update_protein()
}
+ # ------------------------------------------------------------------------
+ # List of ligands for a proposal
+ function _ligands()
+ {
+ if (!$this->has_arg('prop'))
+ $this->_error('No proposal specified');
+
+ $args = array($this->proposalid);
+ $where = 'l.proposalid=:1';
+
+ if ($this->has_arg('lid')) {
+ $where .= ' AND l.ligandid=:' . (sizeof($args) + 1);
+ array_push($args, $this->arg('lid'));
+ }
+
+ $tot = $this->db->pq("SELECT count(distinct l.ligandid) as tot FROM ligand l INNER JOIN proposal p ON p.proposalid = l.proposalid WHERE $where", $args);
+ $tot = intval($tot[0]['TOT']);
+
+ if ($this->has_arg('s')) {
+ $st = sizeof($args) + 1;
+ $where .= " AND l.name LIKE CONCAT('%',:" . $st . ", '%')";
+ array_push($args, $this->arg('s'));
+ }
+
+
+ $start = 0;
+ $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15;
+ $end = $pp;
+
+ if ($this->has_arg('page')) {
+ $pg = $this->arg('page') - 1;
+ $start = $pg * $pp;
+ $end = $pg * $pp + $pp;
+ }
+
+ array_push($args, $start);
+ array_push($args, $end);
+
+ $order = 'l.ligandid DESC';
+
+ $group = 'l.ligandid';
+
+ if ($this->has_arg('sort_by')) {
+ $cols = array(
+ 'NAME' => 'l.name',
+ );
+ $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC';
+ if (array_key_exists($this->arg('sort_by'), $cols))
+ $order = $cols[$this->arg('sort_by')] . ' ' . $dir;
+ }
+
+ $rows = $this->db->paginate("SELECT l.ligandid, l.name, l.smiles, l.libraryname, l.librarybatchnumber, l.platebarcode, l.sourcewell,
+ COUNT(DISTINCT dc.datacollectionid) AS dcount,
+ COUNT(DISTINCT b.blsampleid) AS scount
+ FROM ligand l
+ INNER JOIN proposal p ON p.proposalid = l.proposalid
+ LEFT OUTER JOIN ligand_has_pdb lhp ON lhp.ligandid = l.ligandid
+ LEFT OUTER JOIN blsample_has_ligand bhl ON bhl.ligandid = l.ligandid
+ LEFT OUTER JOIN blsample b ON b.blsampleid = bhl.blsampleid
+ LEFT OUTER JOIN datacollection dc ON b.blsampleid = dc.blsampleid
+ WHERE $where
+ GROUP BY $group
+ ORDER BY $order", $args);
+
+ if ($this->has_arg('lid')) {
+ if (sizeof($rows))
+ $this->_output($rows[0]);
+ else
+ $this->_error('No such ligand');
+ } else
+ $this->_output(array(
+ 'total' => $tot,
+ 'data' => $rows,
+ ));
+ }
+
+
+ function _add_sample_ligand()
+ {
+ if (!$this->has_arg('prop'))
+ $this->_error('No proposal specified');
+ if (!$this->has_arg('lid'))
+ $this->_error('No ligand id specified');
+ if (!$this->has_arg('sid'))
+ $this->_error('No sample id specified');
+
+ $chk = $this->db->pq("SELECT blsampleid, ligandid FROM blsample_has_ligand
+ WHERE blsampleid=:1 AND ligandid=:2", array($this->arg('sid'), $this->arg('lid')));
+ if (sizeof($chk))
+ $this->_error('That sample already has that ligand');
+
+ $this->db->pq(
+ 'INSERT INTO blsample_has_ligand (blsampleid,ligandid) VALUES (:1,:2)',
+ array($this->arg('sid'), $this->arg('lid'))
+ );
+
+ $this->_output(array('LIGANDID' => $this->arg('lid')));
+ }
+
+
+ function _remove_sample_ligand()
+ {
+ if (!$this->has_arg('prop'))
+ $this->_error('No proposal specified');
+
+ if (!$this->has_arg('sid'))
+ $this->_error('No blsampleid specified');
+
+ if (!$this->has_arg('lid'))
+ $this->_error('No ligandid specified');
+
+ $chk = $this->db->pq("SELECT bhl.blsampleid, bhl.ligandid
+ FROM blsample_has_ligand bhl
+ INNER JOIN ligand l ON l.ligandid = bhl.ligandid
+ WHERE l.proposalid=:1 AND bhl.blsampleid=:2 AND l.ligandid=:3", array($this->proposalid, $this->arg('sid'), $this->arg('lid')));
+
+ if (!sizeof($chk))
+ $this->_error('No such ligand or sample');
+
+ # Remove association
+ $this->db->pq("DELETE FROM blsample_has_ligand WHERE blsampleid=:1 and ligandid=:2", array($this->arg('sid'), $this->arg('lid')));
+
+ $this->_output(1);
+ }
+
+
+ function _add_ligand()
+ {
+ if (!$this->has_arg('prop'))
+ $this->_error('No proposal specified');
+ if (!$this->has_arg('NAME'))
+ $this->_error('No ligand name');
+
+ $smiles = $this->has_arg('SMILES') ? $this->arg('SMILES') : '';
+ $libname = $this->has_arg('LIBRARYNAME') ? $this->arg('LIBRARYNAME') : null;
+ $libbatch = $this->has_arg('LIBRARYBATCHNUMBER') ? $this->arg('LIBRARYBATCHNUMBER') : null;
+ $barcode = $this->has_arg('PLATEBARCODE') ? $this->arg('PLATEBARCODE') : null;
+ $well = $this->has_arg('SOURCEWELL') ? $this->arg('SOURCEWELL') : null;
+
+ $chk = $this->db->pq("SELECT name FROM ligand
+ WHERE proposalid=:1 AND name=:2", array($this->proposalid, $this->arg('NAME')));
+ if (sizeof($chk))
+ $this->_error('That ligand name already exists in this proposal');
+
+ $this->db->pq(
+ 'INSERT INTO ligand (proposalid,name,smiles,libraryname,librarybatchnumber,platebarcode,sourcewell)
+ VALUES (:1,:2,:3,:4,:5,:6,:7) RETURNING ligandid INTO :id',
+ array($this->proposalid, $this->arg('NAME'), $smiles, $libname, $libbatch, $barcode, $well)
+ );
+
+ $lid = $this->db->id();
+
+ $this->_output(array('LIGANDID' => $lid));
+ }
+
+ # ------------------------------------------------------------------------
+ # Update a particular field for a ligand
+ function _update_ligand()
+ {
+ if (!$this->has_arg('lid'))
+ $this->_error('No ligandid specified');
+
+ $lig = $this->db->pq("SELECT l.ligandid FROM ligand l
+ WHERE l.proposalid = :1 AND l.ligandid = :2", array($this->proposalid, $this->arg('lid')));
+
+ if (!sizeof($lig))
+ $this->_error('No such ligand');
+
+ if ($this->has_arg('NAME')) {
+ $chk = $this->db->pq("SELECT l.ligandid FROM ligand l
+ WHERE l.proposalid = :1 AND l.name = :2", array($this->proposalid, $this->arg('NAME')));
+ if (sizeof($chk)) $this->_error('That ligand name already exists in this proposal');
+ }
+
+ foreach (array('NAME', 'SMILES', 'LIBRARYNAME', 'LIBRARYBATCHNUMBER', 'PLATEBARCODE', 'SOURCEWELL') as $f) {
+ if ($this->has_arg($f)) {
+ $this->db->pq('UPDATE ligand SET ' . $f . '=:1 WHERE ligandid=:2', array($this->arg($f), $this->arg('lid')));
+ $this->_output(array($f => $this->arg($f)));
+ }
+ }
+ }
+
+
+
# ------------------------------------------------------------------------
# Update a particular field for a sample
function _update_sample()
@@ -2123,15 +2329,20 @@ function _get_pdbs()
if (!$this->has_arg('prop'))
$this->_error('No proposal specified');
- $where = 'pr.proposalid=:1';
- $args = array($this->proposalid);
+ $where = '(pr.proposalid=:1 or l.proposalid=:2)';
+ $args = array($this->proposalid, $this->proposalid);
+ $join = 'LEFT OUTER JOIN protein_has_pdb hp ON p.pdbid = hp.pdbid LEFT OUTER JOIN protein pr ON pr.proteinid = hp.proteinid
+ LEFT OUTER JOIN ligand_has_pdb lhp ON p.pdbid = lhp.pdbid LEFT OUTER JOIN ligand l ON l.ligandid = lhp.ligandid';
if ($this->has_arg('pid')) {
- $where .= ' AND pr.proteinid=:2';
+ $where .= ' AND pr.proteinid=:3';
array_push($args, $this->arg('pid'));
+ } else if ($this->has_arg('lid')) {
+ $where .= ' AND l.ligandid=:3';
+ array_push($args, $this->arg('lid'));
}
- $rows = $this->db->pq("SELECT distinct hp.proteinhaspdbid, p.pdbid,pr.proteinid, p.name,p.code FROM pdb p INNER JOIN protein_has_pdb hp ON p.pdbid = hp.pdbid INNER JOIN protein pr ON pr.proteinid = hp.proteinid WHERE $where ORDER BY p.pdbid DESC", $args);
+ $rows = $this->db->pq("SELECT distinct hp.proteinhaspdbid, p.pdbid, pr.proteinid, p.name, p.code, l.ligandid FROM pdb p $join WHERE $where ORDER BY p.pdbid DESC", $args);
$this->_output($rows);
}
@@ -2155,15 +2366,27 @@ function _download_pdb()
# Add a new pdb
function _add_pdb()
{
- if (!$this->has_arg('PROTEINID'))
- $this->_error('No protein id specified');
+ $proteinid = $this->has_arg('PROTEINID') ? $this->arg('PROTEINID') : null;
+ $ligandid = $this->has_arg('lid') ? $this->arg('lid') : null;
+ if ($proteinid) {
+ $prot = $this->db->pq("SELECT pr.proteinid
+ FROM protein pr
+ WHERE pr.proposalid = :1 AND pr.proteinid = :2", array($this->proposalid, $proteinid));
- $prot = $this->db->pq("SELECT pr.proteinid
- FROM protein pr
- WHERE pr.proposalid = :1 AND pr.proteinid = :2", array($this->proposalid, $this->arg('PROTEINID')));
+ if (!sizeof($prot))
+ $this->_error('No such protein');
- if (!sizeof($prot))
- $this->_error('No such protein');
+ } else if ($ligandid) {
+ $lig = $this->db->pq("SELECT l.ligandid
+ FROM ligand l
+ WHERE l.proposalid = :1 AND l.ligandid = :2", array($this->proposalid, $ligandid));
+
+ if (!sizeof($lig))
+ $this->_error('No such ligand');
+
+ } else {
+ $this->_error('No protein id or ligand id specified');
+ }
if (array_key_exists('pdb_file', $_FILES)) {
if ($_FILES['pdb_file']['name']) {
@@ -2171,34 +2394,47 @@ function _add_pdb()
if ($info['extension'] == 'pdb' || $info['extension'] == 'cif') {
$file = file_get_contents($_FILES['pdb_file']['tmp_name']);
- $this->_associate_pdb($info['basename'], $file, '', $this->arg('PROTEINID'));
+ $this->_associate_pdb($info['basename'], $file, '', $proteinid, $ligandid);
}
}
}
if ($this->has_arg('pdb_code')) {
- $this->_associate_pdb($this->arg('pdb_code'), '', $this->arg('pdb_code'), $this->arg('PROTEINID'));
+ $this->_associate_pdb($this->arg('pdb_code'), '', $this->arg('pdb_code'), $proteinid, $ligandid);
}
if ($this->has_arg('existing_pdb')) {
- $rows = $this->db->pq("SELECT p.pdbid FROM pdb p INNER JOIN protein_has_pdb hp ON p.pdbid = hp.pdbid INNER JOIN protein pr ON pr.proteinid = hp.proteinid WHERE pr.proposalid=:1 AND p.pdbid=:2", array($this->proposalid, $this->arg('existing_pdb')));
+ $rows = $this->db->pq("SELECT p.pdbid FROM pdb p
+ LEFT OUTER JOIN protein_has_pdb hp ON p.pdbid = hp.pdbid
+ LEFT OUTER JOIN protein pr ON pr.proteinid = hp.proteinid
+ LEFT OUTER JOIN ligand_has_pdb lhp ON p.pdbid = lhp.pdbid
+ LEFT OUTER JOIN ligand l ON l.ligandid = lhp.ligandid
+ WHERE (pr.proposalid=:1 OR l.proposalid=:2) AND p.pdbid=:3", array($this->proposalid, $this->proposalid, $this->arg('existing_pdb')));
if (!sizeof($rows))
$this->_error('The specified pdb doesnt exist');
- $this->db->pq("INSERT INTO protein_has_pdb (proteinhaspdbid,proteinid,pdbid) VALUES (s_protein_has_pdb.nextval,:1,:2)", array($this->arg('PROTEINID'), $this->arg('existing_pdb')));
+ if ($proteinid) {
+ $this->db->pq("INSERT INTO protein_has_pdb (proteinid,pdbid) VALUES (:1,:2)", array($proteinid, $this->arg('existing_pdb')));
+ } else if ($ligandid) {
+ $this->db->pq("INSERT INTO ligand_has_pdb (ligandid,pdbid) VALUES (:1,:2)", array($ligandid, $this->arg('existing_pdb')));
+ }
}
$this->_output(1);
}
# Duplication :(
- function _associate_pdb($name, $contents, $code, $pid)
+ function _associate_pdb($name, $contents, $code, $pid, $lid)
{
$this->db->pq("INSERT INTO pdb (pdbid,name,contents,code) VALUES(s_pdb.nextval,:1,:2,:3) RETURNING pdbid INTO :id", array($name, $contents, $code));
$pdbid = $this->db->id();
- $this->db->pq("INSERT INTO protein_has_pdb (proteinhaspdbid,proteinid,pdbid) VALUES (s_protein_has_pdb.nextval,:1,:2)", array($pid, $pdbid));
+ if ($pid) {
+ $this->db->pq("INSERT INTO protein_has_pdb (proteinid,pdbid) VALUES (:1,:2)", array($pid, $pdbid));
+ } else if ($lid) {
+ $this->db->pq("INSERT INTO ligand_has_pdb (ligandid,pdbid) VALUES (:1,:2)", array($lid, $pdbid));
+ }
}
# ------------------------------------------------------------------------
@@ -2207,15 +2443,22 @@ function _remove_pdb()
{
if (!$this->has_arg('prop'))
$this->_error('No proposal specified');
- #if (!$this->has_arg('pid')) $this->_error('No protein specified');
+
if (!$this->has_arg('pdbid'))
$this->_error('No pdb specified');
- $pdb = $this->db->pq("SELECT pd.pdbid
- FROM pdb pd
- INNER JOIN protein_has_pdb hp ON hp.pdbid=pd.pdbid
- INNER JOIN protein p ON p.proteinid = hp.proteinid
- WHERE p.proposalid=:1 AND hp.proteinhaspdbid=:2", array($this->proposalid, $this->arg('pdbid')));
+ if ($this->has_arg('lid')) {
+ $pdb = $this->db->pq("SELECT lhp.pdbid, lhp.ligandid
+ FROM ligand_has_pdb lhp
+ INNER JOIN ligand l ON l.ligandid = lhp.ligandid
+ WHERE l.proposalid=:1 AND lhp.pdbid=:2 AND l.ligandid=:3", array($this->proposalid, $this->arg('pdbid'), $this->arg('lid')));
+ } else {
+ $pdb = $this->db->pq("SELECT pd.pdbid
+ FROM pdb pd
+ INNER JOIN protein_has_pdb hp ON hp.pdbid=pd.pdbid
+ INNER JOIN protein p ON p.proteinid = hp.proteinid
+ WHERE p.proposalid=:1 AND hp.proteinhaspdbid=:2", array($this->proposalid, $this->arg('pdbid')));
+ }
if (!sizeof($pdb))
$this->_error('No such pdb');
@@ -2223,10 +2466,14 @@ function _remove_pdb()
$pdb = $pdb[0];
# Remove association
- $this->db->pq("DELETE FROM protein_has_pdb WHERE proteinhaspdbid=:1", array($this->arg('pdbid')));
+ if ($this->has_arg('lid')) {
+ $this->db->pq("DELETE FROM ligand_has_pdb WHERE pdbid=:1 and ligandid=:2", array($this->arg('pdbid'), $this->arg('lid')));
+ } else {
+ $this->db->pq("DELETE FROM protein_has_pdb WHERE proteinhaspdbid=:1", array($this->arg('pdbid')));
+ }
# Remove entry if its the last one
- $count = $this->db->pq("SELECT pdbid FROM protein_has_pdb WHERE pdbid=:1", array($pdb['PDBID']));
+ $count = $this->db->union(array("SELECT pdbid FROM protein_has_pdb WHERE pdbid=:1", "SELECT pdbid FROM ligand_has_pdb WHERE pdbid=:1"), array($pdb['PDBID']));
if (!sizeof($count))
$this->db->pq("DELETE FROM pdb WHERE pdbid=:1", array($pdb['PDBID']));
diff --git a/api/src/Page/Shipment.php b/api/src/Page/Shipment.php
index 9ae571535..5607eeac5 100644
--- a/api/src/Page/Shipment.php
+++ b/api/src/Page/Shipment.php
@@ -21,6 +21,7 @@ class Shipment extends Page
'sid' => '\d+',
'lcid' => '\d+',
'pid' => '\d+',
+ 'lid' => '\d+',
'iid' => '\d+',
@@ -2276,6 +2277,14 @@ function _get_all_containers()
array_push($args, $this->arg('pid'));
}
+ if ($this->has_arg('lid')) {
+ $join .= ' LEFT OUTER JOIN blsample_has_ligand bhl ON bhl.blsampleid = s.blsampleid';
+ $where .= ' AND bhl.ligandid=:' . (sizeof($args) + 1);
+ $totalQuery->joinClause("LEFT OUTER JOIN blsample s ON s.containerid = c.containerid");
+ $totalQuery->joinClause("LEFT OUTER JOIN blsample_has_ligand bhl ON bhl.blsampleid = s.blsampleid");
+ array_push($args, $this->arg('lid'));
+ }
+
if ($this->has_arg('assigned')) {
$where .= " AND d.dewarstatus LIKE 'processing' AND c.samplechangerlocation > 0";
$totalQuery->joinClause("INNER JOIN dewar d ON d.dewarid = c.dewarid");
diff --git a/client/src/js/collections/ligands.js b/client/src/js/collections/ligands.js
new file mode 100644
index 000000000..112a8dfa6
--- /dev/null
+++ b/client/src/js/collections/ligands.js
@@ -0,0 +1,58 @@
+define(['underscore', 'backbone.paginator', 'models/ligand'], function(_, PageableCollection, Ligand) {
+
+ return PageableCollection.extend({
+ model: Ligand,
+ mode: 'server',
+ url: '/sample/ligands',
+
+ state: {
+ pageSize: 15,
+ },
+
+ parseState: function(r, q, state, options) {
+ return { totalRecords: r.total }
+ },
+
+ parseRecords: function(r, options) {
+ return r.data
+ },
+
+ initialize: function(collection, options) {
+ this.fetched = false
+ this.on('sync', this.setFetched, this)
+ if (options && options.pmodel) {
+ this.pmodel = options.pmodel
+ this.listenTo(this, 'change add remove', this._update_ligand_ids)
+ this.listenTo(this.pmodel, 'sync', this._add_ligands)
+ this._add_ligands()
+ }
+ },
+
+ setFetched: function() {
+ if (this.fetched) return
+ this.fetched = true
+ this.trigger('reset')
+ },
+
+ _update_ligand_ids: function() {
+ var ligs = this.slice(0)
+ var flds = {
+ LIGANDIDS: _.map(ligs, function(m) { return m.get('LIGANDID') }),
+ LIGANDNAMES: _.map(ligs, function(m) { return m.get('NAME') }),
+ }
+ this.pmodel.set(flds)
+ },
+
+ _add_ligands: function() {
+ var ids = this.pmodel.get('LIGANDIDS') || []
+ var nas = this.pmodel.get('LIGANDNAMES') || []
+ var ligs = _.map(ids, function(id, i) {
+ return {
+ LIGANDID: id,
+ NAME: nas[i],
+ }
+ })
+ this.reset(ligs)
+ },
+ })
+})
diff --git a/client/src/js/models/ligand.js b/client/src/js/models/ligand.js
new file mode 100644
index 000000000..1dfdbae82
--- /dev/null
+++ b/client/src/js/models/ligand.js
@@ -0,0 +1,35 @@
+define(['backbone', 'markdown'], function(Backbone, markdown) {
+
+ return Backbone.Model.extend({
+ idAttribute: 'LIGANDID',
+ urlRoot: '/sample/ligands',
+
+ validation: {
+ NAME: {
+ required: true,
+ pattern: 'wwdash',
+ },
+ SMILES: {
+ required: false,
+ pattern: 'smiles'
+ },
+ LIBRARYNAME: {
+ required: false,
+ pattern: 'wwdash',
+ },
+ LIBRARYBATCHNUMBER: {
+ required: false,
+ pattern: 'wwdash',
+ },
+ PLATEBARCODE: {
+ required: false,
+ pattern: 'wwdash',
+ },
+ SOURCEWELL: {
+ required: false,
+ pattern: 'wwdash',
+ },
+ },
+ })
+
+})
diff --git a/client/src/js/models/sample.js b/client/src/js/models/sample.js
index e0267e61c..e3694f534 100644
--- a/client/src/js/models/sample.js
+++ b/client/src/js/models/sample.js
@@ -1,6 +1,6 @@
-define(['backbone', 'collections/components',
+define(['backbone', 'collections/components', 'collections/ligands',
'utils/experimentkinds',
- 'utils/radiationsensitivity'], function(Backbone, Components, EXP, RS) {
+ 'utils/radiationsensitivity'], function(Backbone, Components, Ligands, EXP, RS) {
return Backbone.Model.extend({
idAttribute: 'BLSAMPLEID',
@@ -10,6 +10,7 @@ define(['backbone', 'collections/components',
initialize: function(attrs, options) {
var addPrimary = (options && options.addPrimary) || (this.collection && this.collection.state.addPrimary)
this.set('components', new Components(null, { pmodel: this, addPrimary: addPrimary }))
+ this.set('ligands', new Ligands(null, { pmodel: this }))
this.updateScreeningOptions()
this.listenTo(this, 'change:EXPERIMENTKIND', this.updateExpKind)
@@ -105,6 +106,8 @@ define(['backbone', 'collections/components',
INITIALSAMPLEGROUP: '',
COMPONENTIDS: [],
COMPONENTAMOUNTS: [],
+ LIGANDIDS: [],
+ LIGANDNAMES: [],
X: null,
Y: null,
Z: null,
diff --git a/client/src/js/modules/contact/views/addcontact.js b/client/src/js/modules/contact/views/addcontact.js
index 25eca2a5d..45c5beb0e 100644
--- a/client/src/js/modules/contact/views/addcontact.js
+++ b/client/src/js/modules/contact/views/addcontact.js
@@ -68,6 +68,7 @@ define(['views/form',
this.countries = new Countries()
this.countries.state.pageSize = 9999
this.users = new Users()
+ this.users.state.pageSize = 9999
this.users.queryParams.login = 1
},
diff --git a/client/src/js/modules/dc/views/apstatusitem.js b/client/src/js/modules/dc/views/apstatusitem.js
index d479e1df5..e65ce5358 100644
--- a/client/src/js/modules/dc/views/apstatusitem.js
+++ b/client/src/js/modules/dc/views/apstatusitem.js
@@ -52,9 +52,16 @@ define(['marionette', 'jquery'], function(Marionette, $) {
if (this.getOption('showStrategies')) {
this.ui.strat.empty()
+ var allStrategies = []
_.each(res['screening'], function(sc, n) {
- this.ui.strat.append(n+': '+val[sc]+' ')
+ var strats = {}
+ _.each(sc, function(a) {
+ if (!(a in strats)) strats[a] = 0
+ strats[a]++
+ })
+ allStrategies.push(n+': '+_.map(strats, function(c, st) { return c > 1 ? ''+c+'x '+val[st] : val[st]}).join(' '))
}, this)
+ this.ui.strat.append(allStrategies.join('|'))
}
if (this.getOption('showProcessing')) {
diff --git a/client/src/js/modules/dc/views/autoindexing.js b/client/src/js/modules/dc/views/autoindexing.js
index 55d1c0513..ea4389c7c 100644
--- a/client/src/js/modules/dc/views/autoindexing.js
+++ b/client/src/js/modules/dc/views/autoindexing.js
@@ -8,7 +8,7 @@ define(['marionette',
modelEvents: { 'change': 'render' },
getTemplate: function(m) {
- return (this.model.get('TYPE') == 'XOalign' || this.model.get('TYPE') == 'dials.align_crystal') ? xotemplate : template
+ return (this.model.get('TYPE') == 'XOalign' || this.model.get('TYPE').includes('dials.align_crystal')) ? xotemplate : template
}
})
@@ -31,4 +31,4 @@ define(['marionette',
})
-})
\ No newline at end of file
+})
diff --git a/client/src/js/modules/samples/collections/pdbs.js b/client/src/js/modules/samples/collections/pdbs.js
index b1073d7b6..8d3f5122a 100644
--- a/client/src/js/modules/samples/collections/pdbs.js
+++ b/client/src/js/modules/samples/collections/pdbs.js
@@ -2,10 +2,13 @@ define(['backbone', 'modules/samples/models/pdb'], function(Backbone, PDB) {
return Backbone.Collection.extend({
model: PDB,
- url: function() { return '/sample/pdbs'+(this.pid ? '/pid/'+this.pid : '') },
+ url: function() { return '/sample/pdbs'+(this.pid ? '/pid/'+this.pid : '')+(this.lid ? '/lid/'+this.lid : '') },
initialize: function(models, options) {
- if (options) this.pid = options.pid
+ if (options) {
+ this.pid = options.pid
+ this.lid = options.lid
+ }
},
opts: function() {
@@ -14,4 +17,4 @@ define(['backbone', 'modules/samples/models/pdb'], function(Backbone, PDB) {
})
-})
\ No newline at end of file
+})
diff --git a/client/src/js/modules/samples/components/ligand-add-wrapper.vue b/client/src/js/modules/samples/components/ligand-add-wrapper.vue
new file mode 100644
index 000000000..b3a4996ce
--- /dev/null
+++ b/client/src/js/modules/samples/components/ligand-add-wrapper.vue
@@ -0,0 +1,71 @@
+
+
This page shows details for the selected ligand and a list of samples which make use of it
+ +This page lists all <%-title.toLowerCase()%>s associated with the currently selected proposal.
+ +