5647 |
08 Oct 19 |
nicklas |
1 |
package net.sf.basedb.reggie.pdf; |
5647 |
08 Oct 19 |
nicklas |
2 |
|
6092 |
14 Dec 20 |
nicklas |
3 |
import java.io.ByteArrayOutputStream; |
6058 |
17 Nov 20 |
nicklas |
4 |
import java.io.FileInputStream; |
5647 |
08 Oct 19 |
nicklas |
5 |
import java.io.IOException; |
5647 |
08 Oct 19 |
nicklas |
6 |
import java.text.DecimalFormat; |
5647 |
08 Oct 19 |
nicklas |
7 |
import java.text.DecimalFormatSymbols; |
5647 |
08 Oct 19 |
nicklas |
8 |
import java.text.NumberFormat; |
6449 |
20 Oct 21 |
nicklas |
9 |
import java.time.Instant; |
6449 |
20 Oct 21 |
nicklas |
10 |
import java.time.LocalDateTime; |
6449 |
20 Oct 21 |
nicklas |
11 |
import java.time.ZoneId; |
6449 |
20 Oct 21 |
nicklas |
12 |
import java.time.temporal.ChronoUnit; |
6027 |
28 Oct 20 |
nicklas |
13 |
import java.util.ArrayList; |
5647 |
08 Oct 19 |
nicklas |
14 |
import java.util.Date; |
5647 |
08 Oct 19 |
nicklas |
15 |
import java.util.List; |
5647 |
08 Oct 19 |
nicklas |
16 |
import java.util.Map; |
5694 |
31 Oct 19 |
nicklas |
17 |
import java.util.Set; |
6027 |
28 Oct 20 |
nicklas |
18 |
import java.util.TreeSet; |
6056 |
13 Nov 20 |
nicklas |
19 |
import java.util.regex.Matcher; |
6056 |
13 Nov 20 |
nicklas |
20 |
import java.util.regex.Pattern; |
5647 |
08 Oct 19 |
nicklas |
21 |
|
7024 |
07 Feb 23 |
nicklas |
22 |
import org.apache.commons.lang3.time.FastDateFormat; |
7024 |
07 Feb 23 |
nicklas |
23 |
|
6058 |
17 Nov 20 |
nicklas |
24 |
import com.itextpdf.kernel.colors.Color; |
6058 |
17 Nov 20 |
nicklas |
25 |
import com.itextpdf.kernel.colors.ColorConstants; |
6058 |
17 Nov 20 |
nicklas |
26 |
import com.itextpdf.kernel.colors.DeviceRgb; |
6058 |
17 Nov 20 |
nicklas |
27 |
|
5647 |
08 Oct 19 |
nicklas |
28 |
import net.sf.basedb.core.BioMaterial; |
5647 |
08 Oct 19 |
nicklas |
29 |
import net.sf.basedb.core.BioSource; |
6081 |
25 Nov 20 |
nicklas |
30 |
import net.sf.basedb.core.ConfigurationException; |
5647 |
08 Oct 19 |
nicklas |
31 |
import net.sf.basedb.core.DbControl; |
5647 |
08 Oct 19 |
nicklas |
32 |
import net.sf.basedb.core.Extract; |
6216 |
16 Apr 21 |
nicklas |
33 |
import net.sf.basedb.core.Protocol; |
5647 |
08 Oct 19 |
nicklas |
34 |
import net.sf.basedb.core.RawBioAssay; |
5647 |
08 Oct 19 |
nicklas |
35 |
import net.sf.basedb.core.Sample; |
6031 |
29 Oct 20 |
nicklas |
36 |
import net.sf.basedb.core.plugin.ExportOutputStream; |
6056 |
13 Nov 20 |
nicklas |
37 |
import net.sf.basedb.reggie.Reggie; |
5647 |
08 Oct 19 |
nicklas |
38 |
import net.sf.basedb.reggie.Site; |
5647 |
08 Oct 19 |
nicklas |
39 |
import net.sf.basedb.reggie.converter.DateToStringConverter; |
6027 |
28 Oct 20 |
nicklas |
40 |
import net.sf.basedb.reggie.converter.MultiplyFloatConverter; |
6029 |
28 Oct 20 |
nicklas |
41 |
import net.sf.basedb.reggie.converter.Translater; |
5694 |
31 Oct 19 |
nicklas |
42 |
import net.sf.basedb.reggie.dao.AlignedSequences; |
5647 |
08 Oct 19 |
nicklas |
43 |
import net.sf.basedb.reggie.dao.Annotationtype; |
6027 |
28 Oct 20 |
nicklas |
44 |
import net.sf.basedb.reggie.dao.DemuxedSequences; |
6027 |
28 Oct 20 |
nicklas |
45 |
import net.sf.basedb.reggie.dao.MaskedSequences; |
6027 |
28 Oct 20 |
nicklas |
46 |
import net.sf.basedb.reggie.dao.MergedSequences; |
5647 |
08 Oct 19 |
nicklas |
47 |
import net.sf.basedb.reggie.dao.Rawbioassay; |
5647 |
08 Oct 19 |
nicklas |
48 |
import net.sf.basedb.reggie.dao.Rna; |
5647 |
08 Oct 19 |
nicklas |
49 |
import net.sf.basedb.reggie.dao.RnaQc; |
6027 |
28 Oct 20 |
nicklas |
50 |
import net.sf.basedb.reggie.dao.SequencingRun; |
5647 |
08 Oct 19 |
nicklas |
51 |
import net.sf.basedb.reggie.dao.Subtype; |
6058 |
17 Nov 20 |
nicklas |
52 |
import net.sf.basedb.reggie.pdf.PdfUtil7.Align; |
6069 |
19 Nov 20 |
nicklas |
53 |
import net.sf.basedb.reggie.pdf.PdfUtil7.Options; |
6037 |
02 Nov 20 |
nicklas |
54 |
import net.sf.basedb.reggie.script.ScanBReport; |
6036 |
02 Nov 20 |
nicklas |
55 |
import net.sf.basedb.reggie.script.ScriptResult; |
5647 |
08 Oct 19 |
nicklas |
56 |
import net.sf.basedb.reggie.ssp.SspModel; |
5647 |
08 Oct 19 |
nicklas |
57 |
import net.sf.basedb.util.MD5; |
5694 |
31 Oct 19 |
nicklas |
58 |
import net.sf.basedb.util.Values; |
6056 |
13 Nov 20 |
nicklas |
59 |
import net.sf.basedb.util.formatter.Formatter; |
5647 |
08 Oct 19 |
nicklas |
60 |
|
5647 |
08 Oct 19 |
nicklas |
61 |
/** |
5647 |
08 Oct 19 |
nicklas |
Worker implementation that runs the 'Scan-B report' R script and |
5647 |
08 Oct 19 |
nicklas |
generates a PDF document with the plots and other information. |
5647 |
08 Oct 19 |
nicklas |
64 |
|
5647 |
08 Oct 19 |
nicklas |
@author nicklas |
5647 |
08 Oct 19 |
nicklas |
@since 4.24 |
5647 |
08 Oct 19 |
nicklas |
67 |
*/ |
5647 |
08 Oct 19 |
nicklas |
68 |
public class ScanBReportWorker |
5647 |
08 Oct 19 |
nicklas |
69 |
extends PdfReportWorker |
5647 |
08 Oct 19 |
nicklas |
70 |
{ |
5647 |
08 Oct 19 |
nicklas |
71 |
|
5647 |
08 Oct 19 |
nicklas |
72 |
/** |
5647 |
08 Oct 19 |
nicklas |
The default file name for the generated pdf. |
5647 |
08 Oct 19 |
nicklas |
74 |
*/ |
5647 |
08 Oct 19 |
nicklas |
75 |
public static final String DEFAULT_PDF_NAME = "scanbreport.pdf"; |
5647 |
08 Oct 19 |
nicklas |
76 |
|
5647 |
08 Oct 19 |
nicklas |
77 |
public static final DateToStringConverter DATE_FORMAT = |
7024 |
07 Feb 23 |
nicklas |
78 |
new DateToStringConverter(FastDateFormat.getInstance("yyyy-MM-dd")); |
5647 |
08 Oct 19 |
nicklas |
79 |
|
6056 |
13 Nov 20 |
nicklas |
80 |
private static final float MARGIN_RIGHT = 585; |
5647 |
08 Oct 19 |
nicklas |
81 |
private static final float MARGIN_LEFT = 14; |
6056 |
13 Nov 20 |
nicklas |
82 |
private static final float MARGIN_BOTTOM = 5; |
5647 |
08 Oct 19 |
nicklas |
83 |
|
6056 |
13 Nov 20 |
nicklas |
// HEADER section; 4 rows with 6 columns |
6056 |
13 Nov 20 |
nicklas |
85 |
private static final float[] HEADER_COLS = { 94, 161, 235, 314, 394, 479 }; |
6056 |
13 Nov 20 |
nicklas |
86 |
private static final float[] HEADER_ROWS = { 800, 773, 746, 718 }; |
5647 |
08 Oct 19 |
nicklas |
87 |
|
6027 |
28 Oct 20 |
nicklas |
// SSP section |
6056 |
13 Nov 20 |
nicklas |
89 |
private static final float[] SSP_COLS = { 105, 122 }; |
6057 |
16 Nov 20 |
nicklas |
90 |
private static final float[] SSP_ROWS = { 648, 628.5f, 502.5f, 396.5f, 293.5f, 192.5f, 91.5f }; |
6027 |
28 Oct 20 |
nicklas |
91 |
|
6057 |
16 Nov 20 |
nicklas |
// ROR |
6057 |
16 Nov 20 |
nicklas |
93 |
private static final float[] ROR_COLS = { 290, 335, 349, 363 }; |
6057 |
16 Nov 20 |
nicklas |
94 |
private static final float[] ROR_ROWS = { 629, 610, 635, 615 }; |
6057 |
16 Nov 20 |
nicklas |
95 |
private static final float ROR_RADIUS = 4.5f; |
6075 |
23 Nov 20 |
nicklas |
96 |
private static final Color ROR_DARK_GREEN = new DeviceRgb(0, 100, 0); // Matches ROR T0 line in plot |
6080 |
25 Nov 20 |
nicklas |
97 |
private static final Color ROR_ORANGE = new DeviceRgb(250, 140, 0); // Matches ROR T1 line in plot |
6075 |
23 Nov 20 |
nicklas |
98 |
private static final Color ROR_GREEN = new DeviceRgb(50, 225, 150); // Colors for the green-yellow-red node-status circles |
6075 |
23 Nov 20 |
nicklas |
99 |
private static final Color ROR_YELLOW = new DeviceRgb(235, 235, 50); |
6075 |
23 Nov 20 |
nicklas |
100 |
private static final Color ROR_RED = new DeviceRgb(235, 75, 25); |
6056 |
13 Nov 20 |
nicklas |
101 |
|
6075 |
23 Nov 20 |
nicklas |
// CC15 icon X and Y coordinates |
6065 |
18 Nov 20 |
nicklas |
103 |
private static final float[] CC15 = { 230, 559 }; |
6056 |
13 Nov 20 |
nicklas |
104 |
|
6075 |
23 Nov 20 |
nicklas |
// Plot locations -- Aligned to the RIGHT |
6075 |
23 Nov 20 |
nicklas |
106 |
private static final float PLOT_RIGHT = 566; |
6075 |
23 Nov 20 |
nicklas |
107 |
private static final float[] PLOT_ROWS = { 605, 466, 362, 258, 157, 56 }; |
6075 |
23 Nov 20 |
nicklas |
// private static final float[] PLOT_WIDTH = { 194, 206 }; |
6075 |
23 Nov 20 |
nicklas |
// private static final float PLOT_HEIGHT = 80; |
5647 |
08 Oct 19 |
nicklas |
110 |
|
5647 |
08 Oct 19 |
nicklas |
111 |
private final String config; |
5647 |
08 Oct 19 |
nicklas |
112 |
private final String parameterSet; |
6037 |
02 Nov 20 |
nicklas |
113 |
private ScanBReport script; |
6081 |
25 Nov 20 |
nicklas |
114 |
private List<TemplateFilePage> templates; |
6027 |
28 Oct 20 |
nicklas |
115 |
private NumberFormat noDecimal; |
5647 |
08 Oct 19 |
nicklas |
116 |
private NumberFormat oneDecimal; |
5647 |
08 Oct 19 |
nicklas |
117 |
|
5647 |
08 Oct 19 |
nicklas |
118 |
ScanBReportWorker(PdfReportTemplate template, String config, String parameterSet) |
5647 |
08 Oct 19 |
nicklas |
119 |
{ |
5647 |
08 Oct 19 |
nicklas |
120 |
super(template); |
5647 |
08 Oct 19 |
nicklas |
121 |
this.config = config; |
5647 |
08 Oct 19 |
nicklas |
122 |
this.parameterSet = parameterSet; |
5647 |
08 Oct 19 |
nicklas |
123 |
} |
5647 |
08 Oct 19 |
nicklas |
124 |
|
5647 |
08 Oct 19 |
nicklas |
125 |
/** |
5647 |
08 Oct 19 |
nicklas |
Run the gene report script for the given raw bioassay and then |
5647 |
08 Oct 19 |
nicklas |
create the pdf. |
5647 |
08 Oct 19 |
nicklas |
128 |
*/ |
5647 |
08 Oct 19 |
nicklas |
129 |
@Override |
6036 |
02 Nov 20 |
nicklas |
130 |
public ScriptResult runAndCreatePdf(DbControl dc, Rawbioassay raw, ExportOutputStream out, ReportOptions options) |
5647 |
08 Oct 19 |
nicklas |
131 |
throws IOException |
5647 |
08 Oct 19 |
nicklas |
132 |
{ |
6027 |
28 Oct 20 |
nicklas |
133 |
if (script == null) |
5647 |
08 Oct 19 |
nicklas |
134 |
{ |
5647 |
08 Oct 19 |
nicklas |
// Initialize script in the first call to this method |
6075 |
23 Nov 20 |
nicklas |
// NOTE! List of genes MUST be in this order since it is hard-coded into the R script |
6075 |
23 Nov 20 |
nicklas |
// We need the list here also in order to load the expression values from 'gene.tsv' |
6075 |
23 Nov 20 |
nicklas |
138 |
script = new ScanBReport(config, parameterSet, "ESR1", "PGR", "ERBB2", "MKI67"); |
6081 |
25 Nov 20 |
nicklas |
139 |
templates = TemplateFilePage.fromConfig(config+"/templates", script.getScriptDir()); |
6081 |
25 Nov 20 |
nicklas |
140 |
if (templates.size() == 0) |
6081 |
25 Nov 20 |
nicklas |
141 |
{ |
6081 |
25 Nov 20 |
nicklas |
142 |
throw new ConfigurationException("No 'file' elements specified in reggie-config.xml[/" + config + "/templates]"); |
6081 |
25 Nov 20 |
nicklas |
143 |
} |
6027 |
28 Oct 20 |
nicklas |
144 |
noDecimal = createNumberFormat(0); |
5647 |
08 Oct 19 |
nicklas |
145 |
oneDecimal = createNumberFormat(1); |
5647 |
08 Oct 19 |
nicklas |
146 |
} |
6037 |
02 Nov 20 |
nicklas |
147 |
ScanBReport.Result result = script.run(dc, raw); |
5647 |
08 Oct 19 |
nicklas |
148 |
if (result.getExitStatus() == 0) |
5647 |
08 Oct 19 |
nicklas |
149 |
{ |
5647 |
08 Oct 19 |
nicklas |
150 |
toPdf(dc, result, out, options); |
5647 |
08 Oct 19 |
nicklas |
151 |
} |
5647 |
08 Oct 19 |
nicklas |
152 |
return result; |
5647 |
08 Oct 19 |
nicklas |
153 |
} |
5647 |
08 Oct 19 |
nicklas |
154 |
|
6092 |
14 Dec 20 |
nicklas |
155 |
@Override |
6092 |
14 Dec 20 |
nicklas |
156 |
public byte[] redactBeforePersonalInformation(DbControl dc, Rawbioassay raw, PdfUtil7 pdf) |
6092 |
14 Dec 20 |
nicklas |
157 |
throws IOException |
6092 |
14 Dec 20 |
nicklas |
158 |
{ |
6100 |
12 Jan 21 |
nicklas |
159 |
ByteArrayOutputStream out = new ByteArrayOutputStream(1024*1000); |
6092 |
14 Dec 20 |
nicklas |
160 |
pdf.open(out); |
6100 |
12 Jan 21 |
nicklas |
//pdf.setRedactColor(ColorConstants.PINK, 0.5f); // For debugging |
6100 |
12 Jan 21 |
nicklas |
162 |
pdf.redactRect(1, HEADER_COLS[0]-1, HEADER_ROWS[3]-2, HEADER_COLS[1]-2, HEADER_ROWS[1]+20); // First header column |
6100 |
12 Jan 21 |
nicklas |
163 |
pdf.redactRect(1, HEADER_COLS[2]-1, HEADER_ROWS[1]-2, HEADER_COLS[3]-2, HEADER_ROWS[1]+20); // Arrival date |
6100 |
12 Jan 21 |
nicklas |
164 |
pdf.redactRect(1, HEADER_COLS[4]-1, HEADER_ROWS[0]-2, MARGIN_RIGHT-2, HEADER_ROWS[0]+10); // Assay |
6449 |
20 Oct 21 |
nicklas |
165 |
pdf.redactRect(1, 100, MARGIN_BOTTOM-2, PdfUtil7.FULL_WIDTH, MARGIN_BOTTOM+8); |
6100 |
12 Jan 21 |
nicklas |
166 |
pdf.redactAndClose(true, false); // Change to pdf.redactAndClose(true, true) for debugging |
6092 |
14 Dec 20 |
nicklas |
167 |
return out.toByteArray(); |
6092 |
14 Dec 20 |
nicklas |
168 |
} |
5647 |
08 Oct 19 |
nicklas |
169 |
|
6092 |
14 Dec 20 |
nicklas |
170 |
@Override |
6092 |
14 Dec 20 |
nicklas |
171 |
public void addPersonalInformation(DbControl dc, Rawbioassay raw, PdfUtil7 pdf) |
6092 |
14 Dec 20 |
nicklas |
172 |
throws IOException |
6092 |
14 Dec 20 |
nicklas |
173 |
{ |
6449 |
20 Oct 21 |
nicklas |
174 |
Map<Subtype, BioMaterial> parents = raw.findParentBioMaterial(dc, Subtype.RNA, Subtype.SPECIMEN, Subtype.CASE, Subtype.PATIENT); |
6092 |
14 Dec 20 |
nicklas |
175 |
BioSource patient = (BioSource)parents.get(Subtype.PATIENT); |
6092 |
14 Dec 20 |
nicklas |
176 |
Sample theCase = (Sample)parents.get(Subtype.CASE); |
6092 |
14 Dec 20 |
nicklas |
177 |
Sample specimen = (Sample)parents.get(Subtype.SPECIMEN); |
6449 |
20 Oct 21 |
nicklas |
178 |
Extract rna = (Extract)parents.get(Subtype.RNA); |
6449 |
20 Oct 21 |
nicklas |
179 |
|
6449 |
20 Oct 21 |
nicklas |
180 |
Date rnaQcDate = null; |
6449 |
20 Oct 21 |
nicklas |
181 |
if (rna != null) |
6449 |
20 Oct 21 |
nicklas |
182 |
{ |
6449 |
20 Oct 21 |
nicklas |
183 |
List<RnaQc> rnaqc = RnaQc.findByRna(dc, Rna.get(rna)); |
6449 |
20 Oct 21 |
nicklas |
184 |
RnaQc lastRnaQc = RnaQc.findLast(dc, null, rnaqc); |
6449 |
20 Oct 21 |
nicklas |
185 |
if (lastRnaQc != null) |
6449 |
20 Oct 21 |
nicklas |
186 |
{ |
6449 |
20 Oct 21 |
nicklas |
187 |
rnaQcDate = lastRnaQc.getQcRunDate(dc, null); |
6449 |
20 Oct 21 |
nicklas |
188 |
} |
6449 |
20 Oct 21 |
nicklas |
189 |
} |
6449 |
20 Oct 21 |
nicklas |
190 |
|
6449 |
20 Oct 21 |
nicklas |
191 |
AlignedSequences aligned = raw.getAlignedSequences(dc); |
6449 |
20 Oct 21 |
nicklas |
192 |
MaskedSequences masked = aligned.getMaskedSequences(dc); |
6449 |
20 Oct 21 |
nicklas |
193 |
MergedSequences merged = masked.getMergedSequences(dc); |
6449 |
20 Oct 21 |
nicklas |
194 |
List<DemuxedSequences> demux = merged.getDemuxedSequences(dc); |
6449 |
20 Oct 21 |
nicklas |
195 |
List<SequencingRun> sequencingRuns = new ArrayList<SequencingRun>(); |
6449 |
20 Oct 21 |
nicklas |
196 |
for (DemuxedSequences dx : demux) |
6449 |
20 Oct 21 |
nicklas |
197 |
{ |
6449 |
20 Oct 21 |
nicklas |
198 |
sequencingRuns.add(SequencingRun.getByDemuxedSequences(dc, dx)); |
6449 |
20 Oct 21 |
nicklas |
199 |
} |
6449 |
20 Oct 21 |
nicklas |
200 |
|
6449 |
20 Oct 21 |
nicklas |
201 |
|
6100 |
12 Jan 21 |
nicklas |
202 |
pdf.setTitle("SCAN-B report: " + raw.getName()); |
6092 |
14 Dec 20 |
nicklas |
203 |
|
6092 |
14 Dec 20 |
nicklas |
204 |
float headerTitleOffset = 14f; |
6092 |
14 Dec 20 |
nicklas |
205 |
Options col1 = new Options().maxTextWidth(HEADER_COLS[1]-HEADER_COLS[0]-5); |
6092 |
14 Dec 20 |
nicklas |
206 |
|
6092 |
14 Dec 20 |
nicklas |
207 |
pdf.addText("Personal number", 8, HEADER_COLS[0], HEADER_ROWS[1] + headerTitleOffset); |
6092 |
14 Dec 20 |
nicklas |
208 |
pdf.addText((String)Annotationtype.PERSONAL_NUMBER.getAnnotationValue(dc, patient), 12, HEADER_COLS[0], HEADER_ROWS[1], col1); |
6092 |
14 Dec 20 |
nicklas |
209 |
|
6092 |
14 Dec 20 |
nicklas |
210 |
pdf.addText("PAD", 8, HEADER_COLS[0], HEADER_ROWS[2] + headerTitleOffset); |
6092 |
14 Dec 20 |
nicklas |
211 |
pdf.addText((String)Annotationtype.PAD.getAnnotationValue(dc, specimen), 12, HEADER_COLS[0], HEADER_ROWS[2], col1); |
6092 |
14 Dec 20 |
nicklas |
212 |
|
6092 |
14 Dec 20 |
nicklas |
// If a yellow-label icon is requried we need to make sure there is room for it |
6092 |
14 Dec 20 |
nicklas |
// The specimen name will typically fit in the assigned area, but not if we also need the icon |
6092 |
14 Dec 20 |
nicklas |
215 |
pdf.addText("Sample", 8, HEADER_COLS[0], HEADER_ROWS[3] + headerTitleOffset); |
6092 |
14 Dec 20 |
nicklas |
216 |
boolean needYellowLabelIcon = Annotationtype.YELLOW_LABEL.getAnnotationValue(dc, specimen) != null; |
6092 |
14 Dec 20 |
nicklas |
217 |
if (needYellowLabelIcon) col1.maxTextWidth(col1.getMaxTextWidth()-11); |
6092 |
14 Dec 20 |
nicklas |
218 |
float fontSize = pdf.addText(specimen == null ? null : specimen.getName(), 12, HEADER_COLS[0], HEADER_ROWS[3], col1); |
6092 |
14 Dec 20 |
nicklas |
219 |
if (needYellowLabelIcon) |
6092 |
14 Dec 20 |
nicklas |
220 |
{ |
6092 |
14 Dec 20 |
nicklas |
221 |
addYellowLabelIcon(pdf, specimen.getName(), fontSize); |
6092 |
14 Dec 20 |
nicklas |
222 |
} |
6092 |
14 Dec 20 |
nicklas |
223 |
|
6092 |
14 Dec 20 |
nicklas |
224 |
pdf.addText("Sampling date", 8, HEADER_COLS[2], HEADER_ROWS[1] + headerTitleOffset); |
6092 |
14 Dec 20 |
nicklas |
225 |
if (specimen != null) |
6092 |
14 Dec 20 |
nicklas |
226 |
{ |
6092 |
14 Dec 20 |
nicklas |
227 |
pdf.addText(DATE_FORMAT.convert(specimen.getCreationEvent().getEventDate()), 12, HEADER_COLS[2], HEADER_ROWS[1]); |
6092 |
14 Dec 20 |
nicklas |
228 |
} |
6092 |
14 Dec 20 |
nicklas |
229 |
|
6092 |
14 Dec 20 |
nicklas |
230 |
pdf.addText(raw.getName(), 12, HEADER_COLS[4], HEADER_ROWS[0]); |
6449 |
20 Oct 21 |
nicklas |
231 |
|
6449 |
20 Oct 21 |
nicklas |
// Extra info at bottom -- Days from Sampling to: Arrival (x), Sequencing (y), Gene expression (w), RNA-QC (q). Report date: xxxx-xx-xx |
6449 |
20 Oct 21 |
nicklas |
233 |
LocalDateTime samplingDate = specimen == null ? null : toDay(specimen.getCreationEvent().getEventDate()); |
6449 |
20 Oct 21 |
nicklas |
234 |
LocalDateTime arrivalDate = toDay((Date)Annotationtype.ARRIVAL_DATE.getAnnotationValue(dc, specimen)); |
6449 |
20 Oct 21 |
nicklas |
235 |
LocalDateTime seqDate = toDay(getLatestSequencingEndedDate(dc, sequencingRuns)); |
6449 |
20 Oct 21 |
nicklas |
236 |
LocalDateTime expDate = toDay(raw.getItem().getEntryDate()); |
6449 |
20 Oct 21 |
nicklas |
237 |
|
6449 |
20 Oct 21 |
nicklas |
238 |
StringBuilder text = new StringBuilder(); |
6449 |
20 Oct 21 |
nicklas |
239 |
if (samplingDate != null) |
6449 |
20 Oct 21 |
nicklas |
240 |
{ |
6449 |
20 Oct 21 |
nicklas |
241 |
text.append("Days from Sampling to: "); |
6449 |
20 Oct 21 |
nicklas |
242 |
text.append(" Arrival (").append(daysBetween(samplingDate, arrivalDate)).append(") "); |
6449 |
20 Oct 21 |
nicklas |
243 |
text.append("Sequencing (").append(daysBetween(samplingDate, seqDate)).append(") "); |
6449 |
20 Oct 21 |
nicklas |
244 |
text.append("Gene expression (").append(daysBetween(samplingDate, expDate)).append(") "); |
6449 |
20 Oct 21 |
nicklas |
245 |
text.append("RNA-QC (").append(daysBetween(samplingDate, toDay(rnaQcDate))).append(") "); |
6449 |
20 Oct 21 |
nicklas |
246 |
text.append(". "); |
6449 |
20 Oct 21 |
nicklas |
247 |
} |
6449 |
20 Oct 21 |
nicklas |
248 |
text.append("Report date: ").append(DATE_FORMAT.format(pdf.getCreationDate())); |
6449 |
20 Oct 21 |
nicklas |
249 |
pdf.addText(text.toString(), 8, MARGIN_RIGHT, MARGIN_BOTTOM, Options.ALIGN_RIGHT); |
6092 |
14 Dec 20 |
nicklas |
250 |
} |
6092 |
14 Dec 20 |
nicklas |
251 |
|
6037 |
02 Nov 20 |
nicklas |
252 |
private void toPdf(DbControl dc, ScanBReport.Result result, ExportOutputStream out, ReportOptions options) |
5647 |
08 Oct 19 |
nicklas |
253 |
throws IOException |
5647 |
08 Oct 19 |
nicklas |
254 |
{ |
5647 |
08 Oct 19 |
nicklas |
255 |
Rawbioassay raw = result.raw; |
5647 |
08 Oct 19 |
nicklas |
256 |
|
6027 |
28 Oct 20 |
nicklas |
257 |
AlignedSequences aligned = raw.getAlignedSequences(dc); |
6027 |
28 Oct 20 |
nicklas |
258 |
MaskedSequences masked = aligned.getMaskedSequences(dc); |
6027 |
28 Oct 20 |
nicklas |
259 |
MergedSequences merged = masked.getMergedSequences(dc); |
6027 |
28 Oct 20 |
nicklas |
260 |
List<DemuxedSequences> demux = merged.getDemuxedSequences(dc); |
6056 |
13 Nov 20 |
nicklas |
261 |
Set<String> readStrings = getReadStrings(dc, demux); |
6027 |
28 Oct 20 |
nicklas |
262 |
List<SequencingRun> sequencingRuns = new ArrayList<SequencingRun>(); |
6027 |
28 Oct 20 |
nicklas |
263 |
for (DemuxedSequences dx : demux) |
6027 |
28 Oct 20 |
nicklas |
264 |
{ |
6027 |
28 Oct 20 |
nicklas |
265 |
sequencingRuns.add(SequencingRun.getByDemuxedSequences(dc, dx)); |
6027 |
28 Oct 20 |
nicklas |
266 |
} |
6027 |
28 Oct 20 |
nicklas |
267 |
Set<String> sequencerModels = getSequencerModels(dc, sequencingRuns); |
6027 |
28 Oct 20 |
nicklas |
268 |
|
6027 |
28 Oct 20 |
nicklas |
269 |
Map<Subtype, BioMaterial> parents = raw.findParentBioMaterial(dc, Subtype.PATIENT, Subtype.CASE, Subtype.SPECIMEN, Subtype.LYSATE, Subtype.RNA, Subtype.LIBRARY); |
6027 |
28 Oct 20 |
nicklas |
270 |
Extract lib = (Extract)parents.get(Subtype.LIBRARY); |
6216 |
16 Apr 21 |
nicklas |
271 |
Protocol libProtocol = lib == null ? null : lib.getProtocol(); |
5647 |
08 Oct 19 |
nicklas |
272 |
Extract rna = (Extract)parents.get(Subtype.RNA); |
6027 |
28 Oct 20 |
nicklas |
273 |
Extract lysate = (Extract)parents.get(Subtype.LYSATE); |
5647 |
08 Oct 19 |
nicklas |
274 |
Sample specimen = (Sample)parents.get(Subtype.SPECIMEN); |
5647 |
08 Oct 19 |
nicklas |
275 |
Sample theCase = (Sample)parents.get(Subtype.CASE); |
5647 |
08 Oct 19 |
nicklas |
276 |
BioSource patient = (BioSource)parents.get(Subtype.PATIENT); |
5647 |
08 Oct 19 |
nicklas |
277 |
Site site = Site.findByCaseName(raw.getName()); |
5647 |
08 Oct 19 |
nicklas |
278 |
RawBioAssay rba = raw.getRawBioAssay(); |
6027 |
28 Oct 20 |
nicklas |
279 |
|
5647 |
08 Oct 19 |
nicklas |
280 |
String externalName = raw.getName(); |
6068 |
19 Nov 20 |
nicklas |
281 |
if (specimen != null && specimen.getExternalId() != null) |
5647 |
08 Oct 19 |
nicklas |
282 |
{ |
5647 |
08 Oct 19 |
nicklas |
283 |
externalName = externalName.replace(specimen.getName(), specimen.getExternalId()); |
5647 |
08 Oct 19 |
nicklas |
284 |
} |
5647 |
08 Oct 19 |
nicklas |
285 |
|
6027 |
28 Oct 20 |
nicklas |
286 |
Float rqsOrRin = null; |
6449 |
20 Oct 21 |
nicklas |
287 |
Date rnaQcDate = null; |
6027 |
28 Oct 20 |
nicklas |
288 |
if (rna != null) |
6027 |
28 Oct 20 |
nicklas |
289 |
{ |
6027 |
28 Oct 20 |
nicklas |
290 |
List<RnaQc> rnaqc = RnaQc.findByRna(dc, Rna.get(rna)); |
6027 |
28 Oct 20 |
nicklas |
291 |
RnaQc lastRnaQc = RnaQc.findLast(dc, null, rnaqc); |
6449 |
20 Oct 21 |
nicklas |
292 |
if (lastRnaQc != null) |
6449 |
20 Oct 21 |
nicklas |
293 |
{ |
6449 |
20 Oct 21 |
nicklas |
294 |
rqsOrRin = lastRnaQc.getRqsOrRin(dc, null); |
6449 |
20 Oct 21 |
nicklas |
295 |
rnaQcDate = lastRnaQc.getQcRunDate(dc, null); |
6449 |
20 Oct 21 |
nicklas |
296 |
} |
6027 |
28 Oct 20 |
nicklas |
297 |
} |
6027 |
28 Oct 20 |
nicklas |
298 |
|
6056 |
13 Nov 20 |
nicklas |
299 |
NumericFormatter millions = new NumericFormatter(new MultiplyFloatConverter(1/1000000f), noDecimal, "", ""); |
6027 |
28 Oct 20 |
nicklas |
300 |
NumericFormatter percent = new NumericFormatter(null, noDecimal, "%", ""); |
6027 |
28 Oct 20 |
nicklas |
301 |
NumericFormatter count = new NumericFormatter(null, noDecimal); |
6027 |
28 Oct 20 |
nicklas |
302 |
NumericFormatter fractionAsPercent = new NumericFormatter(new MultiplyFloatConverter(100), noDecimal, "%", ""); |
6056 |
13 Nov 20 |
nicklas |
303 |
NumericFormatter toMillig = new NumericFormatter(new MultiplyFloatConverter(1/1000f), oneDecimal, "", ""); |
6068 |
19 Nov 20 |
nicklas |
304 |
NumericFormatter singleDecimal = new NumericFormatter(null, oneDecimal); |
6057 |
16 Nov 20 |
nicklas |
305 |
RoRFormatter rorFormatter = new RoRFormatter(); |
6027 |
28 Oct 20 |
nicklas |
306 |
|
6029 |
28 Oct 20 |
nicklas |
307 |
Map<String, String> translations = new Translater(); |
6076 |
24 Nov 20 |
nicklas |
// Laterality |
6029 |
28 Oct 20 |
nicklas |
309 |
translations.put("LEFT", "Left"); |
6029 |
28 Oct 20 |
nicklas |
310 |
translations.put("RIGHT", "Right"); |
6076 |
24 Nov 20 |
nicklas |
// Pipeline |
6029 |
28 Oct 20 |
nicklas |
312 |
translations.put("RNAseq/Hisat/StringTie", "Hisat/StringTie"); |
6076 |
24 Nov 20 |
nicklas |
// BiopsyType |
6076 |
24 Nov 20 |
nicklas |
314 |
translations.put("SpecimenCoreBiopsy", "Core"); |
6076 |
24 Nov 20 |
nicklas |
315 |
translations.put("SpecimenCoreBiopsy2nd", "CB 2nd"); |
6076 |
24 Nov 20 |
nicklas |
316 |
translations.put("SpecimenFineNeedleAspiration", "FNA"); |
6076 |
24 Nov 20 |
nicklas |
317 |
translations.put("SpecimenSurgery", "OP"); |
6076 |
24 Nov 20 |
nicklas |
// Subtype |
6076 |
24 Nov 20 |
nicklas |
319 |
translations.put("Basal", "Basal-like"); |
6076 |
24 Nov 20 |
nicklas |
320 |
translations.put("Her2", "HER2-enriched"); |
6076 |
24 Nov 20 |
nicklas |
321 |
translations.put("LumA", "Luminal A"); |
6076 |
24 Nov 20 |
nicklas |
322 |
translations.put("LumB", "Luminal B"); |
6076 |
24 Nov 20 |
nicklas |
323 |
translations.put("Normal", "Normal-like"); |
6076 |
24 Nov 20 |
nicklas |
// Grade |
6029 |
28 Oct 20 |
nicklas |
325 |
translations.put("Grade1", "Grade 1"); |
6029 |
28 Oct 20 |
nicklas |
326 |
translations.put("Grade2", "Grade 2"); |
6029 |
28 Oct 20 |
nicklas |
327 |
translations.put("Grade3", "Grade 3"); |
6076 |
24 Nov 20 |
nicklas |
// HER2 status |
6067 |
19 Nov 20 |
nicklas |
329 |
translations.put("HER2nERn", "Negative"); |
6067 |
19 Nov 20 |
nicklas |
330 |
translations.put("HER2pERn", "Positive"); |
6067 |
19 Nov 20 |
nicklas |
331 |
translations.put("HER2nERp", "Negative"); |
6067 |
19 Nov 20 |
nicklas |
332 |
translations.put("HER2pERp", "Positive"); |
6076 |
24 Nov 20 |
nicklas |
// Library protocol |
6409 |
20 Sep 21 |
nicklas |
334 |
translations.put("CMD TruSeq mRNA v1", "TruSeq CMD"); |
6076 |
24 Nov 20 |
nicklas |
335 |
translations.put("CTG TruSeq mRNA", "TruSeq CTG"); |
6093 |
14 Dec 20 |
nicklas |
336 |
translations.put("Biomek TruSeq mRNA", "TruSeq BM"); |
6076 |
24 Nov 20 |
nicklas |
337 |
translations.put("NeoPrep_TrueSeq_mRNA_normalization_v1.4", "TruSeq NP"); |
6076 |
24 Nov 20 |
nicklas |
338 |
translations.put("NeoPrep TruSeq mRNA V1", "TruSeq NP"); |
6076 |
24 Nov 20 |
nicklas |
339 |
translations.put("TruSeq mRNA", "TruSeq"); |
6076 |
24 Nov 20 |
nicklas |
340 |
translations.put("dUTP", "dUTP"); // Not really needed, just for completeness |
6029 |
28 Oct 20 |
nicklas |
341 |
|
6058 |
17 Nov 20 |
nicklas |
342 |
PdfUtil7 pdfUtil = null; |
5647 |
08 Oct 19 |
nicklas |
343 |
try |
5647 |
08 Oct 19 |
nicklas |
344 |
{ |
6058 |
17 Nov 20 |
nicklas |
345 |
pdfUtil = new PdfUtil7("SCAN-B report: " + externalName, result.getScript()); |
5647 |
08 Oct 19 |
nicklas |
346 |
pdfUtil.open(out); |
5647 |
08 Oct 19 |
nicklas |
347 |
|
6081 |
25 Nov 20 |
nicklas |
348 |
pdfUtil.importPdf(templates.get(0), 0, 0, Float.NaN, Float.NaN, Align.LEFT); |
6029 |
28 Oct 20 |
nicklas |
349 |
|
6056 |
13 Nov 20 |
nicklas |
// To make it easer to see what we add here and what is already on the template |
6028 |
28 Oct 20 |
nicklas |
351 |
if (options.debugMode()) |
6028 |
28 Oct 20 |
nicklas |
352 |
{ |
6058 |
17 Nov 20 |
nicklas |
353 |
pdfUtil.setColor(ColorConstants.BLUE); |
6075 |
23 Nov 20 |
nicklas |
354 |
pdfUtil.setClearColor(new DeviceRgb(248, 248, 248), Float.NaN); |
6028 |
28 Oct 20 |
nicklas |
355 |
} |
6056 |
13 Nov 20 |
nicklas |
356 |
/* |
6027 |
28 Oct 20 |
nicklas |
for (int r = 0; r < HEADER_ROWS.length; r++) |
6027 |
28 Oct 20 |
nicklas |
358 |
{ |
6056 |
13 Nov 20 |
nicklas |
pdfUtil.clearRect(HEADER_COLS[r == 0 ? 4 : 0]-(r == 3 ? 70 : 5), HEADER_ROWS[r]-5, PdfUtil.MARGIN_RIGHT+15, HEADER_ROWS[r]+12); |
6027 |
28 Oct 20 |
nicklas |
360 |
} |
6027 |
28 Oct 20 |
nicklas |
pdfUtil.clearRect(SSP_COLS[0]-5, SSP_ROWS[1]-5, 210, 670); |
6027 |
28 Oct 20 |
nicklas |
pdfUtil.clearRect(SSP_COLS[1]-5, SSP_ROWS[6]-5, 210, 560); |
6056 |
13 Nov 20 |
nicklas |
pdfUtil.clearRect(300, 608, 340, 642); |
6058 |
17 Nov 20 |
nicklas |
pdfUtil.clearRect(PLOT_X1, PLOT_START_Y-4*PLOT_DELTA_Y, PLOT_X1+PLOT_WIDTH, PLOT_START_Y+PLOT_HEIGHT); |
6030 |
28 Oct 20 |
nicklas |
pdfUtil.clearRect(MARGIN_LEFT-5, MARGIN_BOTTOM-5, MARGIN_RIGHT+5, MARGIN_BOTTOM+9); |
6056 |
13 Nov 20 |
nicklas |
366 |
*/ |
6027 |
28 Oct 20 |
nicklas |
367 |
|
6027 |
28 Oct 20 |
nicklas |
// Header section |
6027 |
28 Oct 20 |
nicklas |
369 |
String[] header = new String[6]; |
6027 |
28 Oct 20 |
nicklas |
// Row 1 |
6056 |
13 Nov 20 |
nicklas |
371 |
header[4] = externalName; |
6027 |
28 Oct 20 |
nicklas |
372 |
addHeaderRow(pdfUtil, header, HEADER_ROWS[0]); |
5647 |
08 Oct 19 |
nicklas |
// Row 2 |
6056 |
13 Nov 20 |
nicklas |
374 |
header[0] = patient == null ? null : patient.getExternalId(); |
6056 |
13 Nov 20 |
nicklas |
375 |
header[1] = site == Site.UNKNOWN ? null : site.getName(); |
6056 |
13 Nov 20 |
nicklas |
376 |
header[2] = DATE_FORMAT.convert((Date)Annotationtype.ARRIVAL_DATE.getAnnotationValue(dc, specimen)); |
6056 |
13 Nov 20 |
nicklas |
377 |
header[3] = lysate != null && specimen != null ? toMillig.format(lysate.getCreationEvent().getUsedQuantity(specimen)) : null; |
6056 |
13 Nov 20 |
nicklas |
378 |
header[4] = Values.getString(sequencerModels, ", ", true); |
6082 |
26 Nov 20 |
nicklas |
379 |
header[5] = millions.format(dc, Annotationtype.PF_READS, merged)+"/"+millions.format(dc, Annotationtype.PM_READS, masked)+"/"+millions.format(dc, Annotationtype.ALIGNED_PAIRS, aligned); |
6027 |
28 Oct 20 |
nicklas |
380 |
addHeaderRow(pdfUtil, header, HEADER_ROWS[1]); |
6027 |
28 Oct 20 |
nicklas |
// Row 3 |
6056 |
13 Nov 20 |
nicklas |
382 |
header[0] = theCase == null ? null : theCase.getExternalId(); |
6056 |
13 Nov 20 |
nicklas |
383 |
header[1] = (String)Annotationtype.CONSENT.getAnnotationValue(dc, theCase); |
6056 |
13 Nov 20 |
nicklas |
384 |
header[2] = DATE_FORMAT.convert((Date)Annotationtype.QIACUBE_DATE.getAnnotationValue(dc, rna)); |
6068 |
19 Nov 20 |
nicklas |
385 |
header[3] = singleDecimal.format(rqsOrRin); |
6078 |
24 Nov 20 |
nicklas |
386 |
header[4] = Values.getString(readStrings, ", ", true); |
6056 |
13 Nov 20 |
nicklas |
387 |
header[5] = fractionAsPercent.format(dc, Annotationtype.FRACTION_DUPLICATION, aligned)+", "+count.format(dc, Annotationtype.QC_GENOTYPE_COUNT, aligned) + " (" + percent.format(dc, Annotationtype.QC_GENOTYPE_HET_PCT, aligned) + ")"; |
6056 |
13 Nov 20 |
nicklas |
388 |
addHeaderRow(pdfUtil, header, HEADER_ROWS[2]); |
6056 |
13 Nov 20 |
nicklas |
// Row 4 |
6056 |
13 Nov 20 |
nicklas |
390 |
header[0] = specimen == null ? null : specimen.getExternalId(); |
6056 |
13 Nov 20 |
nicklas |
391 |
header[1] = translations.get(Annotationtype.LATERALITY.getAnnotationValue(dc, specimen)); |
6076 |
24 Nov 20 |
nicklas |
392 |
header[2] = lib == null ? null : DATE_FORMAT.convert(lib.getCreationEvent().getEventDate()); |
6216 |
16 Apr 21 |
nicklas |
393 |
header[3] = libProtocol == null ? null : translations.get(libProtocol.getName()); |
6056 |
13 Nov 20 |
nicklas |
394 |
header[4] = count.format(dc, Annotationtype.FRAGMENT_SIZE_AVG, aligned) + "/" + count.format(dc, Annotationtype.FRAGMENT_SIZE_STDEV, aligned); |
6029 |
28 Oct 20 |
nicklas |
395 |
header[5] = translations.get(Annotationtype.PIPELINE.getAnnotationValue(dc, rba)); |
6056 |
13 Nov 20 |
nicklas |
396 |
addHeaderRow(pdfUtil, header, HEADER_ROWS[3]); |
5647 |
08 Oct 19 |
nicklas |
397 |
|
6056 |
13 Nov 20 |
nicklas |
// Extra info about biopsy type to the left of "Sample" |
6076 |
24 Nov 20 |
nicklas |
399 |
String biopsyType = translations.get(Annotationtype.BIOPSY_TYPE.getAnnotationValue(dc, specimen)); |
6069 |
19 Nov 20 |
nicklas |
400 |
pdfUtil.addText(biopsyType, 12, HEADER_COLS[0]-3, HEADER_ROWS[3], new Options(Align.RIGHT).underline()); |
6092 |
14 Dec 20 |
nicklas |
401 |
|
6056 |
13 Nov 20 |
nicklas |
// YellowLabel icon |
6056 |
13 Nov 20 |
nicklas |
403 |
if (Annotationtype.YELLOW_LABEL.getAnnotationValue(dc, specimen) != null) |
6056 |
13 Nov 20 |
nicklas |
404 |
{ |
6092 |
14 Dec 20 |
nicklas |
405 |
addYellowLabelIcon(pdfUtil, specimen.getExternalId(), 12); |
6056 |
13 Nov 20 |
nicklas |
406 |
} |
6056 |
13 Nov 20 |
nicklas |
407 |
|
6056 |
13 Nov 20 |
nicklas |
// SSP classifications |
6027 |
28 Oct 20 |
nicklas |
409 |
Annotationtype subtype4 = Annotationtype.sspResultAnnotation(SspModel.getModelByName("Subtype")); |
6027 |
28 Oct 20 |
nicklas |
410 |
Annotationtype subtype5 = Annotationtype.sspResultAnnotation(SspModel.getModelByName("PAM50 subtype")); |
6027 |
28 Oct 20 |
nicklas |
411 |
Annotationtype erStatus = Annotationtype.sspResultAnnotation(SspModel.getModelByName("ER")); |
6027 |
28 Oct 20 |
nicklas |
412 |
Annotationtype nhg = Annotationtype.sspResultAnnotation(SspModel.getModelByName("NHG")); |
6027 |
28 Oct 20 |
nicklas |
413 |
Annotationtype ki67Status = Annotationtype.sspResultAnnotation(SspModel.getModelByName("Ki67")); |
6027 |
28 Oct 20 |
nicklas |
414 |
Annotationtype pgrStatus = Annotationtype.sspResultAnnotation(SspModel.getModelByName("PR")); |
6027 |
28 Oct 20 |
nicklas |
415 |
Annotationtype her2Status = Annotationtype.sspResultAnnotation(SspModel.getModelByName("ERBB2")); |
6056 |
13 Nov 20 |
nicklas |
416 |
Annotationtype ror = Annotationtype.sspResultAnnotation(SspModel.getModelByName("ROR asT0")); |
6056 |
13 Nov 20 |
nicklas |
417 |
Annotationtype cc15 = Annotationtype.sspResultAnnotation(SspModel.getModelByName("CC15")); |
6027 |
28 Oct 20 |
nicklas |
418 |
|
6067 |
19 Nov 20 |
nicklas |
// Change the HER2 status model to use depending on the ER status |
6067 |
19 Nov 20 |
nicklas |
420 |
String er = (String)erStatus.getAnnotationValue(dc, rba); |
6067 |
19 Nov 20 |
nicklas |
421 |
if ("Positive".equals(er)) |
6067 |
19 Nov 20 |
nicklas |
422 |
{ |
6067 |
19 Nov 20 |
nicklas |
423 |
her2Status = Annotationtype.sspResultAnnotation(SspModel.getModelByName("ERBB2_ERp")); |
6067 |
19 Nov 20 |
nicklas |
424 |
} |
6067 |
19 Nov 20 |
nicklas |
425 |
else if ("Negative".equals(er)) |
6067 |
19 Nov 20 |
nicklas |
426 |
{ |
6067 |
19 Nov 20 |
nicklas |
427 |
her2Status = Annotationtype.sspResultAnnotation(SspModel.getModelByName("ERBB2_ERn")); |
6067 |
19 Nov 20 |
nicklas |
428 |
} |
6067 |
19 Nov 20 |
nicklas |
429 |
|
6069 |
19 Nov 20 |
nicklas |
430 |
pdfUtil.addText(translations.get(subtype4.getAnnotationValue(dc, rba)), 14, SSP_COLS[0], SSP_ROWS[0]); |
6069 |
19 Nov 20 |
nicklas |
431 |
pdfUtil.addText(translations.get(subtype5.getAnnotationValue(dc, rba)), 14, SSP_COLS[0], SSP_ROWS[1]); |
6069 |
19 Nov 20 |
nicklas |
432 |
pdfUtil.addText(translations.get(er), 14, SSP_COLS[1], SSP_ROWS[2]); |
6069 |
19 Nov 20 |
nicklas |
433 |
pdfUtil.addText(translations.get(pgrStatus.getAnnotationValue(dc, rba)), 14, SSP_COLS[1], SSP_ROWS[3]); |
6069 |
19 Nov 20 |
nicklas |
434 |
pdfUtil.addText(translations.get(her2Status.getAnnotationValue(dc, rba)), 14, SSP_COLS[1], SSP_ROWS[4]); |
6069 |
19 Nov 20 |
nicklas |
435 |
pdfUtil.addText(translations.get(ki67Status.getAnnotationValue(dc, rba)), 14, SSP_COLS[1], SSP_ROWS[5]); |
6069 |
19 Nov 20 |
nicklas |
436 |
pdfUtil.addText(translations.get(nhg.getAnnotationValue(dc, rba)), 14, SSP_COLS[1], SSP_ROWS[6]); |
6027 |
28 Oct 20 |
nicklas |
437 |
|
6056 |
13 Nov 20 |
nicklas |
// ROR is outputted twice but with different ranges for <=20mm and >20mm |
6056 |
13 Nov 20 |
nicklas |
439 |
String rorAsTo = (String)ror.getAnnotationValue(dc, rba); |
6057 |
16 Nov 20 |
nicklas |
440 |
if (rorAsTo != null) |
6057 |
16 Nov 20 |
nicklas |
441 |
{ |
6075 |
23 Nov 20 |
nicklas |
442 |
pdfUtil.addText(rorFormatter.format(rorAsTo+"-4"), 14, ROR_COLS[0], ROR_ROWS[0], new Options().color(ROR_DARK_GREEN)); |
6075 |
23 Nov 20 |
nicklas |
443 |
pdfUtil.addText(rorFormatter.format(rorAsTo+"+5"), 14, ROR_COLS[0], ROR_ROWS[1], new Options().color(ROR_ORANGE)); |
6057 |
16 Nov 20 |
nicklas |
444 |
|
6057 |
16 Nov 20 |
nicklas |
445 |
int r = rorFormatter.getRor(); |
6057 |
16 Nov 20 |
nicklas |
446 |
pdfUtil.drawCircle(ROR_COLS[1], ROR_ROWS[2], ROR_RADIUS, r <= 40 ? ROR_GREEN : r <= 60 ? ROR_YELLOW : ROR_RED); // N0 |
6057 |
16 Nov 20 |
nicklas |
447 |
pdfUtil.drawCircle(ROR_COLS[2], ROR_ROWS[2], ROR_RADIUS, r <= 15 ? ROR_GREEN : r <= 40 ? ROR_YELLOW : ROR_RED); // N1-3 |
6057 |
16 Nov 20 |
nicklas |
448 |
pdfUtil.drawCircle(ROR_COLS[3], ROR_ROWS[2], ROR_RADIUS, ROR_RED); // N4 |
6057 |
16 Nov 20 |
nicklas |
449 |
r += 5; // T1 |
6057 |
16 Nov 20 |
nicklas |
450 |
pdfUtil.drawCircle(ROR_COLS[1], ROR_ROWS[3], ROR_RADIUS, r <= 40 ? ROR_GREEN : r <= 60 ? ROR_YELLOW : ROR_RED); // N0 |
6057 |
16 Nov 20 |
nicklas |
451 |
pdfUtil.drawCircle(ROR_COLS[2], ROR_ROWS[3], ROR_RADIUS, r <= 15 ? ROR_GREEN : r <= 40 ? ROR_YELLOW : ROR_RED); // N1-3 |
6057 |
16 Nov 20 |
nicklas |
452 |
pdfUtil.drawCircle(ROR_COLS[3], ROR_ROWS[3], ROR_RADIUS, ROR_RED); // N4 |
6057 |
16 Nov 20 |
nicklas |
453 |
} |
6056 |
13 Nov 20 |
nicklas |
454 |
|
6056 |
13 Nov 20 |
nicklas |
// CC15 is outputted as an icon |
6056 |
13 Nov 20 |
nicklas |
456 |
String cc15Class = (String)cc15.getAnnotationValue(dc, rba); |
6056 |
13 Nov 20 |
nicklas |
457 |
if (cc15Class != null) |
6056 |
13 Nov 20 |
nicklas |
458 |
{ |
6065 |
18 Nov 20 |
nicklas |
459 |
String cc15Img = "Norm15".equals(cc15Class) ? "warning.svg" : "ok.svg"; |
6065 |
18 Nov 20 |
nicklas |
460 |
pdfUtil.importSvg(Reggie.class.getResourceAsStream("/net/sf/basedb/reggie/pdf/"+cc15Img), CC15[0], CC15[1], 14, 12, Align.RIGHT); |
6056 |
13 Nov 20 |
nicklas |
461 |
} |
6056 |
13 Nov 20 |
nicklas |
462 |
|
6027 |
28 Oct 20 |
nicklas |
463 |
java.io.File workDir = result.getWorkDir(); |
6075 |
23 Nov 20 |
nicklas |
464 |
String[] plots = { "sMS_ROR.pdf", "ER_ESR1.pdf", "PgR_PGR.pdf", "HER2_ERBB2.pdf", "Ki67_MKI67.pdf", "NHG_MKI67.pdf" }; |
6075 |
23 Nov 20 |
nicklas |
465 |
for (int plotNo=0; plotNo < plots.length; plotNo++) |
5647 |
08 Oct 19 |
nicklas |
466 |
{ |
6076 |
24 Nov 20 |
nicklas |
467 |
if (plotNo == 0 && rorAsTo == null) continue; // Skip ROR plot |
6075 |
23 Nov 20 |
nicklas |
468 |
java.io.File f2 = new java.io.File(workDir, plots[plotNo]); |
6027 |
28 Oct 20 |
nicklas |
469 |
if (f2.exists()) |
5647 |
08 Oct 19 |
nicklas |
470 |
{ |
6075 |
23 Nov 20 |
nicklas |
471 |
pdfUtil.importPdf(new FileInputStream(f2), 1, PLOT_RIGHT, PLOT_ROWS[plotNo], Float.NaN, Float.NaN, Align.RIGHT); |
5647 |
08 Oct 19 |
nicklas |
472 |
} |
5647 |
08 Oct 19 |
nicklas |
473 |
} |
6024 |
26 Oct 20 |
nicklas |
474 |
|
6449 |
20 Oct 21 |
nicklas |
// Extra info at bottom -- Days from Arrival to: Sequencing (y), Gene expression (z), RNA-QC (w). Report date: xxxx-xx-xx |
6449 |
20 Oct 21 |
nicklas |
476 |
LocalDateTime arrivalDate = toDay((Date)Annotationtype.ARRIVAL_DATE.getAnnotationValue(dc, specimen)); |
6449 |
20 Oct 21 |
nicklas |
477 |
LocalDateTime seqDate = toDay(getLatestSequencingEndedDate(dc, sequencingRuns)); |
6449 |
20 Oct 21 |
nicklas |
478 |
LocalDateTime expDate = toDay(rba.getEntryDate()); |
6449 |
20 Oct 21 |
nicklas |
479 |
StringBuilder text = new StringBuilder(); |
6449 |
20 Oct 21 |
nicklas |
480 |
if (arrivalDate != null) |
6449 |
20 Oct 21 |
nicklas |
481 |
{ |
6449 |
20 Oct 21 |
nicklas |
482 |
text.append("Days from Arrival to: "); |
6449 |
20 Oct 21 |
nicklas |
483 |
text.append("Sequencing (").append(daysBetween(arrivalDate, seqDate)).append(") "); |
6449 |
20 Oct 21 |
nicklas |
484 |
text.append("Gene expression (").append(daysBetween(arrivalDate, expDate)).append(") "); |
6449 |
20 Oct 21 |
nicklas |
485 |
text.append("RNA-QC (").append(daysBetween(arrivalDate, toDay(rnaQcDate))).append(") "); |
6449 |
20 Oct 21 |
nicklas |
486 |
text.append(". "); |
6449 |
20 Oct 21 |
nicklas |
487 |
} |
6449 |
20 Oct 21 |
nicklas |
488 |
text.append("Report date: ").append(DATE_FORMAT.format(new Date())); |
6449 |
20 Oct 21 |
nicklas |
489 |
pdfUtil.addText(text.toString(), 8, MARGIN_RIGHT, MARGIN_BOTTOM, Options.ALIGN_RIGHT); |
6028 |
28 Oct 20 |
nicklas |
490 |
|
6028 |
28 Oct 20 |
nicklas |
491 |
if (options.debugMode()) pdfUtil.drawGrid(); |
6030 |
28 Oct 20 |
nicklas |
492 |
|
6081 |
25 Nov 20 |
nicklas |
// Page 2, etc. |
6081 |
25 Nov 20 |
nicklas |
494 |
for (int pageNo = 1; pageNo < templates.size(); pageNo++) |
6081 |
25 Nov 20 |
nicklas |
495 |
{ |
6081 |
25 Nov 20 |
nicklas |
496 |
pdfUtil.newPage(); |
6081 |
25 Nov 20 |
nicklas |
497 |
pdfUtil.importPdf(templates.get(pageNo), 0, 0, Float.NaN, Float.NaN, Align.LEFT); |
6081 |
25 Nov 20 |
nicklas |
498 |
} |
5647 |
08 Oct 19 |
nicklas |
499 |
} |
6027 |
28 Oct 20 |
nicklas |
500 |
catch (Exception ex) |
6027 |
28 Oct 20 |
nicklas |
501 |
{ |
6027 |
28 Oct 20 |
nicklas |
502 |
ex.printStackTrace(System.out); |
6027 |
28 Oct 20 |
nicklas |
503 |
throw ex; |
6027 |
28 Oct 20 |
nicklas |
504 |
} |
5647 |
08 Oct 19 |
nicklas |
505 |
finally |
5647 |
08 Oct 19 |
nicklas |
506 |
{ |
5647 |
08 Oct 19 |
nicklas |
507 |
if (pdfUtil != null) pdfUtil.close(); |
5647 |
08 Oct 19 |
nicklas |
508 |
} |
5647 |
08 Oct 19 |
nicklas |
509 |
} |
5647 |
08 Oct 19 |
nicklas |
510 |
|
6058 |
17 Nov 20 |
nicklas |
511 |
private void addHeaderRow(PdfUtil7 pdfUtil, String[] text, float y) |
6027 |
28 Oct 20 |
nicklas |
512 |
{ |
6078 |
24 Nov 20 |
nicklas |
513 |
float maxX = MARGIN_RIGHT; |
6078 |
24 Nov 20 |
nicklas |
514 |
for (int i = text.length-1; i >= 0; i--) |
6027 |
28 Oct 20 |
nicklas |
515 |
{ |
6078 |
24 Nov 20 |
nicklas |
516 |
if (text[i] != null) |
6078 |
24 Nov 20 |
nicklas |
517 |
{ |
6078 |
24 Nov 20 |
nicklas |
518 |
float maxTextWidth = maxX - HEADER_COLS[i]; |
6078 |
24 Nov 20 |
nicklas |
519 |
pdfUtil.addText(text[i], 12, HEADER_COLS[i], y, new Options().maxTextWidth(maxTextWidth)); |
6078 |
24 Nov 20 |
nicklas |
520 |
maxX = HEADER_COLS[i] - 5; |
6078 |
24 Nov 20 |
nicklas |
521 |
} |
6027 |
28 Oct 20 |
nicklas |
522 |
} |
6027 |
28 Oct 20 |
nicklas |
523 |
} |
6027 |
28 Oct 20 |
nicklas |
524 |
|
6092 |
14 Dec 20 |
nicklas |
525 |
/** |
6092 |
14 Dec 20 |
nicklas |
Adds the "YellowLabel" icon. The "X" location is automatically calculated |
6092 |
14 Dec 20 |
nicklas |
so that it fits after the "afterText" that is assumed to be written at |
6092 |
14 Dec 20 |
nicklas |
HEADER_COL[0] and HEADER_ROW[3]. |
6092 |
14 Dec 20 |
nicklas |
529 |
*/ |
6092 |
14 Dec 20 |
nicklas |
530 |
private void addYellowLabelIcon(PdfUtil7 pdfUtil, String afterText, float fontSize) |
6092 |
14 Dec 20 |
nicklas |
531 |
throws IOException |
6092 |
14 Dec 20 |
nicklas |
532 |
{ |
6092 |
14 Dec 20 |
nicklas |
533 |
float imgX = HEADER_COLS[0]+1; |
6092 |
14 Dec 20 |
nicklas |
534 |
float imgY = HEADER_ROWS[3]-1; |
6092 |
14 Dec 20 |
nicklas |
535 |
if (afterText != null) |
6092 |
14 Dec 20 |
nicklas |
536 |
{ |
6092 |
14 Dec 20 |
nicklas |
537 |
imgX += pdfUtil.getDefaultFont().getWidth(afterText, fontSize); |
6092 |
14 Dec 20 |
nicklas |
538 |
} |
6092 |
14 Dec 20 |
nicklas |
539 |
pdfUtil.importSvg(Reggie.class.getResourceAsStream("/net/sf/basedb/reggie/pdf/yellow-label.svg"), imgX, imgY, 11, 11, Align.LEFT); |
6092 |
14 Dec 20 |
nicklas |
540 |
} |
6092 |
14 Dec 20 |
nicklas |
541 |
|
5647 |
08 Oct 19 |
nicklas |
542 |
private NumberFormat createNumberFormat(int numDecimals) |
5647 |
08 Oct 19 |
nicklas |
543 |
{ |
5647 |
08 Oct 19 |
nicklas |
544 |
DecimalFormatSymbols sym = new DecimalFormatSymbols(); |
6027 |
28 Oct 20 |
nicklas |
545 |
sym.setDecimalSeparator('.'); |
5647 |
08 Oct 19 |
nicklas |
546 |
String format = "0"; |
5647 |
08 Oct 19 |
nicklas |
547 |
if (numDecimals > 0) format += "." + MD5.leftPad("", '0', numDecimals); |
5647 |
08 Oct 19 |
nicklas |
548 |
DecimalFormat df = new DecimalFormat(format, sym); |
5647 |
08 Oct 19 |
nicklas |
549 |
return df; |
5647 |
08 Oct 19 |
nicklas |
550 |
} |
5647 |
08 Oct 19 |
nicklas |
551 |
|
6027 |
28 Oct 20 |
nicklas |
552 |
private Set<String> getSequencerModels(DbControl dc, List<SequencingRun> sequencingRuns) |
6027 |
28 Oct 20 |
nicklas |
553 |
{ |
6027 |
28 Oct 20 |
nicklas |
554 |
Set<String> models = new TreeSet<>(); |
6027 |
28 Oct 20 |
nicklas |
555 |
for (SequencingRun sr : sequencingRuns) |
6027 |
28 Oct 20 |
nicklas |
556 |
{ |
6027 |
28 Oct 20 |
nicklas |
557 |
String m = (String)Annotationtype.HARDWARE_MODEL.getAnnotationValue(dc, sr.getItem().getHardware()); |
6027 |
28 Oct 20 |
nicklas |
//String m = sr.getItem().getHardware().getName(); |
6027 |
28 Oct 20 |
nicklas |
559 |
if (m != null) models.add(m); |
6027 |
28 Oct 20 |
nicklas |
560 |
} |
6027 |
28 Oct 20 |
nicklas |
561 |
return models; |
6027 |
28 Oct 20 |
nicklas |
562 |
} |
6449 |
20 Oct 21 |
nicklas |
563 |
|
6449 |
20 Oct 21 |
nicklas |
564 |
private Date getLatestSequencingEndedDate(DbControl dc, List<SequencingRun> sequencingRuns) |
6449 |
20 Oct 21 |
nicklas |
565 |
{ |
6449 |
20 Oct 21 |
nicklas |
566 |
Date lastDate = null; |
6449 |
20 Oct 21 |
nicklas |
567 |
for (SequencingRun sr : sequencingRuns) |
6449 |
20 Oct 21 |
nicklas |
568 |
{ |
6449 |
20 Oct 21 |
nicklas |
569 |
Date d = (Date)Annotationtype.SEQUENCING_END.getAnnotationValue(dc, sr.getItem()); |
6449 |
20 Oct 21 |
nicklas |
570 |
if (lastDate == null || (d != null && d.after(lastDate))) |
6449 |
20 Oct 21 |
nicklas |
571 |
{ |
6449 |
20 Oct 21 |
nicklas |
572 |
lastDate = d; |
6449 |
20 Oct 21 |
nicklas |
573 |
} |
6449 |
20 Oct 21 |
nicklas |
574 |
} |
6449 |
20 Oct 21 |
nicklas |
575 |
return lastDate; |
6449 |
20 Oct 21 |
nicklas |
576 |
} |
6027 |
28 Oct 20 |
nicklas |
577 |
|
6056 |
13 Nov 20 |
nicklas |
578 |
private Set<String> getReadStrings(DbControl dc, List<DemuxedSequences> demux) |
5694 |
31 Oct 19 |
nicklas |
579 |
{ |
6056 |
13 Nov 20 |
nicklas |
580 |
Set<String> readStrings = new TreeSet<>(); |
6078 |
24 Nov 20 |
nicklas |
581 |
Formatter<String> readStringFormatter = new ReadStringFormatter(); |
6056 |
13 Nov 20 |
nicklas |
582 |
for (DemuxedSequences dx : demux) |
5694 |
31 Oct 19 |
nicklas |
583 |
{ |
6056 |
13 Nov 20 |
nicklas |
584 |
String rs = (String)Annotationtype.READ_STRING.getAnnotationValue(dc, dx.getItem()); |
6078 |
24 Nov 20 |
nicklas |
585 |
if (rs != null) readStrings.add(readStringFormatter.format(rs)); |
5694 |
31 Oct 19 |
nicklas |
586 |
} |
6056 |
13 Nov 20 |
nicklas |
587 |
return readStrings; |
6056 |
13 Nov 20 |
nicklas |
588 |
} |
6056 |
13 Nov 20 |
nicklas |
589 |
|
6449 |
20 Oct 21 |
nicklas |
590 |
private LocalDateTime toDay(Date date) |
6449 |
20 Oct 21 |
nicklas |
591 |
{ |
6449 |
20 Oct 21 |
nicklas |
592 |
if (date == null) return null; |
6449 |
20 Oct 21 |
nicklas |
// IMPORTANT! We must use Instant.ofEpochMilli() instead of Date.toInstant() |
6449 |
20 Oct 21 |
nicklas |
// since java.sql.Date.toInstant() throw UnsupporterOperationException |
6449 |
20 Oct 21 |
nicklas |
595 |
LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault()); |
6449 |
20 Oct 21 |
nicklas |
596 |
return dateTime.truncatedTo(ChronoUnit.DAYS); |
6449 |
20 Oct 21 |
nicklas |
597 |
} |
6449 |
20 Oct 21 |
nicklas |
598 |
|
6449 |
20 Oct 21 |
nicklas |
599 |
private String daysBetween(LocalDateTime d1, LocalDateTime d2) |
6449 |
20 Oct 21 |
nicklas |
600 |
{ |
6449 |
20 Oct 21 |
nicklas |
601 |
return d1 == null || d2 == null ? "-" : Long.toString(ChronoUnit.DAYS.between(d1, d2)); |
6449 |
20 Oct 21 |
nicklas |
602 |
} |
6056 |
13 Nov 20 |
nicklas |
603 |
|
6056 |
13 Nov 20 |
nicklas |
604 |
/** |
6056 |
13 Nov 20 |
nicklas |
Formatter implementation for ReadString value that keep |
6056 |
13 Nov 20 |
nicklas |
the number of T's only. Example: 54T1S6B1S52T1S --> 54+52 |
6056 |
13 Nov 20 |
nicklas |
607 |
*/ |
6056 |
13 Nov 20 |
nicklas |
608 |
public static class ReadStringFormatter |
6056 |
13 Nov 20 |
nicklas |
609 |
implements Formatter<String> |
6056 |
13 Nov 20 |
nicklas |
610 |
{ |
6056 |
13 Nov 20 |
nicklas |
611 |
private final Pattern p; |
6056 |
13 Nov 20 |
nicklas |
612 |
public ReadStringFormatter() |
5694 |
31 Oct 19 |
nicklas |
613 |
{ |
6056 |
13 Nov 20 |
nicklas |
614 |
this.p = Pattern.compile("(\\d+)T"); |
5694 |
31 Oct 19 |
nicklas |
615 |
} |
6056 |
13 Nov 20 |
nicklas |
616 |
|
6056 |
13 Nov 20 |
nicklas |
617 |
@Override |
6056 |
13 Nov 20 |
nicklas |
618 |
public String format(String readString) |
6056 |
13 Nov 20 |
nicklas |
619 |
{ |
6056 |
13 Nov 20 |
nicklas |
620 |
Matcher m = p.matcher(readString); |
6056 |
13 Nov 20 |
nicklas |
621 |
StringBuilder sb = new StringBuilder(); |
6056 |
13 Nov 20 |
nicklas |
622 |
while (m.find()) |
6056 |
13 Nov 20 |
nicklas |
623 |
{ |
6056 |
13 Nov 20 |
nicklas |
624 |
if (sb.length() > 0) sb.append("+"); |
6056 |
13 Nov 20 |
nicklas |
625 |
sb.append(m.group(1)); |
6056 |
13 Nov 20 |
nicklas |
626 |
} |
6056 |
13 Nov 20 |
nicklas |
627 |
return sb.toString(); |
6056 |
13 Nov 20 |
nicklas |
628 |
} |
6056 |
13 Nov 20 |
nicklas |
629 |
|
6056 |
13 Nov 20 |
nicklas |
630 |
@Override |
6056 |
13 Nov 20 |
nicklas |
631 |
public String parseString(String s) |
6056 |
13 Nov 20 |
nicklas |
632 |
{ |
6056 |
13 Nov 20 |
nicklas |
633 |
throw new UnsupportedOperationException("parseString"); |
6056 |
13 Nov 20 |
nicklas |
634 |
} |
5694 |
31 Oct 19 |
nicklas |
635 |
} |
5647 |
08 Oct 19 |
nicklas |
636 |
|
5694 |
31 Oct 19 |
nicklas |
637 |
/** |
6056 |
13 Nov 20 |
nicklas |
Formatter for converting RoR values to intervals. RoR classes are |
6056 |
13 Nov 20 |
nicklas |
values with pattern cNNN where NNN is a numeric value typically an |
6056 |
13 Nov 20 |
nicklas |
even multiple of 5. The value should be combined with a range (positive or |
6056 |
13 Nov 20 |
nicklas |
negative). |
6056 |
13 Nov 20 |
nicklas |
Examples: c020-4 --> 16-20, c020+5 --> 21-25 |
5694 |
31 Oct 19 |
nicklas |
643 |
*/ |
6056 |
13 Nov 20 |
nicklas |
644 |
public static class RoRFormatter |
6056 |
13 Nov 20 |
nicklas |
645 |
implements Formatter<String> |
5694 |
31 Oct 19 |
nicklas |
646 |
{ |
6056 |
13 Nov 20 |
nicklas |
647 |
private final Pattern p; |
6057 |
16 Nov 20 |
nicklas |
648 |
private int r; |
6056 |
13 Nov 20 |
nicklas |
649 |
public RoRFormatter() |
5694 |
31 Oct 19 |
nicklas |
650 |
{ |
6056 |
13 Nov 20 |
nicklas |
651 |
this.p = Pattern.compile("c(\\d+)([+-]\\d)?"); |
5694 |
31 Oct 19 |
nicklas |
652 |
} |
6056 |
13 Nov 20 |
nicklas |
653 |
|
5694 |
31 Oct 19 |
nicklas |
654 |
@Override |
6056 |
13 Nov 20 |
nicklas |
655 |
public String format(String ror) |
5694 |
31 Oct 19 |
nicklas |
656 |
{ |
6056 |
13 Nov 20 |
nicklas |
657 |
Matcher m = p.matcher(ror); |
6056 |
13 Nov 20 |
nicklas |
658 |
if (m.matches()) |
6056 |
13 Nov 20 |
nicklas |
659 |
{ |
6057 |
16 Nov 20 |
nicklas |
660 |
r = Values.getInt(m.group(1)); |
6056 |
13 Nov 20 |
nicklas |
661 |
int range = Values.getInt(m.group(2), -4); |
6056 |
13 Nov 20 |
nicklas |
662 |
if (range < 0) |
6056 |
13 Nov 20 |
nicklas |
663 |
{ |
6056 |
13 Nov 20 |
nicklas |
664 |
ror = (r+range)+"-"+r; |
6056 |
13 Nov 20 |
nicklas |
665 |
} |
6056 |
13 Nov 20 |
nicklas |
666 |
else |
6056 |
13 Nov 20 |
nicklas |
667 |
{ |
6056 |
13 Nov 20 |
nicklas |
668 |
ror = (r+1)+"-"+(r+range); |
6056 |
13 Nov 20 |
nicklas |
669 |
} |
6056 |
13 Nov 20 |
nicklas |
670 |
} |
6056 |
13 Nov 20 |
nicklas |
671 |
return ror; |
5694 |
31 Oct 19 |
nicklas |
672 |
} |
5694 |
31 Oct 19 |
nicklas |
673 |
|
6056 |
13 Nov 20 |
nicklas |
674 |
@Override |
6056 |
13 Nov 20 |
nicklas |
675 |
public String parseString(String s) |
5694 |
31 Oct 19 |
nicklas |
676 |
{ |
6056 |
13 Nov 20 |
nicklas |
677 |
throw new UnsupportedOperationException("parseString"); |
5694 |
31 Oct 19 |
nicklas |
678 |
} |
6057 |
16 Nov 20 |
nicklas |
679 |
|
6057 |
16 Nov 20 |
nicklas |
680 |
/** |
6057 |
16 Nov 20 |
nicklas |
Get the last ROR value. |
6057 |
16 Nov 20 |
nicklas |
682 |
*/ |
6057 |
16 Nov 20 |
nicklas |
683 |
public int getRor() |
6057 |
16 Nov 20 |
nicklas |
684 |
{ |
6057 |
16 Nov 20 |
nicklas |
685 |
return r; |
6057 |
16 Nov 20 |
nicklas |
686 |
} |
6057 |
16 Nov 20 |
nicklas |
687 |
} |
5694 |
31 Oct 19 |
nicklas |
688 |
|
5647 |
08 Oct 19 |
nicklas |
689 |
} |