Changeset 2014


Ignore:
Timestamp:
Aug 2, 2013, 1:54:32 PM (9 years ago)
Author:
Nicklas Nordborg
Message:

Fixes #475: Improve box placement suggestion in specimen tube registration

The wizard now uses a single call to get information about as many wells as are needed immediately when entering step 3. If the user makes changes, no additional suggestions are made, only validation to check if the changed position exists and is emtpy.

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

Legend:

Unmodified
Added
Removed
  • extensions/net.sf.basedb.reggie/trunk/resources/sampleproc/specimentube.jsp

    r1980 r2014  
    2626  dc = sc.newDbControl();
    2727  final User user = User.getById(dc, sc.getLoggedInUserId());
    28   final ItemContext cc = Base.getAndSetCurrentContext(sc, Item.SAMPLE, null, null);
    29   final List<BioPlate> recentBoxes = (List<BioPlate>)cc.getRecent(dc, Item.BIOPLATE, "reggie_specimentube");
    30   BioPlate recentBox = null;
    31   String recentBoxName = ""; 
    32   int recentBoxId = 0;
    33   if (recentBoxes.size() > 0)
    34   {
    35     recentBox = recentBoxes.get(0);
    36     int lastRow = recentBox.getRows()-1;
    37     int lastColumn = recentBox.getColumns()-1;
    38     // Move on to next box in order if the last position isn't empty.
    39     if (!recentBox.getBioWell(lastRow, lastColumn).isEmpty())
    40     {
    41       Integer currentBoxNumber = Values.getInteger(recentBox.getName().substring(2), null);
    42       if (currentBoxNumber != null)
    43       {
    44         currentBoxNumber++;
    45         String nameString = ""+currentBoxNumber;       
    46         while(nameString.length() < 3)
    47         {
    48           nameString = "0"+nameString;
    49         }
    50         recentBoxName = "Sp"+nameString;
    51       }     
    52     }
    53     else
    54     {
    55       recentBoxName = recentBox.getName();
    56       recentBoxId = recentBox.getId();
    57     }
    58   }
    5928%>
    6029<base:page type="default" >
     
    205174  var frm = document.forms['reggie'];
    206175  var inputTubeHtml = '';
    207   var nofTubes = frm.nofTubes.value;
    208   boxInfo = Array(nofTubes);
     176  var nofTubes = parseInt(frm.nofTubes.value);
     177  boxInfo = new Array(nofTubes);
     178 
     179  var freeWells = findStoragePositions(nofTubes);
    209180 
    210181  frm.arrivalDate.disabled = true;
     
    223194  frm.biopsyType.disabled = true;
    224195 
     196  boxesAreValid = new Array(nofTubes);
     197  wellsAreValid = new Array(nofTubes);
     198 
    225199  for (var i=0;i<nofTubes;i++)
    226200  {
    227201    var specimen = null;
    228     var boxValue = '<%=recentBoxName %>';
    229     var rowValue = '';
    230     var columnValue = '';
     202   
     203    var freeWell = freeWells.length > i ? freeWells[i] : null;
     204    var boxValue = freeWell == null ? '' : freeWell.bioPlate.name;
     205    var rowValue = freeWell == null ? '' : WELL_ALPHA[freeWell.row];
     206    var columnValue = freeWell == null ? '' : freeWell.column+1;
     207    boxInfo[i] = freeWell == null ? '' : freeWell.bioPlate.id;
     208    boxesAreValid[i] = freeWell != null;
     209    wellsAreValid[i] = freeWell != null;
    231210    var spWeightValue = '';
    232211    var hisWeightValue = '';
    233212    var allPrepWeightValue = '';
    234 
    235     boxInfo[i] = <%=recentBoxId == 0 ? "null" : recentBoxId %>;
    236     var boxDisabled = '';
    237213   
    238214    inputTubeHtml += '<tr>';
     
    247223    inputTubeHtml += '<td class="subprompt">Box</td>';
    248224    inputTubeHtml += '<td class="input">';
    249     inputTubeHtml += '<input type="text" onkeypress="focusOnEnter(event, \''+row+'\')" name='+box+' value="' + boxValue + '" '+ boxDisabled +' size="12" maxlength="12" onKeyUp="boxOnKeyUp('+i+')" onBlur="boxOnChange('+i+')"></td>';
    250     inputTubeHtml += '<td class="status" id="'+box+'.status"></td>';
     225    inputTubeHtml += '<input type="text" onkeypress="focusOnEnter(event, \''+row+'\')" name='+box+' value="' + boxValue + '" size="12" maxlength="12" onKeyUp="boxOnKeyUp('+i+')" onBlur="boxOnChange('+i+')"></td>';
     226    inputTubeHtml += '<td class="status ' + (boxesAreValid[i] ? 'valid' : '') + '" id="'+box+'.status"></td>';
    251227    inputTubeHtml += '<td class="help"><span id="'+box+'.message" class="message" style="display: none;"></span>Box-number where the specimen tube is located in.</td>';   
    252228    inputTubeHtml += '</tr>';       
    253229   
    254     var wellDisabled = rowValue != '' || boxValue == '' ? 'disabled' : '';
    255230    inputTubeHtml += '<tr>';
    256231    inputTubeHtml += '<td class="subprompt">Row</td>';
    257     inputTubeHtml += '<td class="input"><input style="text-transform:uppercase;" type="text"  onkeypress="focusOnEnter(event, \''+column+'\')" name='+row+' value="' + rowValue + '" size="3" maxlength="2" ' + wellDisabled;
     232    inputTubeHtml += '<td class="input"><input style="text-transform:uppercase;" type="text"  onkeypress="focusOnEnter(event, \''+column+'\')" name='+row+' value="' + rowValue + '" size="3" maxlength="2" ';
    258233    inputTubeHtml += ' onBlur="wellOnChange('+i+')">';
    259     inputTubeHtml += '&nbsp;Column<input type="text" onkeypress="focusOnEnter(event, \''+operatorComment+'\')" name='+column+' value="'+ columnValue + '" size="3" maxlength="3" ' + wellDisabled;
     234    inputTubeHtml += '&nbsp;Column<input type="text" onkeypress="focusOnEnter(event, \''+operatorComment+'\');  return true || Numbers.integerOnly(event);" name='+column+' value="'+ columnValue + '" size="3" maxlength="3" ';
    260235    inputTubeHtml += ' onBlur="wellOnChange('+i+')">';
    261     inputTubeHtml += '&nbsp;<a href="javascript:suggestWellOnClick('+i+')" disabled>Suggest</a>'
    262236    inputTubeHtml += '</td>';
    263     inputTubeHtml += '<td class="status" id="rowColumn'+i+'.status"></td>';
     237    inputTubeHtml += '<td class="status ' + (wellsAreValid[i] ? 'valid' : '') + '" id="rowColumn'+i+'.status"></td>';
    264238    inputTubeHtml += '<td class="help"><span id="rowColumn'+i+'.message" class="message" style="display: none;"></span></td>';   
    265239    inputTubeHtml += '</tr>';
     
    274248  }
    275249  setInnerHTML('tubeInputs', inputTubeHtml); 
    276   boxesAreValid = Array(nofTubes);
    277   wellsAreValid = Array(nofTubes);
    278250  currentStep = 3;
    279251  Main.show('tubeSection');
     
    285257  frm.box0.focus();
    286258  frm.box0.setSelectionRange(textLength,textLength);
    287 }
     259 
     260}
     261
     262function findStoragePositions(nofTubes)
     263{
     264  var frm = document.forms['reggie'];
     265 
     266  var request = Ajax.getXmlHttpRequest();
     267  try
     268  {
     269    showLoadingAnimation('Loading Lysate extracts...');
     270    var url = '../SpecimenTubeRegistration.servlet?ID=<%=ID%>&cmd=FindStoragePositions'; 
     271    url += '&nofTubes='+nofTubes;
     272    request.open("GET", url, false); 
     273    request.send(null);
     274  }
     275  finally
     276  {
     277    hideLoadingAnimation();
     278  }
     279 
     280  if (debug) Main.debug(request.responseText);
     281  var response = JSON.parse(request.responseText); 
     282  if (response.status != 'ok')
     283  {
     284    setFatalError(response.message);
     285    return false;
     286  }
     287  return response.wells;
     288}
     289
    288290
    289291function samplingDateTimeOnChange()
     
    345347  var frm = document.forms['reggie'];
    346348  var boxName = 'box'+tubeIndex;
    347   var rowName = 'row'+tubeIndex;
    348   var columnName = 'column'+tubeIndex; 
    349349  var boxValue = frm.elements[boxName].value;
    350   var nofTubes = frm.nofTubes.value;
    351350 
    352351  boxValue = boxValue.substring(2);
     
    360359  }
    361360  frm.elements[boxName].value = boxValue;
    362  
    363   wellsAreValid[tubeIndex] = false;
    364  
    365   if (frm.elements[boxName].value == 'Sp' || frm.elements[boxName].value == '')
    366   {
    367     boxesAreValid[tubeIndex] = true;
    368     wellsAreValid[tubeIndex] = true;
    369     frm.elements[rowName].disabled = true;
    370     frm.elements[rowName].value = '';
    371     frm.elements[columnName].disabled = true;
    372     frm.elements[columnName].value = '';   
    373     setInputStatus(boxName, '', ''); 
    374     setInputStatus('rowColumn'+tubeIndex, '', '');       
    375     return false;
    376   }
    377361   
    378362  var request = Ajax.getXmlHttpRequest();
    379363  var url = '../SpecimenTubeRegistration.servlet?ID=<%=ID%>&cmd=GetBoxInfo';
    380   url += '&boxName=' + boxValue; 
    381   url += '&nofTubes=' + nofTubes;
     364  url += '&boxName=' + boxValue;
    382365  request.open("GET", url, false);
    383366  request.send(null);
     
    395378  {
    396379    boxesAreValid[tubeIndex] = false;
    397     setInputStatus(boxName, response.message, 'invalid');   
    398     frm.elements[rowName].value = '';
    399     frm.elements[columnName].value = '';
    400     frm.elements[rowName].disabled = true;
    401     frm.elements[columnName].disabled = true;
     380    setInputStatus(boxName, response.message, 'invalid');
    402381    return false;
    403382  }   
     
    407386    boxesAreValid[tubeIndex] = true;
    408387    boxInfo[tubeIndex] = response.boxInfo; 
    409     frm.elements[rowName].disabled = false;
    410     frm.elements[columnName].disabled = false;
    411    
    412     var nextWellRow = '';
    413     var nextWellColumn = '';
    414388   
    415389    if (tubeIndex==0)
     
    421395    {     
    422396      setInputStatus(boxName, 'More then one box is used','warning');
    423       frm.elements[rowName].focus();
    424397      return;
    425398    }
    426    
    427     if (response.freeRow != null && response.freeColumn != null)
    428     {
    429       nextWellRow = response.freeRow;
    430       nextWellColumn = parseInt(response.freeColumn, 10)+1;   
    431      
    432       nextWellColumn = nextWellColumn + tubeIndex;
    433      
    434       if (frm.elements[rowName].value == '' &&
    435           frm.elements[columnName].value == '')
    436       {
    437         frm.elements[rowName].value = nextWellRow;   
    438         frm.elements[columnName].value = nextWellColumn;
    439       }
    440     }         
    441399    setInputStatus('rowColumn'+tubeIndex,'Row[A-'+response.rows+'], Columns[1-'+response.columns+']','');
    442400    if (response.message && "Warning:" == response.message.substring(0,8))
     
    447405    {
    448406      setInputStatus(boxName, '', 'valid');
    449     }       
    450     frm.elements[rowName].focus();   
     407    }
    451408  }   
    452409}
     
    463420  var nofTubes = frm.nofTubes.value;
    464421 
     422  wellsAreValid[tubeIndex] = false;
     423 
    465424  if (boxValue != '' &&
    466425      (rowValue == '' || columnValue == ''))
    467426  {
    468     wellsAreValid[tubeIndex] = false;
    469427    setInputStatus('rowColumn'+tubeIndex, 'Row or column cannot be empty when the box is specified.', 'invalid');
    470428    return false;
     
    490448  if (response.status != 'ok')
    491449  {
    492     wellsAreValid[tubeIndex] = false;
    493     setFatalError(response.message);
     450    setInputStatus('rowColumn'+tubeIndex, response.message, 'invalid');
    494451    return false;
    495452  }
     
    502459    else
    503460    {
    504       wellsAreValid[tubeIndex] = false;
    505461      setInputStatus('rowColumn'+tubeIndex, response.message, 'invalid')
    506462      return false;
     
    514470          columnValue == frm.elements['column'+i].value)
    515471      {
    516         wellsAreValid[tubeIndex] = false;
    517472        setInputStatus('rowColumn'+tubeIndex, 'The row and column are already used once in this wizard','invalid');       
    518473        return false;
     
    522477  wellsAreValid[tubeIndex] = true;
    523478  setInputStatus('rowColumn'+tubeIndex, '', 'valid');
    524 }
    525 
    526 function suggestWellOnClick(tubeIndex)
    527 {
    528   var frm = document.forms['reggie'];
    529   var boxName = 'box'+tubeIndex;
    530   var boxValue = frm.elements[boxName].value;
    531   var rowName = 'row'+tubeIndex;
    532   var columnName = 'column'+tubeIndex;
    533   var nextWellRow = '';
    534   var nextWellColumn = '';
    535   var nofTubes = frm.nofTubes.value;
    536  
    537   if (boxValue && boxValue != 'Sp')
    538   {
    539     var request = Ajax.getXmlHttpRequest();
    540     var url = '../SpecimenTubeRegistration.servlet?ID=<%=ID%>&cmd=GetBoxInfo';
    541     url += '&boxName=' + boxValue; 
    542     url += '&nofTubes=' + nofTubes;
    543     request.open("GET", url, false);
    544     request.send(null);
    545 
    546     if (debug) Main.debug(request.responseText);
    547 
    548     var response = JSON.parse(request.responseText);
    549     if (response.status != 'ok')
    550     {
    551       boxesAreValid[tubeIndex] = false;   
    552       setFatalError(response.message);
    553       return false;
    554     }
    555     if (response.boxInfo)
    556     {
    557       boxInfo[tubeIndex] = response.boxInfo;
    558       if (response.freeRow && response.freeColumn)
    559       {
    560         nextWellRow = response.freeRow;
    561         nextWellColumn = parseInt(response.freeColumn, 10)+1;
    562         if( (nextWellColumn + tubeIndex) > response.columns)
    563         {
    564           nextWellColumn = 0;
    565           nextWellRow = '';
    566         }
    567         else
    568         {
    569           nextWellColumn = nextWellColumn + tubeIndex;
    570         }
    571         frm.elements[rowName].value = nextWellRow;   
    572         frm.elements[columnName].value = nextWellColumn;
    573       }     
    574     }
    575     wellOnChange(tubeIndex);
    576     frm.elements['operatorComment'+tubeIndex].focus();
    577   }
    578479}
    579480
     
    766667  var i=0;
    767668  var bioWellsAreValid = true;
    768  
    769669  while (i<boxesAreValid.length && bioWellsAreValid)
    770670  {
     
    772672    {
    773673      bioWellsAreValid = false;
    774     }     
     674    }
    775675    i++;
    776676  }
     
    939839      <td class="prompt">Number of tubes</td>
    940840      <td class="input"><input type="text" name="nofTubes"
    941         onkeypress="focusOnEnter(event, 'arrivalDate')" value="1" size="12" maxlength="10" onblur='nofTubesOnChange()'></td>
     841        onkeypress="focusOnEnter(event, 'arrivalDate'); return Numbers.integerOnly(event);" value="1" size="12" maxlength="10" onblur='nofTubesOnChange()'></td>
    942842      <td class="status" id="nofTubes.status"></td>
    943843      <td class="help"><span id="nofTubes.message" class="message" style="display:none"></span>Number of tubes in this case.</td>
  • extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/dao/StoragePlate.java

    r1639 r2014  
    1111import net.sf.basedb.core.BioWell;
    1212import net.sf.basedb.core.DbControl;
     13import net.sf.basedb.core.Item;
    1314import net.sf.basedb.core.ItemNotFoundException;
    1415import net.sf.basedb.core.ItemQuery;
     
    150151    PlateGeometry geometry = plate.getPlateGeometry();
    151152    ItemSubtype bioMaterialSubtype = plateType.getItemSubtype();
     153    Item bioMaterialType = plateType.getBioMaterialType();
    152154   
    153155    json.put("freeWells", plate.getFreeWells());
     
    157159    JSONObject jsonBioPlateType = new JSONObject();
    158160    jsonBioPlateType.put("id", plateType.getId());
    159     jsonBioPlateType.put("bioMaterialType", plateType.getBioMaterialType().name());
     161    jsonBioPlateType.put("name", plateType.getName());
     162    if (bioMaterialType != null)
     163    {
     164      jsonBioPlateType.put("bioMaterialType", bioMaterialType.name());
     165    }
    160166    json.put("bioPlateType", jsonBioPlateType);
    161167   
  • extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/servlet/SpecimenTubeServlet.java

    r1626 r2014  
    2424import net.sf.basedb.core.query.Expressions;
    2525import net.sf.basedb.core.query.Hql;
     26import net.sf.basedb.core.query.Orders;
    2627import net.sf.basedb.core.query.Restrictions;
     28import net.sf.basedb.reggie.JsonUtil;
    2729import net.sf.basedb.reggie.Site;
    2830import net.sf.basedb.reggie.converter.StringToDateConverter;
     
    118120        json.put("caseInfo", jsonCase);
    119121      }
     122      else if ("FindStoragePositions".equals(cmd))
     123      {
     124        // Find a given number of storage positions. The process
     125        // is complicated by the fact that "old" boxes still may have empty
     126        // locations so we need to find the first 'Sp*' box that has
     127        // I9 empty.
     128        Integer nofTubes = Values.getInt(req.getParameter("nofTubes"), 1);
     129        dc = sc.newDbControl();
     130       
     131        ItemQuery<BioPlate> spBoxQuery = BioPlate.getQuery();
     132        spBoxQuery.join(Hql.innerJoin("bioWells", "bw"));
     133        spBoxQuery.join(Hql.leftJoin("bw", "bioMaterial", "bm", null, false));
     134        spBoxQuery.restrict(Restrictions.like(Hql.property("name"), Expressions.string("Sp%")));
     135        spBoxQuery.restrict(Restrictions.eq(Hql.property("bw", "row"), Expressions.integer(8)));
     136        spBoxQuery.restrict(Restrictions.eq(Hql.property("bw", "column"), Expressions.integer(8)));
     137        spBoxQuery.restrict(Restrictions.eq(Hql.alias("bm"), null));
     138       
     139        spBoxQuery.order(Orders.asc(Hql.property("name")));
     140        List<BioPlate> spBoxes = spBoxQuery.list(dc);
     141       
     142        JSONArray jsonWells = new JSONArray();
     143        for (BioPlate box : spBoxes)
     144        {
     145          BioWell free = getFirstFreeWell(box, 1);
     146          while (free != null)
     147          {
     148            jsonWells.add(JsonUtil.getBioWellAsJSON(free));
     149            if (jsonWells.size() == nofTubes) break;
     150            free = getNextFreeWell(free);
     151          }
     152          if (jsonWells.size() == nofTubes) break;
     153        }
     154       
     155        json.put("wells", jsonWells);
     156      }
    120157      else if ("GetBoxInfo".equals(cmd))
    121158      {
     
    124161        // Get spBox
    125162        String boxName = req.getParameter("boxName");       
    126         Integer spaceReq = Values.getInt(req.getParameter("nofTubes"), 1);
    127163        ItemQuery<BioPlate> spBoxQuery = BioPlate.getQuery();
    128164        spBoxQuery.restrict(Restrictions.eq(Hql.property("name"), Expressions.string(boxName)));
     
    146182          else if (lysBoxes.size() > 1)
    147183          {
    148             json.put("message", "Warning:More then one lys-box were found.");
    149           }
    150           else
    151           {
    152             // Search for empty position in corresponding lys-box.
    153             emptyWell = getFirstFreeWell(lysBoxes.get(0),spaceReq);
    154           }
    155          
    156           // No empty position were found in lys-box. Search for empty position in Sp-box.
    157           if (emptyWell == null)
    158           {
    159             emptyWell = getFirstFreeWell(box, spaceReq);
    160           }
    161           else
    162           {
    163             // else control that same position is free in Sp-box or look in Sp-box.
    164             if (!box.getBioWell(emptyWell.getRow(), emptyWell.getColumn()).isEmpty())
    165             {
    166               emptyWell = getFirstFreeWell(box, spaceReq);             
    167             }
    168           }
    169          
    170           if (emptyWell == null)
    171           {
    172             json.put("message", "Warning:An empty position can not be suggested.");
    173           }
    174          
     184            json.put("message", "Warning:More then one Lys-box were found.");
     185          }
    175186          WellCoordinateFormatter wcfRow = new WellCoordinateFormatter(true);
    176187          json.put("boxInfo", spBoxes.get(0).getId());
    177188          json.put("rows", wcfRow.format(spBoxes.get(0).getRows()-1));
    178189          json.put("columns", spBoxes.get(0).getColumns());
    179           if (emptyWell != null)
    180           {
    181             json.put("freeColumn", emptyWell.getColumn());
    182             json.put("freeRow", wcfRow.format(emptyWell.getRow()));
    183           }                   
     190           
    184191        }
    185192        else if (spBoxes.size()==0)
     
    218225        else if (!plate.getBioWell(rowNumber-1, columnIndex).isEmpty() )
    219226        {
    220           msg = "Specified row:column("+rowString+":"+wcfColumn.format(columnIndex)+") is already taken.";
     227          msg = "Location '"+rowString+wcfColumn.format(columnIndex)+"' is already used.";
    221228        }
    222229        json.put("message", msg);       
     
    260267      {
    261268        dc = sc.newDbControl();
    262         ItemContext cc = sc.getCurrentContext(Item.SAMPLE);
    263269        JSONObject jsonReq = (JSONObject)new JSONParser().parse(req.getReader());       
    264270        JSONObject jsonCase = (JSONObject)jsonReq.get("caseInfo");
     
    333339              if (well != null)
    334340              {
    335                 specimen.setBioWell(well);
    336                 cc.setRecent(well.getPlate(), "reggie_specimentube", 1);               
     341                specimen.setBioWell(well);             
    337342              }
    338343            }           
     
    405410    return firstFreeWell;
    406411  }
     412 
     413  private BioWell getNextFreeWell(BioWell well)
     414  {
     415    BioWell nextFree = null;
     416    BioPlate plate = well.getPlate();
     417    int numColumns = plate.getColumns();
     418   
     419    int nextPos = well.getRow() * numColumns + well.getColumn()+1;
     420    int nextCol = nextPos % numColumns;
     421    int nextRow = nextPos / numColumns;
     422    nextFree = plate.getBioWell(nextRow, nextCol);
     423   
     424    return nextFree;
     425  }
     426
    407427}
Note: See TracChangeset for help on using the changeset viewer.