Changeset 2184


Ignore:
Timestamp:
Jan 9, 2014, 1:12:04 PM (8 years ago)
Author:
Nicklas Nordborg
Message:

Fixes #553: Set different volume and mixing strategy for pools on a plate

Added a section in the plate view so that the "Average volume/lib" and "Mixing strategy" can be selected for each pool. Only minor changes required in the protocol generation since most of it was already working per pool.

Location:
extensions/net.sf.basedb.reggie/trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • extensions/net.sf.basedb.reggie/trunk/resources/libprep/create_pools.jsp

    r2151 r2184  
    3636var currentStep = 1;
    3737
    38 var targetVolumePerLibIsValid = false;
     38var targetVolumePerLibIsValid = [];
    3939var POOL_DATA = [];
    4040
     
    133133function goCreate()
    134134{
    135   if (!targetVolumePerLibIsValid) return;
    136135 
    137136  var submitInfo = {};
     
    140139  var frm = document.forms['reggie'];
    141140  var libPlate = frm.bioplate[frm.bioplate.selectedIndex].bioplate;
    142   submitInfo.targetVolumeInPoolPerLib = parseFloat(frm.target_volume.value);
    143   submitInfo.targetPoolMolarity = TARGET_MOLARITY_IN_POOL;
    144   submitInfo.mixingStrategy = Forms.getCheckedRadio(frm.mixing_strategy).value;
    145141
    146142  var numPools = Plate.getPools();
    147143  for (var poolNo = 0; poolNo < numPools; poolNo++)
    148144  {
     145    if (!targetVolumePerLibIsValid[poolNo]) return;
     146
    149147    var wells = Plate.getPool(poolNo);
    150148   
    151149    var poolInfo = {};
    152150    poolInfo.name = POOL_NAMES[poolNo];
     151    poolInfo.targetPoolMolarity = TARGET_MOLARITY_IN_POOL;
     152    poolInfo.targetVolumeInPoolPerLib = parseFloat(frm['target_volume.'+poolNo].value);
     153    poolInfo.mixingStrategy = Forms.getCheckedRadio(frm['mixing_strategy.'+poolNo]).value;
    153154    poolInfo.comment = frm['comment.'+poolNo].value;
    154155    poolInfo.ebVolumeExtra = POOL_DATA[poolNo].ebVolumeExtra;
     
    272273  LIMIT_FOR_EXTRA_LARGE_MIX = poolInfo.limitForExtraLargeMix;
    273274 
    274   setInnerHTML('pool_schema', schema.name);
    275   setInnerHTML('barcode_variant', barcodeVariant.name);
     275  //setInnerHTML('pool_schema', schema.name);
     276  //setInnerHTML('barcode_variant', barcodeVariant.name);
    276277  setInnerHTML('target_molarity', Numbers.formatNumber(TARGET_MOLARITY_IN_POOL, 1, ' nM'));
    277278
     
    295296
    296297  PoolSchema.buildPoolTableRow(schema, 12);
    297   setTargetVolumePerLib(poolInfo.targetVolumePerLib);
     298  initPoolData(poolInfo.targetVolumePerLib);
     299  updatePoolData();
    298300}
    299301
     
    335337}
    336338
    337 function setTargetVolumePerLib(targetVolumePerLib)
     339function initPoolData(targetVolumePerLib)
     340{
     341  for (var poolNo = 0; poolNo < Plate.getPools(); poolNo++)
     342  {
     343    var poolDiv = document.getElementById('pool.'+poolNo);
     344    var html = '<div class="pool-name">'+POOL_NAMES[poolNo]+'</div>';
     345    html += '<div class="pool-data">';
     346    html += '<table>';
     347    html += '<tr><td class="prompt">Volume/lib</td>';
     348    html += '<td><input type="text" style="width: 4em;" name="target_volume.'+poolNo+'" value="'+targetVolumePerLib+'"';
     349    html += ' onblur="targetVolumeOnBlur(event)" onkeypress="doOnEnter(event, targetVolumeOnBlur); return Numbers.numberOnly(event)">';
     350    html += 'µl (2--10)</td>';
     351    html += '<td id="target_volume.'+poolNo+'.status"></td></tr>';
     352    html += '<tr><td class="prompt">Mixing strategy</td>';
     353    html += '<td colspan="2">';
     354    html += '<label><input type="radio" name="mixing_strategy.'+poolNo+'" value="dynamic" checked';
     355    html += ' onclick="mixingStrategyOnChange(event)">Dynamic</label> ';
     356    html += '<label><input type="radio" name="mixing_strategy.'+poolNo+'" value="fixed"';
     357    html += ' onclick="mixingStrategyOnChange(event)">Fixed</label>';
     358    html += '</td></tr>';
     359    html += '</table>';
     360   
     361    html += '<textarea name="comment.'+poolNo+'"></textarea>';
     362    html += '</div>';
     363    html += '<div class="pool-summary" id="pool-summary.'+poolNo+'"></div>';
     364    poolDiv.innerHTML = html;
     365    setTargetVolumePerLib(poolNo, targetVolumePerLib);
     366  }
     367 
     368}
     369
     370
     371function setTargetVolumePerLib(poolNo, targetVolumePerLib)
    338372{
    339373  var frm = document.forms['reggie'];
    340   frm.target_volume.value = targetVolumePerLib;
    341  
    342   targetVolumePerLibIsValid = false;
     374  if (targetVolumePerLib)
     375  {
     376    frm['target_volume.'+poolNo].value = targetVolumePerLib;
     377  }
     378  else
     379  {
     380    targetVolumePerLib = parseInt(frm['target_volume.'+poolNo].value);
     381  }
     382 
     383  targetVolumePerLibIsValid[poolNo] = false;
    343384  if (targetVolumePerLib < 2 || targetVolumePerLib > 10)
    344385  {
    345     setInputStatus('target_volume', 'Must be between 2 and 10µl', 'invalid');
     386    setInputStatus('target_volume.'+poolNo, 'Must be between 2 and 10µl', 'invalid');
    346387    return;
    347388  }
    348   setInputStatus('target_volume', '', 'valid');
    349   targetVolumePerLibIsValid = true;
     389  setInputStatus('target_volume.'+poolNo, '', 'valid');
     390  targetVolumePerLibIsValid[poolNo] = true;
    350391
    351392  // Pre-process the Library items
    352   var mixingStrategy = Forms.getCheckedRadio(frm.mixing_strategy).value;
    353   var wells = Plate.getWells();
     393  var mixingStrategy = Forms.getCheckedRadio(frm['mixing_strategy.'+poolNo]).value;
     394  var wells = Plate.getPool(poolNo);
    354395  for (var i = 0; i < wells.length; i++)
    355396  {
     
    369410  }
    370411 
    371   updatePoolData();
    372   WellPainter.mixingStrategy = mixingStrategy;
    373   Plate.paint(Plate.getWells());
    374412}
    375413
     
    426464{
    427465  var frm = document.forms['reggie'];
    428   var targetVolumePerLib = parseFloat(frm.target_volume.value);
    429   var mixingStrategy = Forms.getCheckedRadio(frm.mixing_strategy).value;
    430466
    431467  var libPlate = frm.bioplate[frm.bioplate.selectedIndex].bioplate;
     
    433469  var barcodeVariant = PoolSchema.getBarcodeVariantByName(schema, libPlate.barcodeVariant);
    434470
    435   var numSeparateMix = 0;
    436   var numLowQuantity = 0;
    437   var numExcluded = 0;
    438  
    439471  for (var poolNo = 0; poolNo < Plate.getPools(); poolNo++)
    440472  {
     473    var numSeparateMix = 0;
     474    var numLowQuantity = 0;
     475    var numExcluded = 0;
     476   
    441477    // Get all libs into an array
    442478    var libs = [];
     
    460496   
    461497    // Calculate final pool volumes, molarity, etc.
     498    var targetVolumePerLib = parseFloat(frm['target_volume.'+poolNo].value);
     499    var mixingStrategy = Forms.getCheckedRadio(frm['mixing_strategy.'+poolNo]).value;
    462500    var poolInfo = PoolMix.calculateFinalPoolInfo(libs, TARGET_MOLARITY_IN_POOL, targetVolumePerLib, mixingStrategy);
    463501    POOL_DATA[poolNo] = poolInfo;
     
    472510    }
    473511   
     512    WellPainter.mixingStrategy = mixingStrategy;
     513    Plate.paint(wells);
     514
    474515    var warnMsg = null;
    475516    if (poolInfo.ebVolumeExtra < 0)
     
    479520   
    480521    var poolDiv = document.getElementById('pool.'+poolNo);
    481     var poolData = '<div class="pool-data">';
     522    var summaryDiv = document.getElementById('pool-summary.'+poolNo);
     523    var poolData = '';
    482524    poolData += libs.length + ' libs • ';
    483525    poolData += Numbers.formatNumber(poolInfo.molarity, 2, 'nM')+' • ';
     
    487529      poolData += ' • <span class="pool-eb">'+Numbers.formatNumber(poolInfo.ebVolumeExtra, 1, 'µl')+'</span>';
    488530    }
     531    if (numSeparateMix > 0 || numLowQuantity)
     532    {
     533      poolData += '<br>Separate mix: ' + numSeparateMix;
     534      poolData += ' • Low quantity: ' + numLowQuantity;
     535      poolData += '';
     536    }
     537   
    489538    if (warnMsg)
    490539    {
     
    496545      Main.removeClass(poolDiv, 'warning');
    497546    }
    498     poolData += '</div>';
    499     var poolComments = frm['comment.'+poolNo];
    500     var comments = poolComments ? poolComments.value : '';
    501     poolData += '<textarea name="comment.'+poolNo+'"></textarea>';
    502     poolDiv.innerHTML = POOL_NAMES[poolNo] + poolData;
    503     frm['comment.'+poolNo].value = comments;
    504   }
    505  
    506   setInnerHTML('num_separate_mix', numSeparateMix);
    507   setInnerHTML('num_low_quantity', numLowQuantity);
    508   setInnerHTML('num_excluded', numExcluded);
    509 }
    510 
    511 function targetVolumeOnBlur()
     547    summaryDiv.innerHTML = poolData;
     548  }
     549 
     550}
     551
     552function targetVolumeOnBlur(event)
    512553{
    513554  var frm = document.forms['reggie'];
    514   var targetVolumePerLib = parseFloat(frm.target_volume.value);
    515   setTargetVolumePerLib(targetVolumePerLib);
    516 }
    517 
    518 function mixingStrategyOnChange()
    519 {
    520   targetVolumeOnBlur();
     555  var poolNo = parseInt(event.target.name.substr(-1));
     556  setTargetVolumePerLib(poolNo);
     557  updatePoolData();
     558}
     559
     560function mixingStrategyOnChange(event)
     561{
     562  targetVolumeOnBlur(event);
    521563}
    522564
     
    801843  font-style: italic;
    802844}
     845
     846#pool-row th
     847{
     848  border-top: 0;
     849  border-bottom: 1px solid #808080;
     850  vertical-align: top;
     851  padding: 0;
     852}
     853
     854.pool.warning
     855{
     856  background-image: url('../images/warning.png');
     857  background-position: 2% 98%;
     858  background-repeat: no-repeat;
     859}
     860
     861.pool-name
     862{
     863  padding: 2px;
     864}
     865
    803866.pool-data
    804867{
    805868  font-weight: normal;
    806 }
    807 .pool.warning
    808 {
    809   background-image: url('../images/warning.png');
    810   background-position: 98% 5%;
    811   background-repeat: no-repeat;
    812 }
    813 
    814 .pool-data .warning-message
     869  padding: 2px;
     870  border-top: 1px solid #808080;
     871  border-bottom: 1px solid #808080;
     872  background-color: #FFFFFF;
     873}
     874
     875.pool-data td
     876{
     877  text-align: left;
     878  white-space: nowrap;
     879}
     880
     881.pool-data td.prompt
     882{
     883  width: 30%;
     884}
     885
     886.pool-data textarea
     887{
     888  width: 95%;
     889  height: 3.5em;
     890}
     891
     892
     893.pool-summary
     894{
     895  font-weight: normal;
     896  padding: 2px;
     897  font-style: italic;
     898}
     899
     900.pool-summary .warning-message
    815901{
    816902  color: #C80000;
    817   font-style: italic;
    818903}
    819904
     
    844929  background-position: 98% 5%;
    845930  background-repeat: no-repeat;
    846 }
    847 .pool textarea
    848 {
    849   width: 95%;
    850   height: 3.5em;
    851931}
    852932</style>
     
    9271007  <tr>
    9281008    <td rowspan="2" class="stepno">2</td>
    929     <td class="steptitle">Confirm pool layout</td>
     1009    <td class="steptitle">Calculate pool mixing volumes</td>
    9301010  </tr>
    9311011  <tr>
    9321012    <td class="stepfields">
    9331013
    934       <table class="bottomborder" style="width: 100%;">
     1014      <table class="bottomborder" style="width: 100%; display: none;">
    9351015      <tr valign="top">
    9361016        <td class="prompt">Pool layout</td>
     
    9391019        </td>
    9401020        <td class="status"></td>
    941         <td class="help"></td>
     1021        <td class="help" rowspan="2">
     1022        </td>
    9421023      </tr>
    9431024      <tr valign="top">
    9441025        <td class="prompt">Target molarity</td>
    9451026        <td class="input">
    946           <span id="target_molarity"></span>
     1027          <span id="target_molarity2"></span>
    9471028        </td>
    9481029        <td class="status"></td>
    949         <td class="help"></td>
    950       </tr>
    951       <tr valign="top">
    952         <td class="prompt">Average volume / lib</td>
    953         <td class="input">
    954           <input type="text" class="text" name="target_volume"
    955             value="5" style="width: 4em;"
    956             onblur="targetVolumeOnBlur()"
    957             onkeypress="doOnEnter(event, targetVolumeOnBlur); return Numbers.numberOnly(event)"> µl (2--10)
    958         </td>
    959         <td class="status" id="target_volume.status"></td>
    960         <td class="help" rowspan="2"><span id="target_volume.message" class="message" style="display: none;"></span>
    961           Select a target volume when mixing each lib to 2nM before pooling.
    962           Use the <b>Dynamic</b> strategy to prioritize final pool molarity by mixing
    963           different volume for each library. Use the <b>Fixed</b> strategy to mix all
    964           libraries to the given volume.
    965         </td>
    966       </tr>
    967       <tr valign="top">
    968         <td class="prompt">Mixing strategy</td>
    969         <td class="input">
    970           <label><input type="radio" name="mixing_strategy" value="dynamic" checked
    971             onclick="mixingStrategyOnChange()"
    972             >Dynamic</label>
    973           <label><input type="radio" name="mixing_strategy" value="fixed"
    974             onclick="mixingStrategyOnChange()"
    975             >Fixed</label>
    976         </td>
    977         <td class="status" id="mixing_strategy.status"></td>
    9781030      </tr>
    9791031      </table>
     
    10041056        />
    10051057      </tbl:toolbar>
    1006      
    1007       <div style="margin: 1em;" class="messagecontainer note">
    1008         <base:icon image="info.png"/>
    1009         <b>Separate mix:</b> <span id="num_separate_mix"></span>,
    1010         <b>Low quantity:</b> <span id="num_low_quantity"></span>,
    1011         <b>Excluded:</b> <span id="num_excluded"></span>
    1012       </div>
     1058       
     1059        <div class="messagecontainer note" style="margin: 1em auto 1em auto; width: 75%; ">
     1060          Select a target volume when mixing the libraries to a pool with <span id="target_molarity"></span>
     1061          concentration.
     1062          Use the <b>Dynamic strategy</b> to prioritize final pool molarity by mixing
     1063          different volumes for each library. Use the <b>Fixed strategy</b> to mix all
     1064          libraries to the given volume. For each pool a summary is calculated:
     1065          <i>Number of libraries</i> • <i>Final molarity</i> • <i>Total volume</i> • <i>EB volume (dynamic mixing only)</i>
     1066        </div>
    10131067     
    10141068      <table class="plate" style="margin: 1em 1em 1em 1em;" id="plate">
     1069      <tr id="pool-row">
     1070        <th colspan="13">&nbsp;</th>
     1071      </tr>
    10151072      <%
    10161073      int columns = 12;
     
    10541111      %>
    10551112      </tbody>
    1056       <tr id="pool-row">
    1057         <th colspan="13">&nbsp;</th>
    1058       </tr>
    10591113      </table>
    10601114      <div style="margin: 1em;">
    10611115      Low molarity = The concentration of this library too low to mix to target molarity for the pool.<br>
    1062       For each pool: <i>Number of libraries</i> • <i>Final molarity</i> • <i>Total volume</i> • <i>EB volume (dynamic mixing only)</i>
    10631116      </div>
    10641117    </td>
  • extensions/net.sf.basedb.reggie/trunk/resources/libprep/pool_protocol2.jsp

    r2140 r2184  
    248248  function viewAsList(pools, schema, barcodeVariant)
    249249  {
    250     for (var i = 0; i < pools.length; i++)
    251     {
    252       var pool = pools[i];
    253       var wellsInPool = POOL_CURRENT_SCHEMA ? Plate.getPool(i) : null;
     250    for (var poolNo = 0; poolNo < pools.length; poolNo++)
     251    {
     252      var pool = pools[poolNo];
     253      var realPoolNo = schema.getPoolNumForColumn(pool.libraries[0].bioWell.column);
     254
     255      var wellsInPool = POOL_CURRENT_SCHEMA ? Plate.getPool(realPoolNo) : null;
    254256     
    255257      var html = '';
     
    314316      poolData += Numbers.formatNumber(pool.molarity, 2)+'nM; ';
    315317      poolData += Numbers.formatNumber(pool.volume, 1) + 'µl';
     318      poolData += '<br>Mixing strategy: ' + pool.mixingStrategy;
    316319      poolData += '</div>';
    317320      setInnerHTML('molarity.'+pool.id, poolData);
     
    372375
    373376    WellPainter.barcodeVariant = barcodeVariant;
    374     WellPainter.mixingStrategy = pools[0].mixingStrategy;
    375     Plate.paint(Plate.getWells());
    376377    PoolSchema.buildPoolTableRow(schema, 12);
    377378    Main.show('plateview');
     
    380381    {
    381382      var pool = pools[poolNo];
     383      var realPoolNo = schema.getPoolNumForColumn(pool.libraries[0].bioWell.column);
     384      WellPainter.mixingStrategy = pool.mixingStrategy;
     385      Plate.paint(Plate.getPool(realPoolNo));
     386     
    382387      var poolData = '<div class="pool-data">';
    383388      poolData += pool.libraries.length + ' libs • ';
     
    388393        poolData += ' • <span class="pool-eb">'+Numbers.formatNumber(pool.extra.ebVolumeExtra, 1, 'µl')+'</span>';
    389394      }
     395      poolData += '<br>Mixing strategy: ' + pool.mixingStrategy;
    390396      poolData += '<div class="comments">'+pool.comments+'</div>';
    391397      poolData += '</div>';
    392398     
    393       var realPoolNo = schema.getPoolNumForColumn(pool.libraries[0].bioWell.column);
    394399      document.getElementById('pool.'+realPoolNo).innerHTML = pool.name + poolData;
    395400    }
     
    658663  }
    659664 
    660   #pool-row
    661   {
     665  #pool-row th
     666  {
     667    border-top: 0;
     668    border-bottom: 1px solid #808080;
    662669    vertical-align: top;
     670    padding: 0;
    663671  }
    664672 
     
    782790    %>
    783791    <table class="plate" style="margin: 0em 0 0 0; width: 100%; display: none;" id="plateview">
     792    <tr id="pool-row">
     793      <th colspan="13">&nbsp;</th>
     794    </tr>
    784795    <%
    785796    WellCoordinateFormatter rowF = new WellCoordinateFormatter(true);
     
    818829    %>
    819830    </tbody>
    820     <tr id="pool-row">
    821       <th colspan="13">&nbsp;</th>
    822     </tr>
    823831    </table>
    824832    <div style="margin: 1em;">
  • extensions/net.sf.basedb.reggie/trunk/resources/reggie.js

    r2092 r2184  
    5050  if (event.keyCode == 13)
    5151  {
    52     setTimeout(method, 200);
     52    setTimeout(function() { method.call(null, event) }, 200);
    5353  }
    5454  return true;
  • extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/servlet/PoolServlet.java

    r2161 r2184  
    384384        JSONArray jsonPools = (JSONArray)jsonReq.get("pools");
    385385        JSONArray jsonFlagged = (JSONArray)jsonReq.get("flagged");
    386         Number targetVolumeInPoolPerLib = (Number)jsonReq.get("targetVolumeInPoolPerLib");
    387         Number targetPoolMolarity = (Number)jsonReq.get("targetPoolMolarity");
    388         String mixingStrategy = (String)jsonReq.get("mixingStrategy");
    389386       
    390387        ItemSubtype pooledLibraryType = Subtype.POOLED_LIBRARY.load(dc);
     
    400397         
    401398          Number libPlateId = (Number)jsonPool.get("libPlate");
     399          String mixingStrategy = (String)jsonPool.get("mixingStrategy");
     400          Number targetVolumeInPoolPerLib = (Number)jsonPool.get("targetVolumeInPoolPerLib");
     401          Number targetPoolMolarity = (Number)jsonPool.get("targetPoolMolarity");
    402402         
    403403          // Create PooledLibrary item
     
    416416            AnyToAny libPlateLink = AnyToAny.getNew(dc, pool, libPlate, "LibPlate", true);
    417417            dc.saveItemIf(pool, libPlateLink, false);
    418             jsonMessages.add("Linked pool " + pool.getName() + " with " + libPlateLink);
    419418          }
    420419          else
     
    689688        Float dilQuantity = dil.getOriginalQuantity();
    690689        lib.setAnnotation("mixFactor", (dilQuantity + usedQuantity) / usedQuantity);
    691       }
     690        // Add the used quantity back since we want the remaining quantity *before* pooling
     691        if (remain != null) remain += dilQuantity;
     692    }
    692693     
    693694      lib.setAnnotation("dils", result.size());
Note: See TracChangeset for help on using the changeset viewer.