extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/servlet/OuttakeServlet.java

Code
Comments
Other
Rev Date Author Line
4146 03 Oct 16 nicklas 1 package net.sf.basedb.reggie.servlet;
4146 03 Oct 16 nicklas 2
4146 03 Oct 16 nicklas 3 import java.io.IOException;
4157 07 Oct 16 nicklas 4 import java.io.PrintWriter;
4165 21 Oct 16 nicklas 5 import java.util.Date;
4153 05 Oct 16 nicklas 6 import java.util.HashSet;
4153 05 Oct 16 nicklas 7 import java.util.Iterator;
4154 06 Oct 16 nicklas 8 import java.util.List;
4153 05 Oct 16 nicklas 9 import java.util.Set;
4146 03 Oct 16 nicklas 10
4146 03 Oct 16 nicklas 11 import javax.servlet.ServletException;
4146 03 Oct 16 nicklas 12 import javax.servlet.http.HttpServlet;
4146 03 Oct 16 nicklas 13 import javax.servlet.http.HttpServletRequest;
4146 03 Oct 16 nicklas 14 import javax.servlet.http.HttpServletResponse;
4146 03 Oct 16 nicklas 15
4146 03 Oct 16 nicklas 16 import org.json.simple.JSONArray;
4146 03 Oct 16 nicklas 17 import org.json.simple.JSONObject;
4146 03 Oct 16 nicklas 18
6326 14 Jun 21 nicklas 19 import net.sf.basedb.core.AnyToAny;
4166 21 Oct 16 nicklas 20 import net.sf.basedb.core.BioMaterial;
4154 06 Oct 16 nicklas 21 import net.sf.basedb.core.BioMaterialEvent;
4154 06 Oct 16 nicklas 22 import net.sf.basedb.core.BioPlate;
4154 06 Oct 16 nicklas 23 import net.sf.basedb.core.BioPlateType;
4173 24 Oct 16 nicklas 24 import net.sf.basedb.core.BioWell;
4146 03 Oct 16 nicklas 25 import net.sf.basedb.core.DbControl;
4153 05 Oct 16 nicklas 26 import net.sf.basedb.core.Extract;
4893 09 Jul 18 nicklas 27 import net.sf.basedb.core.Include;
4153 05 Oct 16 nicklas 28 import net.sf.basedb.core.Item;
4153 05 Oct 16 nicklas 29 import net.sf.basedb.core.ItemList;
4153 05 Oct 16 nicklas 30 import net.sf.basedb.core.ItemQuery;
4153 05 Oct 16 nicklas 31 import net.sf.basedb.core.ItemSubtype;
4166 21 Oct 16 nicklas 32 import net.sf.basedb.core.MeasuredBioMaterial;
4154 06 Oct 16 nicklas 33 import net.sf.basedb.core.PlateGeometry;
4146 03 Oct 16 nicklas 34 import net.sf.basedb.core.SessionControl;
4154 06 Oct 16 nicklas 35 import net.sf.basedb.core.Type;
4157 07 Oct 16 nicklas 36 import net.sf.basedb.core.query.Annotations;
4154 06 Oct 16 nicklas 37 import net.sf.basedb.core.query.Expressions;
4154 06 Oct 16 nicklas 38 import net.sf.basedb.core.query.Hql;
4154 06 Oct 16 nicklas 39 import net.sf.basedb.core.query.Orders;
4154 06 Oct 16 nicklas 40 import net.sf.basedb.core.query.Restrictions;
4164 20 Oct 16 nicklas 41 import net.sf.basedb.core.snapshot.SnapshotManager;
4146 03 Oct 16 nicklas 42 import net.sf.basedb.reggie.JsonUtil;
4146 03 Oct 16 nicklas 43 import net.sf.basedb.reggie.Reggie;
4146 03 Oct 16 nicklas 44 import net.sf.basedb.reggie.counter.CounterService;
4153 05 Oct 16 nicklas 45 import net.sf.basedb.reggie.dao.Annotationtype;
4154 06 Oct 16 nicklas 46 import net.sf.basedb.reggie.dao.BioplateType;
4154 06 Oct 16 nicklas 47 import net.sf.basedb.reggie.dao.Geometry;
4893 09 Jul 18 nicklas 48 import net.sf.basedb.reggie.dao.ReggieItem;
4146 03 Oct 16 nicklas 49 import net.sf.basedb.reggie.dao.ReggieRole;
4153 05 Oct 16 nicklas 50 import net.sf.basedb.reggie.dao.Subtype;
4153 05 Oct 16 nicklas 51 import net.sf.basedb.util.Values;
4146 03 Oct 16 nicklas 52 import net.sf.basedb.util.error.ThrowableUtil;
5371 16 Apr 19 nicklas 53 import net.sf.basedb.util.excel.XlsxTableWriter;
5371 16 Apr 19 nicklas 54 import net.sf.basedb.util.excel.XlsxToCsvUtil;
4173 24 Oct 16 nicklas 55 import net.sf.basedb.util.export.TableWriter;
4146 03 Oct 16 nicklas 56
4146 03 Oct 16 nicklas 57 /**
4146 03 Oct 16 nicklas 58   Handles outtake of biomaterial for external parties.
4146 03 Oct 16 nicklas 59   
4146 03 Oct 16 nicklas 60   @author nicklas
4146 03 Oct 16 nicklas 61    @since 4.8
4146 03 Oct 16 nicklas 62 */
4146 03 Oct 16 nicklas 63 public class OuttakeServlet 
4146 03 Oct 16 nicklas 64   extends HttpServlet 
4146 03 Oct 16 nicklas 65 {
4146 03 Oct 16 nicklas 66
4146 03 Oct 16 nicklas 67   private static final long serialVersionUID = -1308781977707573172L;
4146 03 Oct 16 nicklas 68
4146 03 Oct 16 nicklas 69   public OuttakeServlet()
4146 03 Oct 16 nicklas 70   {}
4146 03 Oct 16 nicklas 71
4146 03 Oct 16 nicklas 72   @SuppressWarnings("unchecked")
4146 03 Oct 16 nicklas 73   @Override
4146 03 Oct 16 nicklas 74   protected void doGet(HttpServletRequest req, HttpServletResponse resp)
4146 03 Oct 16 nicklas 75     throws ServletException, IOException 
4146 03 Oct 16 nicklas 76   {
4146 03 Oct 16 nicklas 77     String cmd = req.getParameter("cmd");
4146 03 Oct 16 nicklas 78     JsonUtil.setJsonResponseHeaders(resp);
4146 03 Oct 16 nicklas 79     
4146 03 Oct 16 nicklas 80     JSONObject json = new JSONObject();
4146 03 Oct 16 nicklas 81     json.put("status", "ok");
4146 03 Oct 16 nicklas 82   
4153 05 Oct 16 nicklas 83     final SessionControl sc = Reggie.getSessionControl(req);
4146 03 Oct 16 nicklas 84     DbControl dc = null;
4146 03 Oct 16 nicklas 85     try
4146 03 Oct 16 nicklas 86     {
4146 03 Oct 16 nicklas 87       if ("CheckSourceList".equals(cmd))
4146 03 Oct 16 nicklas 88       {
6336 16 Jun 21 nicklas 89         dc = sc.newDbControl(":Define outtake");
4146 03 Oct 16 nicklas 90
4153 05 Oct 16 nicklas 91         int listId = Values.getInt(req.getParameter("listId"));
6326 14 Jun 21 nicklas 92         ItemSubtype outtakeType = Subtype.getByCName(req.getParameter("outtakeType")).get(dc);
4153 05 Oct 16 nicklas 93         
4153 05 Oct 16 nicklas 94         ItemList list = ItemList.getById(dc, listId);
4153 05 Oct 16 nicklas 95         Item memberType = list.getMemberType();
4153 05 Oct 16 nicklas 96         ItemSubtype memberSubtype = list.getItemSubtype();
4153 05 Oct 16 nicklas 97         
4153 05 Oct 16 nicklas 98         JSONObject jsonList = new JSONObject();
4153 05 Oct 16 nicklas 99         jsonList.put("id", list.getId());
4153 05 Oct 16 nicklas 100         jsonList.put("name", list.getName());
4153 05 Oct 16 nicklas 101         jsonList.put("itemType", memberType.name());
4153 05 Oct 16 nicklas 102         jsonList.put("size", list.getSize());
4153 05 Oct 16 nicklas 103         
4153 05 Oct 16 nicklas 104         if (memberType == Item.EXTRACT)
4153 05 Oct 16 nicklas 105         {
4994 02 Oct 18 nicklas 106           int numDoNotUse = 0;
6326 14 Jun 21 nicklas 107           int numIncorrectSubtype = 0;
6183 26 Mar 21 nicklas 108           ItemQuery<Extract> memberQuery = list.getMembers();
4153 05 Oct 16 nicklas 109           memberQuery.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
4153 05 Oct 16 nicklas 110           
4153 05 Oct 16 nicklas 111           MinMax remainingQuantity = new MinMax();
6220 20 Apr 21 nicklas 112           MinMax conc = new MinMax();
4153 05 Oct 16 nicklas 113                     
4153 05 Oct 16 nicklas 114           Iterator<Extract> it = memberQuery.iterate(dc);
4153 05 Oct 16 nicklas 115           while (it.hasNext())
4153 05 Oct 16 nicklas 116           {
4153 05 Oct 16 nicklas 117             Extract e = it.next();
4153 05 Oct 16 nicklas 118             remainingQuantity.add(e.getRemainingQuantity());
6220 20 Apr 21 nicklas 119             Float c = (Float)Annotationtype.ND_CONC.getAnnotationValue(dc, e);
6220 20 Apr 21 nicklas 120             if (c == null) c = (Float)Annotationtype.QUBIT_CONC.getAnnotationValue(dc, e);
6220 20 Apr 21 nicklas 121             conc.add(c);
4153 05 Oct 16 nicklas 122             
6326 14 Jun 21 nicklas 123             ItemSubtype subtype = e.getItemSubtype();          
6326 14 Jun 21 nicklas 124             if (!outtakeType.equals(subtype))
6326 14 Jun 21 nicklas 125             {
6326 14 Jun 21 nicklas 126               numIncorrectSubtype++;
6326 14 Jun 21 nicklas 127             }
4994 02 Oct 18 nicklas 128             
4994 02 Oct 18 nicklas 129             if (Annotationtype.DO_NOT_USE.getAnnotationValue(dc, e) != null)
4994 02 Oct 18 nicklas 130             {
4994 02 Oct 18 nicklas 131               numDoNotUse++;
4994 02 Oct 18 nicklas 132             }
4153 05 Oct 16 nicklas 133           }
4153 05 Oct 16 nicklas 134           
4153 05 Oct 16 nicklas 135           jsonList.put("remainingQuantity", remainingQuantity.asJSONObject());
6220 20 Apr 21 nicklas 136           jsonList.put("conc", conc.asJSONObject());
4994 02 Oct 18 nicklas 137           jsonList.put("numDoNotUse", numDoNotUse);
6326 14 Jun 21 nicklas 138           jsonList.put("numIncorrectSubtype", numIncorrectSubtype);
4153 05 Oct 16 nicklas 139         }
4153 05 Oct 16 nicklas 140         
6326 14 Jun 21 nicklas 141         if (memberSubtype != null)
4153 05 Oct 16 nicklas 142         {
4153 05 Oct 16 nicklas 143           jsonList.put("subtype", Subtype.get(memberSubtype).asJSONObject(dc));
4153 05 Oct 16 nicklas 144         }
4153 05 Oct 16 nicklas 145         
4153 05 Oct 16 nicklas 146         json.put("list", jsonList);
4146 03 Oct 16 nicklas 147       }
4157 07 Oct 16 nicklas 148       else if ("GetOuttakeWorklists".equals(cmd))
4157 07 Oct 16 nicklas 149       {
6336 16 Jun 21 nicklas 150         dc = sc.newDbControl("Register outtake");
4157 07 Oct 16 nicklas 151         
4157 07 Oct 16 nicklas 152         ItemQuery<ItemList> query = ItemList.getQuery();
4157 07 Oct 16 nicklas 153         query.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
4157 07 Oct 16 nicklas 154         query.restrict(Restrictions.eq(Hql.property("externalId"), Expressions.string(Reggie.OUTTAKE_ALIQUOTS_LIST_ID)));
4168 21 Oct 16 nicklas 155         if (Values.getBoolean(req.getParameter("forDelivery")))
4157 07 Oct 16 nicklas 156         {
4179 27 Oct 16 nicklas 157           // OuttakeResult == Successful
4179 27 Oct 16 nicklas 158           query.join(Annotations.innerJoin(null, Annotationtype.OUTTAKE_RESULT.load(dc), "ors"));
4179 27 Oct 16 nicklas 159           query.restrict(Restrictions.eq(Hql.alias("ors"), Expressions.string("Successful")));
4179 27 Oct 16 nicklas 160           
4168 21 Oct 16 nicklas 161           // CompletedDate != null AND DeliveredDate == null
4168 21 Oct 16 nicklas 162           query.join(Annotations.innerJoin(null, Annotationtype.OUTTAKE_COMPLETED_DATE.load(dc), "ocd"));
4168 21 Oct 16 nicklas 163           query.join(Annotations.leftJoin(null, Annotationtype.OUTTAKE_DELIVERED_DATE.load(dc), "odd"));
4168 21 Oct 16 nicklas 164           query.restrict(Restrictions.eq(Hql.alias("odd"), null));
4168 21 Oct 16 nicklas 165         }
4168 21 Oct 16 nicklas 166         else
4168 21 Oct 16 nicklas 167         {
4168 21 Oct 16 nicklas 168           // CompletedDate == null
4157 07 Oct 16 nicklas 169           query.join(Annotations.leftJoin(null, Annotationtype.OUTTAKE_COMPLETED_DATE.load(dc), "cd"));
4157 07 Oct 16 nicklas 170           query.restrict(Restrictions.eq(Hql.alias("cd"), null));
4157 07 Oct 16 nicklas 171         }
4157 07 Oct 16 nicklas 172         query.order(Orders.asc(Hql.property("name")));
4157 07 Oct 16 nicklas 173         
4157 07 Oct 16 nicklas 174         List<ItemList> result = query.list(dc);
4157 07 Oct 16 nicklas 175         JSONArray jsonLists = new JSONArray();
4157 07 Oct 16 nicklas 176         for (ItemList list: result)
4157 07 Oct 16 nicklas 177         {
4157 07 Oct 16 nicklas 178           JSONObject jsonList = new JSONObject();
4157 07 Oct 16 nicklas 179           
4157 07 Oct 16 nicklas 180           jsonList.put("id", list.getId());
4157 07 Oct 16 nicklas 181           jsonList.put("name", list.getName());
4179 27 Oct 16 nicklas 182           jsonList.put("itemType", list.getMemberType().name());
4157 07 Oct 16 nicklas 183           jsonList.put("size", list.getSize());
4157 07 Oct 16 nicklas 184           jsonList.put("comments", list.getDescription());
6326 14 Jun 21 nicklas 185           jsonList.put("parentType", Annotationtype.OUTTAKE_PARENT_TYPE.getAnnotationValue(dc, list));
4157 07 Oct 16 nicklas 186           jsonLists.add(jsonList);
4157 07 Oct 16 nicklas 187         }
4157 07 Oct 16 nicklas 188         json.put("workLists", jsonLists);
4157 07 Oct 16 nicklas 189       }
4157 07 Oct 16 nicklas 190       else if ("DownloadLabels".equals(cmd))
4157 07 Oct 16 nicklas 191       {
4157 07 Oct 16 nicklas 192         json = null; // No JSON output
4157 07 Oct 16 nicklas 193         
4157 07 Oct 16 nicklas 194         int workListId = Values.getInt(req.getParameter("workListId"));
4166 21 Oct 16 nicklas 195         boolean useExternalId = Values.getBoolean(req.getParameter("useExternalId"));
5310 15 Feb 19 nicklas 196         String format = Values.getString(req.getParameter("format"), "csv");
4146 03 Oct 16 nicklas 197
6336 16 Jun 21 nicklas 198         dc = sc.newDbControl(":Labels for outtake");
4157 07 Oct 16 nicklas 199         
4157 07 Oct 16 nicklas 200         ItemList workList = ItemList.getById(dc, workListId);
4157 07 Oct 16 nicklas 201         
6183 26 Mar 21 nicklas 202         ItemQuery<Extract> memberQuery = workList.getMembers();
4157 07 Oct 16 nicklas 203         memberQuery.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
4157 07 Oct 16 nicklas 204         // Sort by bioplate position
4157 07 Oct 16 nicklas 205         memberQuery.join(Hql.leftJoin("bioWell", "bw"));
4157 07 Oct 16 nicklas 206         memberQuery.join(Hql.leftJoin("bw", "bioPlate", "bp", null, false));
4157 07 Oct 16 nicklas 207         memberQuery.order(Orders.asc(Hql.property("bp", "name")));
4157 07 Oct 16 nicklas 208         memberQuery.order(Orders.asc(Hql.property("bw", "row")));
4157 07 Oct 16 nicklas 209         memberQuery.order(Orders.asc(Hql.property("bw", "column")));
5310 15 Feb 19 nicklas 210         String filename = Reggie.makeSafeFileName(workList.getName())+"-labels";
5310 15 Feb 19 nicklas 211
5310 15 Feb 19 nicklas 212         TableWriter tw = null;
5310 15 Feb 19 nicklas 213         XlsxTableWriter xls = null;
5310 15 Feb 19 nicklas 214         if ("xlsx".equals(format))
5310 15 Feb 19 nicklas 215         {
5310 15 Feb 19 nicklas 216           resp.setHeader("Content-Disposition", "attachment; filename="+filename+".xlsx");
5371 16 Apr 19 nicklas 217           resp.setContentType(XlsxToCsvUtil.XLSX_MIME_TYPE);
5310 15 Feb 19 nicklas 218           xls = new XlsxTableWriter("Labels");
5310 15 Feb 19 nicklas 219           tw = xls;
5310 15 Feb 19 nicklas 220         }
5310 15 Feb 19 nicklas 221         else
5310 15 Feb 19 nicklas 222         {
5310 15 Feb 19 nicklas 223           resp.setHeader("Content-Disposition", "attachment; filename="+filename+".csv");
5310 15 Feb 19 nicklas 224           resp.setContentType("text/plain");
5310 15 Feb 19 nicklas 225           resp.setCharacterEncoding("UTF-8");
5310 15 Feb 19 nicklas 226           tw = new TableWriter(resp.getWriter());
5310 15 Feb 19 nicklas 227           // The LABEL header is only required in the CSV format
5310 15 Feb 19 nicklas 228           tw.tablePrintData("LABEL");
5310 15 Feb 19 nicklas 229         }
4157 07 Oct 16 nicklas 230         
4157 07 Oct 16 nicklas 231         
4157 07 Oct 16 nicklas 232         Iterator<Extract> it = memberQuery.iterate(dc);
4157 07 Oct 16 nicklas 233         while (it.hasNext())
4157 07 Oct 16 nicklas 234         {
4157 07 Oct 16 nicklas 235           Extract aliquot = it.next();
4166 21 Oct 16 nicklas 236           String name = aliquot.getName();
4168 21 Oct 16 nicklas 237           if (useExternalId && aliquot.getExternalId() != null)
4166 21 Oct 16 nicklas 238           {
4168 21 Oct 16 nicklas 239             name = aliquot.getExternalId();
4166 21 Oct 16 nicklas 240           }
5310 15 Feb 19 nicklas 241           tw.tablePrintData(name);
4157 07 Oct 16 nicklas 242         }
4157 07 Oct 16 nicklas 243         
5310 15 Feb 19 nicklas 244         if (xls != null) 
5310 15 Feb 19 nicklas 245         {
5310 15 Feb 19 nicklas 246           xls.getSheet().setColumnWidth(0, 18*256);
5310 15 Feb 19 nicklas 247           xls.saveTo(resp.getOutputStream());
5310 15 Feb 19 nicklas 248           xls.close();
5310 15 Feb 19 nicklas 249         }
5310 15 Feb 19 nicklas 250         tw.flush();
4157 07 Oct 16 nicklas 251         
4157 07 Oct 16 nicklas 252       }
4164 20 Oct 16 nicklas 253       else if ("GetOuttakeInfo".equals(cmd))
4164 20 Oct 16 nicklas 254       {
4164 20 Oct 16 nicklas 255         int workListId = Values.getInt(req.getParameter("workListId"));
4166 21 Oct 16 nicklas 256         boolean useExternalId = Values.getBoolean(req.getParameter("useExternalId"));
4157 07 Oct 16 nicklas 257
6336 16 Jun 21 nicklas 258         dc = sc.newDbControl(":Lab protocol for outtake");
4164 20 Oct 16 nicklas 259         SnapshotManager manager = new SnapshotManager();
4164 20 Oct 16 nicklas 260         
4164 20 Oct 16 nicklas 261         ItemList list = ItemList.getById(dc, workListId);
4164 20 Oct 16 nicklas 262         JSONObject jsonList = new JSONObject();
4164 20 Oct 16 nicklas 263         jsonList.put("id", list.getId());
4164 20 Oct 16 nicklas 264         jsonList.put("name", list.getName());
4164 20 Oct 16 nicklas 265         jsonList.put("size", list.getSize());
4164 20 Oct 16 nicklas 266         jsonList.put("comments", list.getDescription());
4165 21 Oct 16 nicklas 267         
4165 21 Oct 16 nicklas 268         Float targetAmount = (Float)Annotationtype.OUTTAKE_TARGET_AMOUNT.getAnnotationValue(dc, manager, list);
4165 21 Oct 16 nicklas 269         Float targetVolume = (Float)Annotationtype.OUTTAKE_TARGET_VOLUME.getAnnotationValue(dc, manager, list);
4165 21 Oct 16 nicklas 270         
4168 21 Oct 16 nicklas 271         jsonList.put("TargetAmount", targetAmount);
4168 21 Oct 16 nicklas 272         jsonList.put("TargetVolume", targetVolume);
6326 14 Jun 21 nicklas 273         jsonList.put("parentType", Annotationtype.OUTTAKE_PARENT_TYPE.getAnnotationValue(dc, manager, list));
4164 20 Oct 16 nicklas 274         
6183 26 Mar 21 nicklas 275         ItemQuery<Extract> memberQuery = list.getMembers();
4164 20 Oct 16 nicklas 276         memberQuery.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
4164 20 Oct 16 nicklas 277         // Sort by bioplate position
4164 20 Oct 16 nicklas 278         memberQuery.join(Hql.leftJoin("bioWell", "bw"));
4164 20 Oct 16 nicklas 279         memberQuery.join(Hql.leftJoin("bw", "bioPlate", "bp", null, false));
4164 20 Oct 16 nicklas 280         memberQuery.order(Orders.asc(Hql.property("bp", "name")));
4164 20 Oct 16 nicklas 281         memberQuery.order(Orders.asc(Hql.property("bw", "row")));
4164 20 Oct 16 nicklas 282         memberQuery.order(Orders.asc(Hql.property("bw", "column")));
4164 20 Oct 16 nicklas 283         
4164 20 Oct 16 nicklas 284         JSONArray jsonAliquots = new JSONArray();
4164 20 Oct 16 nicklas 285         Iterator<Extract> it = memberQuery.iterate(dc);
4164 20 Oct 16 nicklas 286         while (it.hasNext())
4164 20 Oct 16 nicklas 287         {
4164 20 Oct 16 nicklas 288           Extract aliquot = it.next();
4164 20 Oct 16 nicklas 289           Extract parent = (Extract)aliquot.getParent();
4164 20 Oct 16 nicklas 290           
4164 20 Oct 16 nicklas 291           JSONObject jsonA = new JSONObject();
4164 20 Oct 16 nicklas 292           jsonA.put("id", aliquot.getId());
4164 20 Oct 16 nicklas 293           jsonA.put("name", aliquot.getName());
4166 21 Oct 16 nicklas 294           if (useExternalId)
4166 21 Oct 16 nicklas 295           {
4168 21 Oct 16 nicklas 296             jsonA.put("externalId", aliquot.getExternalId());
4166 21 Oct 16 nicklas 297           }
4168 21 Oct 16 nicklas 298           jsonA.put("bioWell", JsonUtil.getBioWellAsJSON(aliquot.getBioWell(), true));
4168 21 Oct 16 nicklas 299
4165 21 Oct 16 nicklas 300           Float remain = parent.getRemainingQuantity();
6220 20 Apr 21 nicklas 301           Float conc = (Float)Annotationtype.ND_CONC.getAnnotationValue(dc, parent);
6220 20 Apr 21 nicklas 302           if (conc == null) conc = (Float)Annotationtype.QUBIT_CONC.getAnnotationValue(dc, parent);
4165 21 Oct 16 nicklas 303           
6326 14 Jun 21 nicklas 304           if (targetAmount != null && targetVolume != null)
4165 21 Oct 16 nicklas 305           {
6326 14 Jun 21 nicklas 306             // Aliquots should be normalized
6326 14 Jun 21 nicklas 307             if (remain != null && conc != null)
6326 14 Jun 21 nicklas 308             {
6326 14 Jun 21 nicklas 309               Dilution d = new Dilution(targetAmount, targetVolume, conc, remain);
6326 14 Jun 21 nicklas 310               jsonA.put("dilution", d.asJSONObject());
6326 14 Jun 21 nicklas 311             }
4165 21 Oct 16 nicklas 312           }
6326 14 Jun 21 nicklas 313           else if (targetVolume != null)
6326 14 Jun 21 nicklas 314           {
6326 14 Jun 21 nicklas 315             if (remain != null)
6326 14 Jun 21 nicklas 316             {
6326 14 Jun 21 nicklas 317               // A specific volume is needed (remaining quantity is expcted to be µl)
6326 14 Jun 21 nicklas 318               Dilution d = new Dilution(targetVolume, remain);
6326 14 Jun 21 nicklas 319               jsonA.put("dilution", d.asJSONObject());
6326 14 Jun 21 nicklas 320             }
6326 14 Jun 21 nicklas 321           }
4165 21 Oct 16 nicklas 322           
4164 20 Oct 16 nicklas 323           JSONObject jsonP = new JSONObject();
4164 20 Oct 16 nicklas 324           jsonP.put("id", parent.getId());
4164 20 Oct 16 nicklas 325           jsonP.put("name", parent.getName());
6730 05 May 22 nicklas 326           jsonP.put("label", Annotationtype.TUBE_LABEL.getAnnotationValue(dc, parent));
4164 20 Oct 16 nicklas 327           jsonP.put("bioWell", JsonUtil.getBioWellAsJSON(parent.getBioWell(), true));
6220 20 Apr 21 nicklas 328           jsonP.put("conc", conc);
4166 21 Oct 16 nicklas 329           jsonP.put("remainingQuantity", remain);
4164 20 Oct 16 nicklas 330           
4164 20 Oct 16 nicklas 331           jsonA.put("parent", jsonP);
4164 20 Oct 16 nicklas 332           jsonAliquots.add(jsonA);
4164 20 Oct 16 nicklas 333         }
4164 20 Oct 16 nicklas 334         
4164 20 Oct 16 nicklas 335         json.put("list", jsonList);
4164 20 Oct 16 nicklas 336         json.put("aliquots", jsonAliquots);
4164 20 Oct 16 nicklas 337       }
4173 24 Oct 16 nicklas 338       else if ("DownloadDeliveryFile".equals(cmd))
4173 24 Oct 16 nicklas 339       {
4173 24 Oct 16 nicklas 340         json = null; // No JSON output
4173 24 Oct 16 nicklas 341         
4173 24 Oct 16 nicklas 342         int workListId = Values.getInt(req.getParameter("workListId"));
4173 24 Oct 16 nicklas 343         boolean useExternalId = Values.getBoolean(req.getParameter("useExternalId"));
4164 20 Oct 16 nicklas 344
6336 16 Jun 21 nicklas 345         dc = sc.newDbControl(":Export outtake delivery data");
4173 24 Oct 16 nicklas 346         
4173 24 Oct 16 nicklas 347         ItemList workList = ItemList.getById(dc, workListId);
6326 14 Jun 21 nicklas 348         Float targetAmount = (Float)Annotationtype.OUTTAKE_TARGET_AMOUNT.getAnnotationValue(dc, workList);
6326 14 Jun 21 nicklas 349         Float targetVolume = (Float)Annotationtype.OUTTAKE_TARGET_VOLUME.getAnnotationValue(dc, workList);
6326 14 Jun 21 nicklas 350       
6183 26 Mar 21 nicklas 351         ItemQuery<Extract> memberQuery = workList.getMembers();
4173 24 Oct 16 nicklas 352         memberQuery.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
4173 24 Oct 16 nicklas 353         // Sort by bioplate position
4173 24 Oct 16 nicklas 354         memberQuery.join(Hql.leftJoin("bioWell", "bw"));
4173 24 Oct 16 nicklas 355         memberQuery.join(Hql.leftJoin("bw", "bioPlate", "bp", null, false));
4173 24 Oct 16 nicklas 356         memberQuery.order(Orders.asc(Hql.property("bp", "name")));
4173 24 Oct 16 nicklas 357         memberQuery.order(Orders.asc(Hql.property("bw", "row")));
4173 24 Oct 16 nicklas 358         memberQuery.order(Orders.asc(Hql.property("bw", "column")));
4173 24 Oct 16 nicklas 359         String filename = Reggie.makeSafeFileName(workList.getName());
4173 24 Oct 16 nicklas 360         
4173 24 Oct 16 nicklas 361         resp.setHeader("Content-Disposition", "attachment; filename="+filename+".txt");
4173 24 Oct 16 nicklas 362         resp.setContentType("text/plain");
4173 24 Oct 16 nicklas 363         resp.setCharacterEncoding("UTF-8");
4173 24 Oct 16 nicklas 364         
4173 24 Oct 16 nicklas 365         PrintWriter out = resp.getWriter();
4173 24 Oct 16 nicklas 366         
4173 24 Oct 16 nicklas 367         TableWriter tw = new TableWriter(out);
6326 14 Jun 21 nicklas 368         Object[] data = null;
6326 14 Jun 21 nicklas 369         if (targetAmount != null && targetVolume != null)
6326 14 Jun 21 nicklas 370         {
6326 14 Jun 21 nicklas 371           tw.tablePrintData("Name", "Box", "Position", "Quantity (µg)", "Concentration (ng/µl)");
6326 14 Jun 21 nicklas 372           data = new Object[5];
6326 14 Jun 21 nicklas 373         }
6326 14 Jun 21 nicklas 374         else
6326 14 Jun 21 nicklas 375         {
6326 14 Jun 21 nicklas 376           tw.tablePrintData("Name", "Box", "Position", "Quantity (µl)");
6326 14 Jun 21 nicklas 377           data = new Object[4];
6326 14 Jun 21 nicklas 378         }
4173 24 Oct 16 nicklas 379         
4173 24 Oct 16 nicklas 380         Iterator<Extract> it = memberQuery.iterate(dc);
4173 24 Oct 16 nicklas 381         while (it.hasNext())
4173 24 Oct 16 nicklas 382         {
4173 24 Oct 16 nicklas 383           Extract aliquot = it.next();
4173 24 Oct 16 nicklas 384           BioWell well = aliquot.getBioWell();
4173 24 Oct 16 nicklas 385           String name = aliquot.getName();
4173 24 Oct 16 nicklas 386           if (useExternalId && aliquot.getExternalId() != null)
4173 24 Oct 16 nicklas 387           {
4173 24 Oct 16 nicklas 388             name = aliquot.getExternalId();
4173 24 Oct 16 nicklas 389           }
4173 24 Oct 16 nicklas 390           data[0] = name;
4173 24 Oct 16 nicklas 391           data[1] = well.getPlate().getName();
4173 24 Oct 16 nicklas 392           data[2] = well.getCoordinate();
4173 24 Oct 16 nicklas 393           data[3] = Values.formatNumber(aliquot.getRemainingQuantity(), 1);
6326 14 Jun 21 nicklas 394           if (data.length > 4)
6326 14 Jun 21 nicklas 395           {
6326 14 Jun 21 nicklas 396             data[4] = Values.formatNumber((Float)Annotationtype.DILUTION_CONC.getAnnotationValue(dc, aliquot), 1);
6326 14 Jun 21 nicklas 397           }
4173 24 Oct 16 nicklas 398           tw.tablePrintData(data);
4173 24 Oct 16 nicklas 399         }
4173 24 Oct 16 nicklas 400         
4173 24 Oct 16 nicklas 401         tw.flush();
4173 24 Oct 16 nicklas 402         out.flush();
4173 24 Oct 16 nicklas 403         out.close();
4173 24 Oct 16 nicklas 404       }
4173 24 Oct 16 nicklas 405
4146 03 Oct 16 nicklas 406     }
4146 03 Oct 16 nicklas 407     catch (Throwable t)
4146 03 Oct 16 nicklas 408     {
4146 03 Oct 16 nicklas 409       t.printStackTrace();
4157 07 Oct 16 nicklas 410       if (json != null)
4157 07 Oct 16 nicklas 411       {
4157 07 Oct 16 nicklas 412         json.clear();
4157 07 Oct 16 nicklas 413         json.put("status", "error");
4157 07 Oct 16 nicklas 414         json.put("message", t.getMessage());
4157 07 Oct 16 nicklas 415         json.put("stacktrace", ThrowableUtil.stackTraceToString(t));
4157 07 Oct 16 nicklas 416       }
4157 07 Oct 16 nicklas 417       else
4157 07 Oct 16 nicklas 418       {
4157 07 Oct 16 nicklas 419         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, t.getMessage());
4157 07 Oct 16 nicklas 420       }
4146 03 Oct 16 nicklas 421     }
4146 03 Oct 16 nicklas 422     finally
4146 03 Oct 16 nicklas 423     {
4146 03 Oct 16 nicklas 424       if (dc != null) dc.close();
5310 15 Feb 19 nicklas 425       if (json != null)
5310 15 Feb 19 nicklas 426       {
5310 15 Feb 19 nicklas 427         json.writeJSONString(resp.getWriter());
5310 15 Feb 19 nicklas 428       }
4146 03 Oct 16 nicklas 429     }
4146 03 Oct 16 nicklas 430   }
4146 03 Oct 16 nicklas 431   
4146 03 Oct 16 nicklas 432   @SuppressWarnings("unchecked")
4146 03 Oct 16 nicklas 433   @Override
4146 03 Oct 16 nicklas 434   protected void doPost(HttpServletRequest req, HttpServletResponse resp)
4146 03 Oct 16 nicklas 435     throws ServletException, IOException 
4146 03 Oct 16 nicklas 436   {
4146 03 Oct 16 nicklas 437     String cmd = req.getParameter("cmd");
4146 03 Oct 16 nicklas 438     JsonUtil.setJsonResponseHeaders(resp);
4146 03 Oct 16 nicklas 439     
4146 03 Oct 16 nicklas 440     JSONObject json = new JSONObject();
4146 03 Oct 16 nicklas 441     JSONArray jsonMessages = new JSONArray();
4146 03 Oct 16 nicklas 442     json.put("status", "ok");
4146 03 Oct 16 nicklas 443     
4146 03 Oct 16 nicklas 444     final SessionControl sc = Reggie.getSessionControl(req);
4146 03 Oct 16 nicklas 445     DbControl dc = null;
4146 03 Oct 16 nicklas 446       
4146 03 Oct 16 nicklas 447     try
4146 03 Oct 16 nicklas 448     {
4146 03 Oct 16 nicklas 449       if ("DefineOuttake".equals(cmd))
4146 03 Oct 16 nicklas 450       {
6336 16 Jun 21 nicklas 451         dc = sc.newDbControl(":Define outtake wizard");
4146 03 Oct 16 nicklas 452
4146 03 Oct 16 nicklas 453         ReggieRole.checkPermission(dc, "'" + cmd + "' wizard", ReggieRole.PREP_CURATOR, ReggieRole.ADMINISTRATOR);
4146 03 Oct 16 nicklas 454
4146 03 Oct 16 nicklas 455         JSONObject jsonReq = JsonUtil.parseRequest(req);
4154 06 Oct 16 nicklas 456         
4154 06 Oct 16 nicklas 457         Number sourceListId = (Number)jsonReq.get("sourceList");
4154 06 Oct 16 nicklas 458         ItemList sourceList = ItemList.getById(dc, sourceListId.intValue());
4154 06 Oct 16 nicklas 459         String comments = (String)jsonReq.get("comments");
4154 06 Oct 16 nicklas 460         
6326 14 Jun 21 nicklas 461         Number targetAmount = (Number)jsonReq.get("targetAmount");
6326 14 Jun 21 nicklas 462         Number targetVolume = (Number)jsonReq.get("targetVolume");
6326 14 Jun 21 nicklas 463         ItemSubtype outtakeType = Subtype.getByCName((String)jsonReq.get("outtakeType")).get(dc);
4154 06 Oct 16 nicklas 464         
4154 06 Oct 16 nicklas 465         ItemList aliquotList = ItemList.getNew(dc, sourceList.getMemberType());
4154 06 Oct 16 nicklas 466         aliquotList.setName((String)jsonReq.get("outtakeName"));
4154 06 Oct 16 nicklas 467         aliquotList.setDescription(comments);
4154 06 Oct 16 nicklas 468         aliquotList.setExternalId(Reggie.OUTTAKE_ALIQUOTS_LIST_ID);
4154 06 Oct 16 nicklas 469         dc.saveItem(aliquotList);
4154 06 Oct 16 nicklas 470         
6326 14 Jun 21 nicklas 471         AnyToAny sourceLink = AnyToAny.getNew(dc, aliquotList, sourceList, "Source list", false);
6326 14 Jun 21 nicklas 472         sourceLink.setDescription("This is the source list that contain parent items for the aliquots in this list");
6326 14 Jun 21 nicklas 473         dc.saveItem(sourceLink);
6326 14 Jun 21 nicklas 474         
4154 06 Oct 16 nicklas 475         Annotationtype.OUTTAKE_TARGET_AMOUNT.setAnnotationValue(dc, aliquotList, targetAmount);
4154 06 Oct 16 nicklas 476         Annotationtype.OUTTAKE_TARGET_VOLUME.setAnnotationValue(dc, aliquotList, targetVolume);
6326 14 Jun 21 nicklas 477         Annotationtype.OUTTAKE_PARENT_TYPE.setAnnotationValue(dc, aliquotList, outtakeType.getName());
4154 06 Oct 16 nicklas 478         
4893 09 Jul 18 nicklas 479         // Query for finding existing child aliquots
4154 06 Oct 16 nicklas 480         ItemQuery<Extract> aliquotQuery = Extract.getQuery();
4154 06 Oct 16 nicklas 481         aliquotQuery.join(Hql.innerJoin("creationEvent", Item.BIOMATERIALEVENT.getAlias()));
4154 06 Oct 16 nicklas 482         aliquotQuery.join(Hql.innerJoin(Item.BIOMATERIALEVENT.getAlias(), "sources", "src"));
4154 06 Oct 16 nicklas 483         aliquotQuery.restrict(Restrictions.eq(Hql.index("src", null), Hql.entityParameter("parent", Item.EXTRACT)));
4154 06 Oct 16 nicklas 484         aliquotQuery.restrict(Restrictions.like(Hql.property("name"), Expressions.parameter("name")));
4154 06 Oct 16 nicklas 485         
4154 06 Oct 16 nicklas 486         // Load items in the source list
6183 26 Mar 21 nicklas 487         ItemQuery<Extract> memberQuery = sourceList.getMembers();
4154 06 Oct 16 nicklas 488         memberQuery.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
4154 06 Oct 16 nicklas 489         // Sort by bioplate position
4154 06 Oct 16 nicklas 490         memberQuery.join(Hql.leftJoin("bioWell", "bw"));
4154 06 Oct 16 nicklas 491         memberQuery.join(Hql.leftJoin("bw", "bioPlate", "bp", null, false));
4154 06 Oct 16 nicklas 492         memberQuery.order(Orders.asc(Hql.property("bp", "name")));
4154 06 Oct 16 nicklas 493         memberQuery.order(Orders.asc(Hql.property("bw", "row")));
4154 06 Oct 16 nicklas 494         memberQuery.order(Orders.asc(Hql.property("bw", "column")));
4154 06 Oct 16 nicklas 495         
4154 06 Oct 16 nicklas 496         // Prepare storage boxes
4154 06 Oct 16 nicklas 497         PlateGeometry geometry = Geometry.NINE_BY_NINE.load(dc);
4154 06 Oct 16 nicklas 498         //PlateGeometry geometry = Geometry.THREE_BY_TWO.load(dc);
4154 06 Oct 16 nicklas 499         BioPlateType storageType = BioplateType.STORAGE_BOX.load(dc);
4154 06 Oct 16 nicklas 500         BioPlate aliquotBox = null;
4154 06 Oct 16 nicklas 501         int row = 0;
4154 06 Oct 16 nicklas 502         int column = 0;
4154 06 Oct 16 nicklas 503         
4893 09 Jul 18 nicklas 504         // Storage box name parameters for outtakes
4154 06 Oct 16 nicklas 505         String boxPrefix = "ScanB-Outtake";
4893 09 Jul 18 nicklas 506         int numDigitsInBoxName = 3;        
4154 06 Oct 16 nicklas 507         
4154 06 Oct 16 nicklas 508         Iterator<Extract> it = memberQuery.iterate(dc);
4154 06 Oct 16 nicklas 509         while (it.hasNext())
4154 06 Oct 16 nicklas 510         {
4154 06 Oct 16 nicklas 511           Extract parent = it.next();
4154 06 Oct 16 nicklas 512           Extract aliquot = Extract.getNew(dc);
4154 06 Oct 16 nicklas 513           
4893 09 Jul 18 nicklas 514           // Check existing aliquots and generate new name for this aliquot
4154 06 Oct 16 nicklas 515           aliquotQuery.setEntityParameter("parent", parent);
4893 09 Jul 18 nicklas 516           aliquotQuery.setParameter("name", parent.getName()+".a%", Type.STRING);
4893 09 Jul 18 nicklas 517           String aliquotName = ReggieItem.getNextChildItemName(dc, parent.getName(), aliquotQuery, "a", true);
4893 09 Jul 18 nicklas 518           aliquot.setName(aliquotName);
4154 06 Oct 16 nicklas 519           aliquot.setDescription(comments);
4146 03 Oct 16 nicklas 520
4154 06 Oct 16 nicklas 521           // Link to parent item
4154 06 Oct 16 nicklas 522           BioMaterialEvent creationEvent = aliquot.getCreationEvent();
4154 06 Oct 16 nicklas 523           creationEvent.setSource(parent);
4154 06 Oct 16 nicklas 524
4168 21 Oct 16 nicklas 525           // Find parent item (=Specimen) with External ID and copy it to the aliquot
4168 21 Oct 16 nicklas 526           BioMaterial specimen = findParentWithExternalId(parent);
4168 21 Oct 16 nicklas 527           if (specimen != null) 
4168 21 Oct 16 nicklas 528           {
4168 21 Oct 16 nicklas 529             aliquot.setExternalId(aliquot.getName().replace(specimen.getName(), specimen.getExternalId()));
4168 21 Oct 16 nicklas 530           }
4168 21 Oct 16 nicklas 531           
4154 06 Oct 16 nicklas 532           // Place in storage box
4154 06 Oct 16 nicklas 533           if (aliquotBox == null)
4154 06 Oct 16 nicklas 534           {
4893 09 Jul 18 nicklas 535             ItemQuery<BioPlate> boxQuery = BioPlate.getQuery();
4893 09 Jul 18 nicklas 536             boxQuery.include(Include.ALL);
4893 09 Jul 18 nicklas 537             String boxName = ReggieItem.getNextItemName(dc, boxQuery, boxPrefix, numDigitsInBoxName, true);
4154 06 Oct 16 nicklas 538             aliquotBox = BioPlate.getNew(dc, geometry, storageType);
4893 09 Jul 18 nicklas 539             aliquotBox.setName(boxName);
4154 06 Oct 16 nicklas 540             aliquotBox.setDescription(comments);
4154 06 Oct 16 nicklas 541             dc.saveItem(aliquotBox);
4154 06 Oct 16 nicklas 542           }
4154 06 Oct 16 nicklas 543           aliquot.setBioWell(aliquotBox.getBioWell(row, column));
4154 06 Oct 16 nicklas 544           //jsonMessages.add("Created: " + aliquot.getName() + "; " +aliquotBox + aliquot.getBioWell().getCoordinate());
4154 06 Oct 16 nicklas 545
4154 06 Oct 16 nicklas 546           // Move to the next position/box
4154 06 Oct 16 nicklas 547           column++;
4154 06 Oct 16 nicklas 548           if (column >= geometry.getColumns())
4154 06 Oct 16 nicklas 549           {
4154 06 Oct 16 nicklas 550             row++;
4154 06 Oct 16 nicklas 551             column = 0;
4154 06 Oct 16 nicklas 552             if (row >= geometry.getRows())
4154 06 Oct 16 nicklas 553             {
4154 06 Oct 16 nicklas 554               row = 0;
4154 06 Oct 16 nicklas 555               aliquotBox = null;
4154 06 Oct 16 nicklas 556             }
4154 06 Oct 16 nicklas 557           }
4154 06 Oct 16 nicklas 558           
4154 06 Oct 16 nicklas 559           dc.saveItem(aliquot);
4154 06 Oct 16 nicklas 560           aliquotList.add(aliquot);
4154 06 Oct 16 nicklas 561         }
4154 06 Oct 16 nicklas 562
4154 06 Oct 16 nicklas 563         // Protect the list from changes
4154 06 Oct 16 nicklas 564         aliquotList.setDisableManualMembers(true);
4154 06 Oct 16 nicklas 565         aliquotList.setDisableSyncFilters(true);
4154 06 Oct 16 nicklas 566         
4146 03 Oct 16 nicklas 567         dc.commit();
4154 06 Oct 16 nicklas 568         jsonMessages.add("Created " + aliquotList.getSize() + " outtake aliquots in list: " + aliquotList.getName());
4154 06 Oct 16 nicklas 569       }
4165 21 Oct 16 nicklas 570       else if ("RegisterOuttakeComplete".equals(cmd))
4165 21 Oct 16 nicklas 571       {
6336 16 Jun 21 nicklas 572         dc = sc.newDbControl(":Register outtake");
4165 21 Oct 16 nicklas 573
4173 24 Oct 16 nicklas 574         ReggieRole.checkPermission(dc, "'" + cmd + "' wizard", ReggieRole.SAMPLE_PREP, ReggieRole.ADMINISTRATOR);
4165 21 Oct 16 nicklas 575
4165 21 Oct 16 nicklas 576         JSONObject jsonReq = JsonUtil.parseRequest(req);
4165 21 Oct 16 nicklas 577         
4165 21 Oct 16 nicklas 578         Number workListId = (Number)jsonReq.get("workList");
4165 21 Oct 16 nicklas 579         String comments = (String)jsonReq.get("comments");
4165 21 Oct 16 nicklas 580         Date completedDate = Reggie.CONVERTER_STRING_TO_DATE.convert((String)jsonReq.get("completedDate"));
4179 27 Oct 16 nicklas 581         boolean failed = Boolean.TRUE.equals(jsonReq.get("failed"));
4165 21 Oct 16 nicklas 582
4165 21 Oct 16 nicklas 583         ItemList workList = ItemList.getById(dc, workListId.intValue());
4165 21 Oct 16 nicklas 584         workList.setDescription(comments);
4165 21 Oct 16 nicklas 585         Annotationtype.OUTTAKE_COMPLETED_DATE.setAnnotationValue(dc, workList, completedDate);
4179 27 Oct 16 nicklas 586         Annotationtype.OUTTAKE_RESULT.setAnnotationValue(dc, workList, failed ? "Failed" : "Successful");
4165 21 Oct 16 nicklas 587
4179 27 Oct 16 nicklas 588         boolean sampleConsumed = failed ? Boolean.TRUE.equals(jsonReq.get("sampleConsumed")) : true;
4179 27 Oct 16 nicklas 589         JSONArray jsonExcept = (JSONArray)jsonReq.get("except");
4179 27 Oct 16 nicklas 590         Set<Integer> except = new HashSet<>();
4179 27 Oct 16 nicklas 591         if (jsonExcept != null) 
4179 27 Oct 16 nicklas 592         {
4179 27 Oct 16 nicklas 593           for (Object o : jsonExcept)
4179 27 Oct 16 nicklas 594           {
4179 27 Oct 16 nicklas 595             except.add(((Number)o).intValue());
4179 27 Oct 16 nicklas 596           }
4179 27 Oct 16 nicklas 597         }
4165 21 Oct 16 nicklas 598         
6183 26 Mar 21 nicklas 599         ItemQuery<Extract> memberQuery = workList.getMembers();
4165 21 Oct 16 nicklas 600         memberQuery.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
4165 21 Oct 16 nicklas 601         // Sort by bioplate position
4165 21 Oct 16 nicklas 602         memberQuery.join(Hql.leftJoin("bioWell", "bw"));
4165 21 Oct 16 nicklas 603         memberQuery.join(Hql.leftJoin("bw", "bioPlate", "bp", null, false));
4165 21 Oct 16 nicklas 604         memberQuery.order(Orders.asc(Hql.property("bp", "name")));
4165 21 Oct 16 nicklas 605         memberQuery.order(Orders.asc(Hql.property("bw", "row")));
4165 21 Oct 16 nicklas 606         memberQuery.order(Orders.asc(Hql.property("bw", "column")));
4165 21 Oct 16 nicklas 607         
4165 21 Oct 16 nicklas 608         Float targetAmount = (Float)Annotationtype.OUTTAKE_TARGET_AMOUNT.getAnnotationValue(dc, workList);
4165 21 Oct 16 nicklas 609         Float targetVolume = (Float)Annotationtype.OUTTAKE_TARGET_VOLUME.getAnnotationValue(dc, workList);
4165 21 Oct 16 nicklas 610         
4165 21 Oct 16 nicklas 611         JSONArray jsonAliquots = new JSONArray();
4165 21 Oct 16 nicklas 612         Iterator<Extract> it = memberQuery.iterate(dc);
4179 27 Oct 16 nicklas 613         int numExceptions = 0;
4165 21 Oct 16 nicklas 614         while (it.hasNext())
4165 21 Oct 16 nicklas 615         {
4165 21 Oct 16 nicklas 616           Extract aliquot = it.next();
4165 21 Oct 16 nicklas 617           Extract parent = (Extract)aliquot.getParent();
4165 21 Oct 16 nicklas 618           BioMaterialEvent creationEvent = aliquot.getCreationEvent();
4165 21 Oct 16 nicklas 619           creationEvent.setEventDate(completedDate);
4165 21 Oct 16 nicklas 620           
4165 21 Oct 16 nicklas 621           Float remain = parent.getRemainingQuantity();
6220 20 Apr 21 nicklas 622           Float conc = (Float)Annotationtype.ND_CONC.getAnnotationValue(dc, parent);
6220 20 Apr 21 nicklas 623           if (conc == null) conc = (Float)Annotationtype.QUBIT_CONC.getAnnotationValue(dc, parent);
4165 21 Oct 16 nicklas 624           
6326 14 Jun 21 nicklas 625           if (targetAmount != null && targetVolume != null)
4165 21 Oct 16 nicklas 626           {
6326 14 Jun 21 nicklas 627             if (remain != null && conc != null)
4179 27 Oct 16 nicklas 628             {
6326 14 Jun 21 nicklas 629               boolean isException = except.contains(aliquot.getId());
6326 14 Jun 21 nicklas 630               if (isException) numExceptions++;
6326 14 Jun 21 nicklas 631               
6326 14 Jun 21 nicklas 632               if (sampleConsumed != isException)
6326 14 Jun 21 nicklas 633               {
6326 14 Jun 21 nicklas 634                 Dilution d = new Dilution(targetAmount, targetVolume, conc, remain);
6326 14 Jun 21 nicklas 635                 creationEvent.getEventSource(parent).setUsedQuantity(d.amount);
6326 14 Jun 21 nicklas 636                 aliquot.setOriginalQuantity(d.amount);
6326 14 Jun 21 nicklas 637                 Annotationtype.DILUTION_CONC.setAnnotationValue(dc, aliquot, d.getDilutedConc());
6326 14 Jun 21 nicklas 638                 //jsonMessages.add(aliquot.getId() + ":" + aliquot.getName() + ": " + ndConc + "--" + d.getDilutedConc() + ":"+isException);
6326 14 Jun 21 nicklas 639               }
6326 14 Jun 21 nicklas 640               else
6326 14 Jun 21 nicklas 641               {
6326 14 Jun 21 nicklas 642                 // jsonMessages.add(aliquot.getId() + ":" + aliquot.getName() + " not consumed: " + isException);
6326 14 Jun 21 nicklas 643               }
4179 27 Oct 16 nicklas 644             }
6326 14 Jun 21 nicklas 645           }
6326 14 Jun 21 nicklas 646           else if (targetVolume != null)
6326 14 Jun 21 nicklas 647           {
6326 14 Jun 21 nicklas 648             if (remain != null)
4179 27 Oct 16 nicklas 649             {
6326 14 Jun 21 nicklas 650               boolean isException = except.contains(aliquot.getId());
6326 14 Jun 21 nicklas 651               if (isException) numExceptions++;
6326 14 Jun 21 nicklas 652               if (sampleConsumed != isException)
6326 14 Jun 21 nicklas 653               {
6326 14 Jun 21 nicklas 654                 Dilution d = new Dilution(targetVolume, remain);
6326 14 Jun 21 nicklas 655                 creationEvent.getEventSource(parent).setUsedQuantity(d.volume);
6326 14 Jun 21 nicklas 656                 aliquot.setOriginalQuantity(d.volume);
6326 14 Jun 21 nicklas 657                 //jsonMessages.add(aliquot.getId() + ":" + aliquot.getName() + ": " + d.volume + ":"+isException);
6326 14 Jun 21 nicklas 658               }
6326 14 Jun 21 nicklas 659               else
6326 14 Jun 21 nicklas 660               {
6326 14 Jun 21 nicklas 661                 //jsonMessages.add(aliquot.getId() + ":" + aliquot.getName() + " not consumed: " + isException);
6326 14 Jun 21 nicklas 662               }
4179 27 Oct 16 nicklas 663             }
4165 21 Oct 16 nicklas 664           }
4165 21 Oct 16 nicklas 665         }
4165 21 Oct 16 nicklas 666         
4179 27 Oct 16 nicklas 667         if (failed)
4179 27 Oct 16 nicklas 668         {
4179 27 Oct 16 nicklas 669           jsonMessages.add(workList.getName() + " registered as failed");
4179 27 Oct 16 nicklas 670           String msg = sampleConsumed ? "All samples consumed" : "No samples consumed";
4179 27 Oct 16 nicklas 671           if (numExceptions > 0)
4179 27 Oct 16 nicklas 672           {
4179 27 Oct 16 nicklas 673             msg += " except " + numExceptions;
4179 27 Oct 16 nicklas 674           }
4179 27 Oct 16 nicklas 675           jsonMessages.add(msg);
4179 27 Oct 16 nicklas 676         }
4179 27 Oct 16 nicklas 677         else
4179 27 Oct 16 nicklas 678         {
4179 27 Oct 16 nicklas 679           jsonMessages.add(workList.getName() + " registered as completed");
4179 27 Oct 16 nicklas 680         }
4165 21 Oct 16 nicklas 681         dc.commit();
4165 21 Oct 16 nicklas 682       }
4173 24 Oct 16 nicklas 683       else if ("RegisterOuttakeDelivered".equals(cmd))
4173 24 Oct 16 nicklas 684       {
6336 16 Jun 21 nicklas 685         dc = sc.newDbControl(":Export outtake delivery data");
4173 24 Oct 16 nicklas 686
4173 24 Oct 16 nicklas 687         ReggieRole.checkPermission(dc, "'" + cmd + "' wizard", ReggieRole.PREP_CURATOR, ReggieRole.ADMINISTRATOR);
4173 24 Oct 16 nicklas 688
4173 24 Oct 16 nicklas 689         JSONObject jsonReq = JsonUtil.parseRequest(req);
4173 24 Oct 16 nicklas 690         
4173 24 Oct 16 nicklas 691         Number workListId = (Number)jsonReq.get("workList");
4173 24 Oct 16 nicklas 692         String comments = (String)jsonReq.get("comments");
4173 24 Oct 16 nicklas 693         Date deliveredDate = Reggie.CONVERTER_STRING_TO_DATE.convert((String)jsonReq.get("deliveredDate"));
4173 24 Oct 16 nicklas 694
4173 24 Oct 16 nicklas 695         ItemList workList = ItemList.getById(dc, workListId.intValue());
4173 24 Oct 16 nicklas 696         workList.setDescription(comments);
4173 24 Oct 16 nicklas 697         Annotationtype.OUTTAKE_DELIVERED_DATE.setAnnotationValue(dc, workList, deliveredDate);
4173 24 Oct 16 nicklas 698
4173 24 Oct 16 nicklas 699         jsonMessages.add(workList.getName() + " registered as delivered");
4173 24 Oct 16 nicklas 700         dc.commit();
4173 24 Oct 16 nicklas 701       }
4146 03 Oct 16 nicklas 702       json.put("messages", jsonMessages);
4146 03 Oct 16 nicklas 703       CounterService.getInstance().setForceCount();
4146 03 Oct 16 nicklas 704     }
4146 03 Oct 16 nicklas 705     catch (Throwable t)
4146 03 Oct 16 nicklas 706     {
4146 03 Oct 16 nicklas 707       t.printStackTrace();
4146 03 Oct 16 nicklas 708       json.clear();
4146 03 Oct 16 nicklas 709       json.put("status", "error");
4146 03 Oct 16 nicklas 710       json.put("message", t.getMessage());
4146 03 Oct 16 nicklas 711       json.put("stacktrace", ThrowableUtil.stackTraceToString(t));      
4146 03 Oct 16 nicklas 712     }
4146 03 Oct 16 nicklas 713     finally
4146 03 Oct 16 nicklas 714     {
4146 03 Oct 16 nicklas 715       if (dc != null) dc.close();
4146 03 Oct 16 nicklas 716       json.writeJSONString(resp.getWriter());
4146 03 Oct 16 nicklas 717     }
4146 03 Oct 16 nicklas 718   }
4146 03 Oct 16 nicklas 719
4166 21 Oct 16 nicklas 720   
4166 21 Oct 16 nicklas 721   private BioMaterial findParentWithExternalId(MeasuredBioMaterial item)
4166 21 Oct 16 nicklas 722   {
4166 21 Oct 16 nicklas 723     BioMaterial parent = item;
4166 21 Oct 16 nicklas 724     while (parent != null && parent.getExternalId() == null)
4166 21 Oct 16 nicklas 725     {
4166 21 Oct 16 nicklas 726       parent = item.getParent();
4166 21 Oct 16 nicklas 727       if (parent instanceof MeasuredBioMaterial) 
4166 21 Oct 16 nicklas 728       {
4166 21 Oct 16 nicklas 729         item = (MeasuredBioMaterial)parent;
4166 21 Oct 16 nicklas 730       }
4166 21 Oct 16 nicklas 731       else 
4166 21 Oct 16 nicklas 732       {
4166 21 Oct 16 nicklas 733         item = null;
4166 21 Oct 16 nicklas 734       }
4166 21 Oct 16 nicklas 735     }
4166 21 Oct 16 nicklas 736     
4166 21 Oct 16 nicklas 737     return parent;
4166 21 Oct 16 nicklas 738   }
4166 21 Oct 16 nicklas 739   
4153 05 Oct 16 nicklas 740   /**
4153 05 Oct 16 nicklas 741     Helper class for keeping track of min and max of a collection of values.
4153 05 Oct 16 nicklas 742     Also keep counters for total number of values and null values.
4153 05 Oct 16 nicklas 743   */
4153 05 Oct 16 nicklas 744   static class MinMax
4153 05 Oct 16 nicklas 745   {
4153 05 Oct 16 nicklas 746     private Float min;
4153 05 Oct 16 nicklas 747     private Float max;
4153 05 Oct 16 nicklas 748     private int nulls;
4153 05 Oct 16 nicklas 749     private int values;
4153 05 Oct 16 nicklas 750     
4153 05 Oct 16 nicklas 751     MinMax()
4153 05 Oct 16 nicklas 752     {}
4153 05 Oct 16 nicklas 753     
4153 05 Oct 16 nicklas 754     /**
4153 05 Oct 16 nicklas 755       Add a value.
4153 05 Oct 16 nicklas 756     */
4153 05 Oct 16 nicklas 757     void add(Float value)
4153 05 Oct 16 nicklas 758     {
4153 05 Oct 16 nicklas 759       values++;
4153 05 Oct 16 nicklas 760       if (value == null)
4153 05 Oct 16 nicklas 761       {
4153 05 Oct 16 nicklas 762         nulls++;
4153 05 Oct 16 nicklas 763         return;
4153 05 Oct 16 nicklas 764       }
4153 05 Oct 16 nicklas 765       if (min == null || value < min) min = value;
4153 05 Oct 16 nicklas 766       if (max == null || value > max) max = value;
4153 05 Oct 16 nicklas 767     }
4153 05 Oct 16 nicklas 768     
4153 05 Oct 16 nicklas 769     /**
4153 05 Oct 16 nicklas 770       The min value.
4153 05 Oct 16 nicklas 771     */
4153 05 Oct 16 nicklas 772     Float getMin()
4153 05 Oct 16 nicklas 773     {
4153 05 Oct 16 nicklas 774       return min;
4153 05 Oct 16 nicklas 775     }
4153 05 Oct 16 nicklas 776     
4153 05 Oct 16 nicklas 777     /**
4153 05 Oct 16 nicklas 778       The max value.
4153 05 Oct 16 nicklas 779     */
4153 05 Oct 16 nicklas 780     Float getMax()
4153 05 Oct 16 nicklas 781     {
4153 05 Oct 16 nicklas 782       return max;
4153 05 Oct 16 nicklas 783     }
4153 05 Oct 16 nicklas 784     
4153 05 Oct 16 nicklas 785     /**
4153 05 Oct 16 nicklas 786       Total number of values.
4153 05 Oct 16 nicklas 787     */
4153 05 Oct 16 nicklas 788     int getValues()
4153 05 Oct 16 nicklas 789     {
4153 05 Oct 16 nicklas 790       return values;
4153 05 Oct 16 nicklas 791     }
4153 05 Oct 16 nicklas 792     
4153 05 Oct 16 nicklas 793     /**
4153 05 Oct 16 nicklas 794       Number of null values.
4153 05 Oct 16 nicklas 795     */
4153 05 Oct 16 nicklas 796     int getNulls()
4153 05 Oct 16 nicklas 797     {
4153 05 Oct 16 nicklas 798       return nulls;
4153 05 Oct 16 nicklas 799     }
4153 05 Oct 16 nicklas 800     
4153 05 Oct 16 nicklas 801     JSONObject asJSONObject()
4153 05 Oct 16 nicklas 802     {
4153 05 Oct 16 nicklas 803       JSONObject json = new JSONObject();
4153 05 Oct 16 nicklas 804       json.put("min", min);
4153 05 Oct 16 nicklas 805       json.put("max", max);
4153 05 Oct 16 nicklas 806       json.put("values", values);
4153 05 Oct 16 nicklas 807       json.put("nulls", nulls);
4153 05 Oct 16 nicklas 808       return json;
4153 05 Oct 16 nicklas 809     }
4153 05 Oct 16 nicklas 810     
4153 05 Oct 16 nicklas 811   }
4165 21 Oct 16 nicklas 812   
4165 21 Oct 16 nicklas 813   /**
4165 21 Oct 16 nicklas 814     Calculates dilution information for an extract with known
4165 21 Oct 16 nicklas 815     remaining quantity and concentration given a target amount and
4165 21 Oct 16 nicklas 816     volume. 
4165 21 Oct 16 nicklas 817   */
4165 21 Oct 16 nicklas 818   static class Dilution
4165 21 Oct 16 nicklas 819   {
4165 21 Oct 16 nicklas 820
4165 21 Oct 16 nicklas 821     final float remainingQuantity;
4165 21 Oct 16 nicklas 822     final float conc;
4165 21 Oct 16 nicklas 823     
4165 21 Oct 16 nicklas 824     // Results
4165 21 Oct 16 nicklas 825     float amount;
4165 21 Oct 16 nicklas 826     float volume;
4165 21 Oct 16 nicklas 827     float water;
4165 21 Oct 16 nicklas 828     float dilutedConc;
4165 21 Oct 16 nicklas 829     
4165 21 Oct 16 nicklas 830     /**
6326 14 Jun 21 nicklas 831       Use a fixed target volume. If remaining quantity is lower
6326 14 Jun 21 nicklas 832       we can't take more of course.
6326 14 Jun 21 nicklas 833       @since 4.31.3
6326 14 Jun 21 nicklas 834     */
6326 14 Jun 21 nicklas 835     Dilution(float targetVolume, float remainingQuantity)
6326 14 Jun 21 nicklas 836     {
6326 14 Jun 21 nicklas 837       this.remainingQuantity = remainingQuantity;
6326 14 Jun 21 nicklas 838       this.conc = Float.NaN;
6326 14 Jun 21 nicklas 839       
6326 14 Jun 21 nicklas 840       this.volume = Math.min(targetVolume, remainingQuantity);
6326 14 Jun 21 nicklas 841       this.water = Float.NaN;
6326 14 Jun 21 nicklas 842       this.amount = Float.NaN;
6326 14 Jun 21 nicklas 843     }
6326 14 Jun 21 nicklas 844     
6326 14 Jun 21 nicklas 845     /**
4165 21 Oct 16 nicklas 846       Create a new dilution calculation.
4165 21 Oct 16 nicklas 847       @param targetAmount The target amount (µg)
4165 21 Oct 16 nicklas 848       @param targetVolume The target volume (µl)
4165 21 Oct 16 nicklas 849       @param conc The concentration of the extract (ng/µl)
4165 21 Oct 16 nicklas 850       @param remainingQuantity The remaining quantity of the extract (µg)
4165 21 Oct 16 nicklas 851      */
4165 21 Oct 16 nicklas 852     Dilution(float targetAmount, float targetVolume, float conc, float remainingQuantity)
4165 21 Oct 16 nicklas 853     {
4165 21 Oct 16 nicklas 854       this.remainingQuantity = remainingQuantity;
4165 21 Oct 16 nicklas 855       this.conc = conc;
4165 21 Oct 16 nicklas 856       
4165 21 Oct 16 nicklas 857       float targetConc = targetAmount / targetVolume;
4165 21 Oct 16 nicklas 858           
4165 21 Oct 16 nicklas 859       // Take targetAmount, but not more than what is remaining
4165 21 Oct 16 nicklas 860       amount = targetAmount;
4165 21 Oct 16 nicklas 861       if (remainingQuantity < amount)
4165 21 Oct 16 nicklas 862       {
4165 21 Oct 16 nicklas 863         amount = remainingQuantity;
4165 21 Oct 16 nicklas 864         // Adjust target volume to keep target concentration the same
4165 21 Oct 16 nicklas 865         targetVolume = amount / targetConc;
4165 21 Oct 16 nicklas 866       }
4165 21 Oct 16 nicklas 867         
4165 21 Oct 16 nicklas 868       // Calculate volume rounded up to one decimal place
4165 21 Oct 16 nicklas 869       volume = (float)Math.ceil(10000 * (amount / conc)) / 10;
4165 21 Oct 16 nicklas 870       
4165 21 Oct 16 nicklas 871       if (volume < 1f)
4165 21 Oct 16 nicklas 872       {
4165 21 Oct 16 nicklas 873         // Do not take less than 1µl
4165 21 Oct 16 nicklas 874         volume = 1f;
4165 21 Oct 16 nicklas 875         amount = volume * conc / 1000;
4165 21 Oct 16 nicklas 876         // Adjust target volume to keep concentration the same
4165 21 Oct 16 nicklas 877         targetVolume = amount / targetConc;
4165 21 Oct 16 nicklas 878       }
4165 21 Oct 16 nicklas 879       
4165 21 Oct 16 nicklas 880       // May be negative, but then we don't dilute at all
4165 21 Oct 16 nicklas 881       water = targetVolume - volume;
4165 21 Oct 16 nicklas 882     }
4165 21 Oct 16 nicklas 883     
4165 21 Oct 16 nicklas 884     /**
4165 21 Oct 16 nicklas 885       Expected concentration after dilution. Negative water volume
4165 21 Oct 16 nicklas 886       means that no dilution is done.
4165 21 Oct 16 nicklas 887     */
4165 21 Oct 16 nicklas 888     float getDilutedConc()
4165 21 Oct 16 nicklas 889     {
4165 21 Oct 16 nicklas 890       return water <= 0 ? conc : 1000 * amount / (volume + water);
4165 21 Oct 16 nicklas 891     }
4165 21 Oct 16 nicklas 892     
4165 21 Oct 16 nicklas 893     JSONObject asJSONObject()
4165 21 Oct 16 nicklas 894     {
4165 21 Oct 16 nicklas 895       JSONObject json = new JSONObject();
4165 21 Oct 16 nicklas 896       json.put("amount", amount);
4165 21 Oct 16 nicklas 897       json.put("volume", volume);
4165 21 Oct 16 nicklas 898       json.put("water", water);
4165 21 Oct 16 nicklas 899       return json;
4165 21 Oct 16 nicklas 900     }
4165 21 Oct 16 nicklas 901   }
4146 03 Oct 16 nicklas 902 }