3786 |
17 Mar 16 |
olle |
1 |
package net.sf.basedb.reggie.servlet; |
3786 |
17 Mar 16 |
olle |
2 |
|
3786 |
17 Mar 16 |
olle |
3 |
import java.io.BufferedReader; |
3786 |
17 Mar 16 |
olle |
4 |
import java.io.IOException; |
3786 |
17 Mar 16 |
olle |
5 |
import java.io.InputStream; |
3786 |
17 Mar 16 |
olle |
6 |
import java.io.InputStreamReader; |
4026 |
25 Jul 16 |
olle |
7 |
import java.io.OutputStream; |
4026 |
25 Jul 16 |
olle |
8 |
import java.io.OutputStreamWriter; |
5275 |
31 Jan 19 |
nicklas |
9 |
import java.io.Writer; |
4026 |
25 Jul 16 |
olle |
10 |
import java.nio.charset.Charset; |
3786 |
17 Mar 16 |
olle |
11 |
import java.util.ArrayList; |
3786 |
17 Mar 16 |
olle |
12 |
import java.util.Arrays; |
3786 |
17 Mar 16 |
olle |
13 |
import java.util.Date; |
3816 |
29 Mar 16 |
olle |
14 |
import java.util.EnumSet; |
3786 |
17 Mar 16 |
olle |
15 |
import java.util.HashMap; |
4720 |
28 Mar 18 |
nicklas |
16 |
import java.util.HashSet; |
3786 |
17 Mar 16 |
olle |
17 |
import java.util.List; |
5262 |
24 Jan 19 |
nicklas |
18 |
import java.util.Map; |
3816 |
29 Mar 16 |
olle |
19 |
import java.util.Set; |
4708 |
19 Mar 18 |
nicklas |
20 |
import java.util.TreeSet; |
3786 |
17 Mar 16 |
olle |
21 |
|
5295 |
12 Feb 19 |
nicklas |
22 |
import javax.crypto.Cipher; |
5295 |
12 Feb 19 |
nicklas |
23 |
import javax.crypto.CipherInputStream; |
5295 |
12 Feb 19 |
nicklas |
24 |
import javax.crypto.CipherOutputStream; |
3786 |
17 Mar 16 |
olle |
25 |
import javax.servlet.ServletException; |
3786 |
17 Mar 16 |
olle |
26 |
import javax.servlet.http.HttpServlet; |
3786 |
17 Mar 16 |
olle |
27 |
import javax.servlet.http.HttpServletRequest; |
3786 |
17 Mar 16 |
olle |
28 |
import javax.servlet.http.HttpServletResponse; |
3786 |
17 Mar 16 |
olle |
29 |
|
3786 |
17 Mar 16 |
olle |
30 |
import org.json.simple.JSONArray; |
3786 |
17 Mar 16 |
olle |
31 |
import org.json.simple.JSONObject; |
3786 |
17 Mar 16 |
olle |
32 |
|
3786 |
17 Mar 16 |
olle |
33 |
import net.sf.basedb.clients.web.fileupload.FileUpload; |
3786 |
17 Mar 16 |
olle |
34 |
import net.sf.basedb.clients.web.fileupload.UploadedFile; |
3858 |
21 Apr 16 |
olle |
35 |
import net.sf.basedb.core.AnnotationBatcher; |
3858 |
21 Apr 16 |
olle |
36 |
import net.sf.basedb.core.AnnotationBatcher.Change; |
3786 |
17 Mar 16 |
olle |
37 |
import net.sf.basedb.core.AnnotationType; |
3786 |
17 Mar 16 |
olle |
38 |
import net.sf.basedb.core.Application; |
3786 |
17 Mar 16 |
olle |
39 |
import net.sf.basedb.core.BioSource; |
3786 |
17 Mar 16 |
olle |
40 |
import net.sf.basedb.core.DbControl; |
3917 |
02 May 16 |
olle |
41 |
import net.sf.basedb.core.Include; |
3858 |
21 Apr 16 |
olle |
42 |
import net.sf.basedb.core.Item; |
3786 |
17 Mar 16 |
olle |
43 |
import net.sf.basedb.core.ItemQuery; |
5273 |
31 Jan 19 |
nicklas |
44 |
import net.sf.basedb.core.ItemResultIterator; |
5273 |
31 Jan 19 |
nicklas |
45 |
import net.sf.basedb.core.ProgressReporter; |
3917 |
02 May 16 |
olle |
46 |
import net.sf.basedb.core.Sample; |
3786 |
17 Mar 16 |
olle |
47 |
import net.sf.basedb.core.SessionControl; |
3920 |
03 May 16 |
olle |
48 |
import net.sf.basedb.core.SimpleProgressReporter; |
3786 |
17 Mar 16 |
olle |
49 |
import net.sf.basedb.core.query.Expressions; |
3786 |
17 Mar 16 |
olle |
50 |
import net.sf.basedb.core.query.Hql; |
5260 |
22 Jan 19 |
nicklas |
51 |
import net.sf.basedb.core.query.Orders; |
3786 |
17 Mar 16 |
olle |
52 |
import net.sf.basedb.core.query.Restrictions; |
5256 |
21 Jan 19 |
nicklas |
53 |
import net.sf.basedb.reggie.converter.IdentityConverter; |
3786 |
17 Mar 16 |
olle |
54 |
import net.sf.basedb.reggie.JsonUtil; |
3786 |
17 Mar 16 |
olle |
55 |
import net.sf.basedb.reggie.Reggie; |
5387 |
26 Apr 19 |
nicklas |
56 |
import net.sf.basedb.reggie.activity.ActivityDef; |
3786 |
17 Mar 16 |
olle |
57 |
import net.sf.basedb.reggie.counter.CounterService; |
5332 |
20 Mar 19 |
nicklas |
58 |
import net.sf.basedb.reggie.crypto.CryptoUtil; |
3786 |
17 Mar 16 |
olle |
59 |
import net.sf.basedb.reggie.dao.Annotationtype; |
4459 |
21 Apr 17 |
nicklas |
60 |
import net.sf.basedb.reggie.dao.ReferenceDateSource; |
3786 |
17 Mar 16 |
olle |
61 |
import net.sf.basedb.reggie.dao.ReggieRole; |
3786 |
17 Mar 16 |
olle |
62 |
import net.sf.basedb.reggie.dao.Subtype; |
5273 |
31 Jan 19 |
nicklas |
63 |
import net.sf.basedb.util.ChainedProgressReporter; |
5252 |
21 Jan 19 |
nicklas |
64 |
import net.sf.basedb.util.FileUtil; |
5295 |
12 Feb 19 |
nicklas |
65 |
import net.sf.basedb.util.MD5; |
3786 |
17 Mar 16 |
olle |
66 |
import net.sf.basedb.util.Values; |
3889 |
28 Apr 16 |
olle |
67 |
import net.sf.basedb.util.encode.TabCrLfEncoderDecoder; |
3786 |
17 Mar 16 |
olle |
68 |
import net.sf.basedb.util.error.ThrowableUtil; |
5275 |
31 Jan 19 |
nicklas |
69 |
import net.sf.basedb.util.export.TableWriter; |
5288 |
08 Feb 19 |
nicklas |
70 |
import net.sf.basedb.util.filter.Filter; |
5290 |
11 Feb 19 |
nicklas |
71 |
import net.sf.basedb.util.filter.StaticFilter; |
3786 |
17 Mar 16 |
olle |
72 |
|
3786 |
17 Mar 16 |
olle |
73 |
public class IncaServlet |
3786 |
17 Mar 16 |
olle |
74 |
extends HttpServlet |
3786 |
17 Mar 16 |
olle |
75 |
{ |
3786 |
17 Mar 16 |
olle |
76 |
private static final long serialVersionUID = 2550233926528990677L; |
5261 |
23 Jan 19 |
nicklas |
77 |
|
3972 |
25 May 16 |
olle |
78 |
private static final String REPORT_TYPE_IMPORT = "import"; |
4026 |
25 Jul 16 |
olle |
79 |
private static final String REPORT_TYPE_IMPORT_OUTPUT_CSV = "import_output_csv"; |
3972 |
25 May 16 |
olle |
80 |
private static final String REPORT_TYPE_STATISTICS = "statistics"; |
3980 |
27 May 16 |
olle |
81 |
private static final String REPORT_TYPE_STATISTICS_CSV = "statistics_csv"; |
3972 |
25 May 16 |
olle |
82 |
private static final String INCA_IMPORT_REPORT_FILENAME = "inca_import_report.txt"; |
4026 |
25 Jul 16 |
olle |
83 |
private static final String INCA_IMPORT_OUTPUT_CSV_FILENAME = "inca_import_output.csv"; |
3972 |
25 May 16 |
olle |
84 |
private static final String INCA_STATISTICS_REPORT_FILENAME = "inca_statistics_report.txt"; |
3980 |
27 May 16 |
olle |
85 |
private static final String INCA_STATISTICS_CSV_FILENAME = "inca_statistics.csv"; |
3972 |
25 May 16 |
olle |
86 |
private static final String IMPORT_PROGRESS_ID = "inca-import-progress"; |
3972 |
25 May 16 |
olle |
87 |
private static final String STATISTICS_PROGRESS_ID = "inca-stat-progress"; |
3786 |
17 Mar 16 |
olle |
88 |
|
3786 |
17 Mar 16 |
olle |
89 |
public IncaServlet() |
3786 |
17 Mar 16 |
olle |
90 |
{} |
3786 |
17 Mar 16 |
olle |
91 |
|
3786 |
17 Mar 16 |
olle |
92 |
@Override |
3786 |
17 Mar 16 |
olle |
93 |
protected void doGet(HttpServletRequest req, HttpServletResponse resp) |
3786 |
17 Mar 16 |
olle |
94 |
throws ServletException, IOException |
3786 |
17 Mar 16 |
olle |
95 |
{ |
3786 |
17 Mar 16 |
olle |
96 |
String cmd = req.getParameter("cmd"); |
3786 |
17 Mar 16 |
olle |
97 |
JsonUtil.setJsonResponseHeaders(resp); |
3786 |
17 Mar 16 |
olle |
98 |
|
3786 |
17 Mar 16 |
olle |
99 |
JSONObject json = new JSONObject(); |
3786 |
17 Mar 16 |
olle |
100 |
json.put("status", "ok"); |
3786 |
17 Mar 16 |
olle |
101 |
|
3975 |
26 May 16 |
nicklas |
102 |
final SessionControl sc = Reggie.getSessionControl(req); |
3786 |
17 Mar 16 |
olle |
103 |
DbControl dc = null; |
3786 |
17 Mar 16 |
olle |
104 |
try |
3786 |
17 Mar 16 |
olle |
105 |
{ |
3951 |
18 May 16 |
olle |
106 |
if ("CheckForIncaReportFile".equals(cmd)) |
3786 |
17 Mar 16 |
olle |
107 |
{ |
3951 |
18 May 16 |
olle |
108 |
String reportType = req.getParameter("reporttype"); |
5294 |
12 Feb 19 |
nicklas |
109 |
boolean fileFound = checkForReportFile(sc, reportType); |
4032 |
28 Jul 16 |
olle |
110 |
json.put("reporttype", reportType); |
4032 |
28 Jul 16 |
olle |
111 |
json.put("incaReportFileExists", fileFound); |
4032 |
28 Jul 16 |
olle |
112 |
} |
4032 |
28 Jul 16 |
olle |
113 |
else if ("CheckForIncaReportFiles".equals(cmd)) |
4032 |
28 Jul 16 |
olle |
114 |
{ |
4032 |
28 Jul 16 |
olle |
115 |
String reportTypes = req.getParameter("reporttypes"); |
3786 |
17 Mar 16 |
olle |
116 |
|
4032 |
28 Jul 16 |
olle |
117 |
JSONObject jsonResult = new JSONObject(); |
4032 |
28 Jul 16 |
olle |
118 |
if (reportTypes != null) |
3856 |
18 Apr 16 |
olle |
119 |
{ |
4032 |
28 Jul 16 |
olle |
120 |
String[] reportTypeArr = reportTypes.split(",", -1); |
4032 |
28 Jul 16 |
olle |
121 |
for (int i = 0; i < reportTypeArr.length; i++) |
4032 |
28 Jul 16 |
olle |
122 |
{ |
5415 |
09 May 19 |
nicklas |
123 |
String reportType = reportTypeArr[i]; |
5294 |
12 Feb 19 |
nicklas |
124 |
boolean fileFound = checkForReportFile(sc, reportType); |
4032 |
28 Jul 16 |
olle |
125 |
jsonResult.put(reportType, fileFound); |
4032 |
28 Jul 16 |
olle |
126 |
} |
3856 |
18 Apr 16 |
olle |
127 |
} |
4032 |
28 Jul 16 |
olle |
128 |
json.put("result", jsonResult); |
3856 |
18 Apr 16 |
olle |
129 |
} |
3951 |
18 May 16 |
olle |
130 |
else if ("DownloadIncaReportFile".equals(cmd)) |
3856 |
18 Apr 16 |
olle |
131 |
{ |
5252 |
21 Jan 19 |
nicklas |
132 |
json = null; // No JSON output |
5252 |
21 Jan 19 |
nicklas |
133 |
|
6336 |
16 Jun 21 |
nicklas |
134 |
dc = sc.newDbControl(":INCA Import"); |
5252 |
21 Jan 19 |
nicklas |
135 |
ReggieRole.checkPermission(dc, "'" + cmd + "' wizard", ReggieRole.PATIENT_CURATOR, ReggieRole.ADMINISTRATOR); |
5252 |
21 Jan 19 |
nicklas |
136 |
|
3951 |
18 May 16 |
olle |
137 |
String reportType = req.getParameter("reporttype"); |
3951 |
18 May 16 |
olle |
138 |
String filename = fetchReportFileName(reportType); |
5279 |
05 Feb 19 |
nicklas |
139 |
String reportPath = getReportCacheKey(sc, reportType); |
3856 |
18 Apr 16 |
olle |
140 |
|
3786 |
17 Mar 16 |
olle |
141 |
resp.setHeader("Content-Disposition", "attachment; filename=" + filename); |
3786 |
17 Mar 16 |
olle |
142 |
resp.setContentType("text/plain"); |
3786 |
17 Mar 16 |
olle |
143 |
resp.setCharacterEncoding("UTF-8"); |
5279 |
05 Feb 19 |
nicklas |
144 |
|
5295 |
12 Feb 19 |
nicklas |
145 |
InputStream in = Application.getStaticCache().read(reportPath, 5); |
5295 |
12 Feb 19 |
nicklas |
146 |
|
5332 |
20 Mar 19 |
nicklas |
147 |
Cipher c = CryptoUtil.createSessionCipher(sc, Cipher.DECRYPT_MODE, reportType); |
5295 |
12 Feb 19 |
nicklas |
148 |
in = new CipherInputStream(in, c); |
5295 |
12 Feb 19 |
nicklas |
149 |
|
5252 |
21 Jan 19 |
nicklas |
150 |
OutputStream out = resp.getOutputStream(); |
5295 |
12 Feb 19 |
nicklas |
151 |
FileUtil.copy(in, out); |
5295 |
12 Feb 19 |
nicklas |
152 |
in.close(); |
5252 |
21 Jan 19 |
nicklas |
153 |
out.flush(); |
5252 |
21 Jan 19 |
nicklas |
154 |
out.close(); |
3786 |
17 Mar 16 |
olle |
155 |
} |
3786 |
17 Mar 16 |
olle |
156 |
} |
3786 |
17 Mar 16 |
olle |
157 |
catch (Throwable t) |
3786 |
17 Mar 16 |
olle |
158 |
{ |
3786 |
17 Mar 16 |
olle |
159 |
t.printStackTrace(); |
5252 |
21 Jan 19 |
nicklas |
160 |
if (json != null) |
5252 |
21 Jan 19 |
nicklas |
161 |
{ |
5252 |
21 Jan 19 |
nicklas |
162 |
json.clear(); |
5252 |
21 Jan 19 |
nicklas |
163 |
json.put("status", "error"); |
5252 |
21 Jan 19 |
nicklas |
164 |
json.put("message", t.getMessage()); |
5252 |
21 Jan 19 |
nicklas |
165 |
json.put("stacktrace", ThrowableUtil.stackTraceToString(t)); |
5252 |
21 Jan 19 |
nicklas |
166 |
} |
5252 |
21 Jan 19 |
nicklas |
167 |
else |
5252 |
21 Jan 19 |
nicklas |
168 |
{ |
5252 |
21 Jan 19 |
nicklas |
169 |
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, t.getClass().getName() + ": " + t.getMessage()); |
5252 |
21 Jan 19 |
nicklas |
170 |
} |
3786 |
17 Mar 16 |
olle |
171 |
} |
3786 |
17 Mar 16 |
olle |
172 |
finally |
3786 |
17 Mar 16 |
olle |
173 |
{ |
3786 |
17 Mar 16 |
olle |
174 |
if (dc != null) dc.close(); |
5252 |
21 Jan 19 |
nicklas |
175 |
if (json != null) |
5252 |
21 Jan 19 |
nicklas |
176 |
{ |
5252 |
21 Jan 19 |
nicklas |
177 |
json.writeJSONString(resp.getWriter()); |
5252 |
21 Jan 19 |
nicklas |
178 |
} |
3786 |
17 Mar 16 |
olle |
179 |
} |
3786 |
17 Mar 16 |
olle |
180 |
} |
3786 |
17 Mar 16 |
olle |
181 |
|
3786 |
17 Mar 16 |
olle |
182 |
@Override |
3786 |
17 Mar 16 |
olle |
183 |
protected void doPost(HttpServletRequest req, HttpServletResponse resp) |
3786 |
17 Mar 16 |
olle |
184 |
throws ServletException, IOException |
3786 |
17 Mar 16 |
olle |
185 |
{ |
3786 |
17 Mar 16 |
olle |
186 |
String cmd = req.getParameter("cmd"); |
3786 |
17 Mar 16 |
olle |
187 |
JsonUtil.setJsonResponseHeaders(resp); |
3786 |
17 Mar 16 |
olle |
188 |
|
3786 |
17 Mar 16 |
olle |
189 |
JSONObject json = new JSONObject(); |
3786 |
17 Mar 16 |
olle |
190 |
json.put("status", "ok"); |
3786 |
17 Mar 16 |
olle |
191 |
|
3786 |
17 Mar 16 |
olle |
192 |
JSONArray jsonMessages = new JSONArray(); |
3786 |
17 Mar 16 |
olle |
193 |
|
3975 |
26 May 16 |
nicklas |
194 |
final SessionControl sc = Reggie.getSessionControl(req); |
3786 |
17 Mar 16 |
olle |
195 |
DbControl dc = null; |
5288 |
08 Feb 19 |
nicklas |
196 |
String progressId = null; |
3786 |
17 Mar 16 |
olle |
197 |
try |
3786 |
17 Mar 16 |
olle |
198 |
{ |
3786 |
17 Mar 16 |
olle |
199 |
if ("ImportInca".equals(cmd)) |
3786 |
17 Mar 16 |
olle |
200 |
{ |
6336 |
16 Jun 21 |
nicklas |
201 |
dc = sc.newDbControl(":INCA import"); |
5280 |
06 Feb 19 |
nicklas |
202 |
|
3928 |
12 May 16 |
olle |
203 |
ReggieRole.checkPermission(dc, "'" + cmd + "' wizard", ReggieRole.PATIENT_CURATOR, ReggieRole.ADMINISTRATOR); |
3928 |
12 May 16 |
olle |
204 |
|
3920 |
03 May 16 |
olle |
205 |
SimpleProgressReporter progress = new SimpleProgressReporter(null); |
3950 |
18 May 16 |
olle |
206 |
progressId = IMPORT_PROGRESS_ID; |
3950 |
18 May 16 |
olle |
207 |
sc.setSessionSetting(progressId, progress); |
5256 |
21 Jan 19 |
nicklas |
208 |
|
5270 |
29 Jan 19 |
nicklas |
// 'simplecheck' or 'fullcheck', a null value means actual import |
5270 |
29 Jan 19 |
nicklas |
210 |
String checkType = Values.getStringOrNull(req.getParameter("checkType")); |
5415 |
09 May 19 |
nicklas |
211 |
Date incaExportDate = Reggie.CONVERTER_STRING_TO_DATE.convert(req.getParameter("exportdate")); |
5269 |
28 Jan 19 |
nicklas |
212 |
Date incaImportDate = new Date(); |
5269 |
28 Jan 19 |
nicklas |
213 |
|
5256 |
21 Jan 19 |
nicklas |
// Parse the INCA file |
5256 |
21 Jan 19 |
nicklas |
215 |
FileUpload fileUpload = new FileUpload(req); |
5256 |
21 Jan 19 |
nicklas |
216 |
UploadedFile uf = fileUpload.next(); |
5269 |
28 Jan 19 |
nicklas |
217 |
|
5273 |
31 Jan 19 |
nicklas |
218 |
progress.display(5, "Parsing '" + uf.getFilename() + "'..."); |
5273 |
31 Jan 19 |
nicklas |
219 |
|
5288 |
08 Feb 19 |
nicklas |
220 |
IncaFile incaFile = new IncaFile(uf.getFilename(), false); |
5270 |
29 Jan 19 |
nicklas |
221 |
incaFile.exportDate = incaExportDate; |
5269 |
28 Jan 19 |
nicklas |
222 |
|
5269 |
28 Jan 19 |
nicklas |
223 |
incaFile.parse(uf.getInputStream()); |
5260 |
22 Jan 19 |
nicklas |
224 |
|
5269 |
28 Jan 19 |
nicklas |
// Find columns that map to defined INCA2_* annotation types |
5280 |
06 Feb 19 |
nicklas |
226 |
/* |
5280 |
06 Feb 19 |
nicklas |
Include options to make a query only return items shared |
5280 |
06 Feb 19 |
nicklas |
to either the current project or the logged-in user. |
5280 |
06 Feb 19 |
nicklas |
The latter is needed for annotation types that represent sensitive PAD numbers, |
5280 |
06 Feb 19 |
nicklas |
and therefore only are accessible to users belonging to |
5280 |
06 Feb 19 |
nicklas |
the PatientCurator group. |
5280 |
06 Feb 19 |
nicklas |
232 |
*/ |
5280 |
06 Feb 19 |
nicklas |
233 |
ItemQuery<AnnotationType> query = AnnotationType.getQuery(null); |
5280 |
06 Feb 19 |
nicklas |
234 |
query.setIncludes(EnumSet.of(Include.IN_PROJECT, Include.SHARED)); |
5280 |
06 Feb 19 |
nicklas |
235 |
query.restrict(Restrictions.like(Hql.property("name"), Expressions.string("INCA2_" + "%"))); |
5280 |
06 Feb 19 |
nicklas |
236 |
query.order(Orders.asc(Hql.property("name"))); |
5280 |
06 Feb 19 |
nicklas |
237 |
List<AnnotationType> incaAnnotationTypes = query.list(dc); |
5289 |
11 Feb 19 |
nicklas |
238 |
incaFile.mapAnnotationColumns(incaAnnotationTypes, false); |
5256 |
21 Jan 19 |
nicklas |
239 |
|
5269 |
28 Jan 19 |
nicklas |
// Check for duplicate laterality |
5269 |
28 Jan 19 |
nicklas |
241 |
incaFile.doLateralityCheck(); |
5269 |
28 Jan 19 |
nicklas |
242 |
|
5269 |
28 Jan 19 |
nicklas |
// Check data values against annotation types |
5288 |
08 Feb 19 |
nicklas |
244 |
incaFile.doDataCheck(null); |
5269 |
28 Jan 19 |
nicklas |
245 |
|
5270 |
29 Jan 19 |
nicklas |
246 |
if (checkType == null || "fullcheck".equals(checkType)) |
5267 |
25 Jan 19 |
nicklas |
247 |
{ |
5273 |
31 Jan 19 |
nicklas |
// Remaining data lines are checked against the database for a patient and case |
5273 |
31 Jan 19 |
nicklas |
249 |
ChainedProgressReporter cProgress = new ChainedProgressReporter(progress); |
5279 |
05 Feb 19 |
nicklas |
250 |
int progressEnd = checkType == null ? 30 : 90; |
5279 |
05 Feb 19 |
nicklas |
251 |
cProgress.setRange(5, progressEnd); |
5273 |
31 Jan 19 |
nicklas |
252 |
incaFile.doDatabaseMapping(dc, cProgress); |
5279 |
05 Feb 19 |
nicklas |
253 |
|
5279 |
05 Feb 19 |
nicklas |
254 |
cProgress.setRange(progressEnd, progressEnd+5); |
5293 |
12 Feb 19 |
nicklas |
255 |
createIncaOutputFile(sc, incaFile, REPORT_TYPE_IMPORT_OUTPUT_CSV, cProgress); |
5267 |
25 Jan 19 |
nicklas |
256 |
} |
5267 |
25 Jan 19 |
nicklas |
257 |
|
5273 |
31 Jan 19 |
nicklas |
// Summarize all excluded data lines |
5273 |
31 Jan 19 |
nicklas |
259 |
incaFile.summarizeExcludedLines(); |
5273 |
31 Jan 19 |
nicklas |
260 |
dc.close(); |
5273 |
31 Jan 19 |
nicklas |
261 |
|
5270 |
29 Jan 19 |
nicklas |
262 |
if (checkType == null) |
3786 |
17 Mar 16 |
olle |
263 |
{ |
5273 |
31 Jan 19 |
nicklas |
// Actual import |
3858 |
21 Apr 16 |
olle |
// Get new DbControl for use with AnnotationBatcher |
6336 |
16 Jun 21 |
nicklas |
266 |
dc = sc.newDbControl(dc.getName()); |
5269 |
28 Jan 19 |
nicklas |
267 |
|
5273 |
31 Jan 19 |
nicklas |
268 |
ChainedProgressReporter cProgress = new ChainedProgressReporter(progress); |
5279 |
05 Feb 19 |
nicklas |
269 |
cProgress.setRange(35, 95); |
5273 |
31 Jan 19 |
nicklas |
270 |
incaFile.doImport(dc, cProgress); |
5269 |
28 Jan 19 |
nicklas |
271 |
|
5273 |
31 Jan 19 |
nicklas |
272 |
progress.display(98, "Comitting transaction. This may take a while..."); |
5421 |
13 May 19 |
nicklas |
273 |
ActivityDef.UPDATED_INCA.create(dc, incaFile.numCasesUpdated); |
5279 |
05 Feb 19 |
nicklas |
274 |
dc.commit(); |
5273 |
31 Jan 19 |
nicklas |
275 |
|
5273 |
31 Jan 19 |
nicklas |
276 |
jsonMessages.add("Processed " + incaFile.numCasesChecked + " data lines for " + incaFile.numCasesChecked + " cases."); |
5273 |
31 Jan 19 |
nicklas |
277 |
jsonMessages.add(incaFile.numCasesUpdated + " cases updated with " + |
5273 |
31 Jan 19 |
nicklas |
278 |
incaFile.numAnnotations[Change.ADDED.ordinal()] + " new, " + |
5273 |
31 Jan 19 |
nicklas |
279 |
incaFile.numAnnotations[Change.UPDATED.ordinal()] + " modified and " + |
5273 |
31 Jan 19 |
nicklas |
280 |
incaFile.numAnnotations[Change.DELETED.ordinal()] + " removed INCA annotations."); |
5273 |
31 Jan 19 |
nicklas |
281 |
jsonMessages.add(incaFile.numAnnotations[Change.NO_CHANGE.ordinal()] + " annotations without change."); |
5273 |
31 Jan 19 |
nicklas |
282 |
if (incaFile.numCasesUpdated < incaFile.numCasesChecked) |
3858 |
21 Apr 16 |
olle |
283 |
{ |
5273 |
31 Jan 19 |
nicklas |
284 |
jsonMessages.add((incaFile.numCasesChecked - incaFile.numCasesUpdated) + " cases without change."); |
3858 |
21 Apr 16 |
olle |
285 |
} |
3786 |
17 Mar 16 |
olle |
286 |
} |
5287 |
08 Feb 19 |
nicklas |
287 |
incaFile.endDate = new Date(); |
5263 |
24 Jan 19 |
nicklas |
288 |
|
3786 |
17 Mar 16 |
olle |
// Create INCA import report file |
5279 |
05 Feb 19 |
nicklas |
290 |
createIncaImportReportFile(sc, incaFile, checkType); |
5273 |
31 Jan 19 |
nicklas |
291 |
|
5273 |
31 Jan 19 |
nicklas |
292 |
JSONObject jsonIncaFile = incaFile.getFileInfoInJSON(new JSONObject()); |
5273 |
31 Jan 19 |
nicklas |
293 |
|
5263 |
24 Jan 19 |
nicklas |
294 |
json.put("fileInfo", jsonIncaFile); |
5270 |
29 Jan 19 |
nicklas |
295 |
json.put("checkType", checkType); |
5273 |
31 Jan 19 |
nicklas |
296 |
progress.display(100, "Completed"); |
3786 |
17 Mar 16 |
olle |
297 |
} |
3963 |
20 May 16 |
olle |
298 |
else if ("IncaStatistics".equals(cmd)) |
3963 |
20 May 16 |
olle |
299 |
{ |
6336 |
16 Jun 21 |
nicklas |
300 |
dc = sc.newDbControl(":INCA statistics"); |
3963 |
20 May 16 |
olle |
301 |
|
3963 |
20 May 16 |
olle |
302 |
ReggieRole.checkPermission(dc, "'" + cmd + "' wizard", ReggieRole.PATIENT_CURATOR, ReggieRole.ADMINISTRATOR); |
3963 |
20 May 16 |
olle |
303 |
|
3963 |
20 May 16 |
olle |
304 |
SimpleProgressReporter progress = new SimpleProgressReporter(null); |
3963 |
20 May 16 |
olle |
305 |
progressId = STATISTICS_PROGRESS_ID; |
3963 |
20 May 16 |
olle |
306 |
sc.setSessionSetting(progressId, progress); |
5287 |
08 Feb 19 |
nicklas |
307 |
|
5287 |
08 Feb 19 |
nicklas |
308 |
String checkType = Values.getStringOrNull(req.getParameter("checkType")); |
5287 |
08 Feb 19 |
nicklas |
309 |
|
5288 |
08 Feb 19 |
nicklas |
// Which date to use for statistics selection? 'diagnosis' or 'operation' date |
5288 |
08 Feb 19 |
nicklas |
311 |
KeyColumn dateVariableCol = "operation".equals(req.getParameter("statdatevar")) ? |
5288 |
08 Feb 19 |
nicklas |
312 |
KeyColumn.OP_KIR_DAT : KeyColumn.A_DIAG_DAT; |
5288 |
08 Feb 19 |
nicklas |
313 |
|
5288 |
08 Feb 19 |
nicklas |
// Date interval for the statistics |
5288 |
08 Feb 19 |
nicklas |
315 |
Date statStartDate = Reggie.CONVERTER_STRING_TO_DATE.convert(req.getParameter("startdate")); |
5288 |
08 Feb 19 |
nicklas |
316 |
Date statEndDate = Reggie.CONVERTER_STRING_TO_DATE.convert(req.getParameter("enddate")); |
5288 |
08 Feb 19 |
nicklas |
317 |
|
5288 |
08 Feb 19 |
nicklas |
// Filter for cancer type |
5288 |
08 Feb 19 |
nicklas |
319 |
String cancerType = Values.getStringOrNull(req.getParameter("cancertype")); |
5288 |
08 Feb 19 |
nicklas |
320 |
|
5287 |
08 Feb 19 |
nicklas |
// Parse the INCA file |
5287 |
08 Feb 19 |
nicklas |
322 |
FileUpload fileUpload = new FileUpload(req); |
5287 |
08 Feb 19 |
nicklas |
323 |
UploadedFile uf = fileUpload.next(); |
5287 |
08 Feb 19 |
nicklas |
324 |
|
5287 |
08 Feb 19 |
nicklas |
325 |
progress.display(5, "Parsing '" + uf.getFilename() + "'..."); |
5287 |
08 Feb 19 |
nicklas |
326 |
|
5288 |
08 Feb 19 |
nicklas |
327 |
IncaFile incaFile = new IncaFile(uf.getFilename(), true); |
5287 |
08 Feb 19 |
nicklas |
328 |
incaFile.parse(uf.getInputStream()); |
5287 |
08 Feb 19 |
nicklas |
329 |
|
5288 |
08 Feb 19 |
nicklas |
// Find columns that map to defined INCA2_* annotation types |
5288 |
08 Feb 19 |
nicklas |
331 |
ItemQuery<AnnotationType> query = AnnotationType.getQuery(null); |
5288 |
08 Feb 19 |
nicklas |
332 |
query.setIncludes(EnumSet.of(Include.IN_PROJECT, Include.SHARED)); |
5288 |
08 Feb 19 |
nicklas |
333 |
query.restrict(Restrictions.like(Hql.property("name"), Expressions.string("INCA2_" + "%"))); |
5288 |
08 Feb 19 |
nicklas |
334 |
query.order(Orders.asc(Hql.property("name"))); |
5288 |
08 Feb 19 |
nicklas |
335 |
List<AnnotationType> incaAnnotationTypes = query.list(dc); |
5289 |
11 Feb 19 |
nicklas |
336 |
incaFile.mapAnnotationColumns(incaAnnotationTypes, true); |
5288 |
08 Feb 19 |
nicklas |
337 |
|
5288 |
08 Feb 19 |
nicklas |
// Check that we have all columns required for statistics |
5288 |
08 Feb 19 |
nicklas |
// and that they are mapped to annotation types |
5288 |
08 Feb 19 |
nicklas |
340 |
KeyColumn[] columnsForStatistics = {KeyColumn.CANCER_TYPE, dateVariableCol, |
5288 |
08 Feb 19 |
nicklas |
341 |
KeyColumn.ER_STATUS, KeyColumn.PGR_STATUS, KeyColumn.HER2_STATUS, |
5288 |
08 Feb 19 |
nicklas |
342 |
KeyColumn.AGE, KeyColumn.NHG_STATUS, KeyColumn.TUMOR_SIZE }; |
5288 |
08 Feb 19 |
nicklas |
343 |
Header[] headersForStatistics = incaFile.checkKeyColumns(true, columnsForStatistics); |
5288 |
08 Feb 19 |
nicklas |
344 |
|
5288 |
08 Feb 19 |
nicklas |
// Check for duplicate laterality |
5288 |
08 Feb 19 |
nicklas |
346 |
incaFile.doLateralityCheck(); |
5288 |
08 Feb 19 |
nicklas |
347 |
|
5288 |
08 Feb 19 |
nicklas |
// Check data values against key columns requried for statistics |
5288 |
08 Feb 19 |
nicklas |
349 |
incaFile.doDataCheck(headersForStatistics); |
5288 |
08 Feb 19 |
nicklas |
350 |
|
5288 |
08 Feb 19 |
nicklas |
// Perform filtering on date |
5288 |
08 Feb 19 |
nicklas |
352 |
int dateColIndex = incaFile.keyColumns[dateVariableCol.ordinal()]; |
5290 |
11 Feb 19 |
nicklas |
353 |
DateFilter dateFilter = new DateFilter(dateColIndex, statStartDate, statEndDate); |
5288 |
08 Feb 19 |
nicklas |
354 |
incaFile.doFilter(dateFilter, ExcludedLine.FILTERED_BY_DATE); |
5288 |
08 Feb 19 |
nicklas |
355 |
|
5288 |
08 Feb 19 |
nicklas |
// Perform filtering on cancer type |
5290 |
11 Feb 19 |
nicklas |
357 |
ColumnValueFilter cancerTypeFilter = null; |
5288 |
08 Feb 19 |
nicklas |
358 |
if (cancerType != null) |
5288 |
08 Feb 19 |
nicklas |
359 |
{ |
5288 |
08 Feb 19 |
nicklas |
360 |
int cancerTypeColIndex = incaFile.keyColumns[KeyColumn.CANCER_TYPE.ordinal()]; |
5290 |
11 Feb 19 |
nicklas |
361 |
cancerTypeFilter = new ColumnValueFilter(cancerTypeColIndex, cancerType.equals("invasive") ? 1 : 2); |
5288 |
08 Feb 19 |
nicklas |
362 |
incaFile.doFilter(cancerTypeFilter, ExcludedLine.FILTERED_BY_CANCER_TYPE); |
5290 |
11 Feb 19 |
nicklas |
363 |
} |
5292 |
12 Feb 19 |
nicklas |
364 |
|
5292 |
12 Feb 19 |
nicklas |
365 |
if (checkType == null || "fullstatistics".equals(checkType)) |
5292 |
12 Feb 19 |
nicklas |
366 |
{ |
5292 |
12 Feb 19 |
nicklas |
// Remaining data lines are checked against the database for a patient and case |
5292 |
12 Feb 19 |
nicklas |
368 |
ChainedProgressReporter cProgress = new ChainedProgressReporter(progress); |
5292 |
12 Feb 19 |
nicklas |
369 |
cProgress.setRange(5, 90); |
5292 |
12 Feb 19 |
nicklas |
370 |
incaFile.doDatabaseMapping(dc, cProgress); |
5292 |
12 Feb 19 |
nicklas |
371 |
|
5293 |
12 Feb 19 |
nicklas |
372 |
cProgress.setRange(90, 95); |
5293 |
12 Feb 19 |
nicklas |
373 |
createIncaOutputFile(sc, incaFile, REPORT_TYPE_STATISTICS_CSV, cProgress); |
5292 |
12 Feb 19 |
nicklas |
374 |
} |
5292 |
12 Feb 19 |
nicklas |
375 |
|
5287 |
08 Feb 19 |
nicklas |
// Summarize all excluded data lines |
5287 |
08 Feb 19 |
nicklas |
377 |
incaFile.summarizeExcludedLines(); |
5290 |
11 Feb 19 |
nicklas |
378 |
|
5292 |
12 Feb 19 |
nicklas |
379 |
StatisticalVariable allEntries = new StatisticalVariable("All entries", -1, null); |
5290 |
11 Feb 19 |
nicklas |
380 |
allEntries.addGroup("All", new StaticFilter<>(true)); |
5290 |
11 Feb 19 |
nicklas |
381 |
|
5292 |
12 Feb 19 |
nicklas |
382 |
StatisticalVariable erStatus = new StatisticalVariable("ER Status", incaFile.keyColumns[KeyColumn.ER_STATUS.ordinal()], null); |
5290 |
11 Feb 19 |
nicklas |
383 |
erStatus.addValueGroup("Positive", 1).addValueGroup("Negative", 2).addNaGroup(); |
5290 |
11 Feb 19 |
nicklas |
384 |
|
5292 |
12 Feb 19 |
nicklas |
385 |
StatisticalVariable pgrStatus = new StatisticalVariable("PgR Status", incaFile.keyColumns[KeyColumn.PGR_STATUS.ordinal()], null); |
5290 |
11 Feb 19 |
nicklas |
386 |
pgrStatus.addValueGroup("Positive", 1).addValueGroup("Negative", 2).addNaGroup(); |
5290 |
11 Feb 19 |
nicklas |
387 |
|
5292 |
12 Feb 19 |
nicklas |
388 |
StatisticalVariable her2Status = new StatisticalVariable("HER2 Status", incaFile.keyColumns[KeyColumn.HER2_STATUS.ordinal()], null); |
5290 |
11 Feb 19 |
nicklas |
389 |
her2Status.addValueGroup("Positive", 1).addValueGroup("Negative", 2).addNaGroup(); |
5290 |
11 Feb 19 |
nicklas |
390 |
|
5292 |
12 Feb 19 |
nicklas |
391 |
StatisticalVariable age = new StatisticalVariable("Age", incaFile.keyColumns[KeyColumn.AGE.ordinal()], "years"); |
5290 |
11 Feb 19 |
nicklas |
392 |
age.addGroup("<50", new BetweenFilter(age.column, -1, 49)) |
5290 |
11 Feb 19 |
nicklas |
393 |
.addGroup("50-70", new BetweenFilter(age.column, 50, 70)) |
5290 |
11 Feb 19 |
nicklas |
394 |
.addGroup(">70", new BetweenFilter(age.column, 71, -1)) |
5290 |
11 Feb 19 |
nicklas |
395 |
.addNaGroup(); |
5290 |
11 Feb 19 |
nicklas |
396 |
|
5292 |
12 Feb 19 |
nicklas |
397 |
StatisticalVariable nhg = new StatisticalVariable("NHG", incaFile.keyColumns[KeyColumn.NHG_STATUS.ordinal()], null); |
5290 |
11 Feb 19 |
nicklas |
398 |
nhg.addValueGroup("Grade 1", 1).addValueGroup("Grade 2", 2).addValueGroup("Grade 3", 3).addNaGroup(); |
5290 |
11 Feb 19 |
nicklas |
399 |
|
5292 |
12 Feb 19 |
nicklas |
400 |
StatisticalVariable tumorSize = new StatisticalVariable("Tumor size", incaFile.keyColumns[KeyColumn.TUMOR_SIZE.ordinal()], "mm"); |
5290 |
11 Feb 19 |
nicklas |
401 |
tumorSize.addGroup("<10", new BetweenFilter(tumorSize.column, -1, 9)) |
5290 |
11 Feb 19 |
nicklas |
402 |
.addGroup("10-20", new BetweenFilter(tumorSize.column, 10, 20)) |
5290 |
11 Feb 19 |
nicklas |
403 |
.addGroup(">20", new BetweenFilter(tumorSize.column, 21, -1)) |
5290 |
11 Feb 19 |
nicklas |
404 |
.addNaGroup(); |
5290 |
11 Feb 19 |
nicklas |
405 |
|
5290 |
11 Feb 19 |
nicklas |
406 |
incaFile.doStatistics(allEntries, erStatus, pgrStatus, her2Status, age, nhg, tumorSize); |
5290 |
11 Feb 19 |
nicklas |
407 |
|
5287 |
08 Feb 19 |
nicklas |
408 |
dc.close(); |
5287 |
08 Feb 19 |
nicklas |
409 |
|
5287 |
08 Feb 19 |
nicklas |
410 |
incaFile.endDate = new Date(); |
5287 |
08 Feb 19 |
nicklas |
411 |
|
5287 |
08 Feb 19 |
nicklas |
// Create INCA statistics report file |
5290 |
11 Feb 19 |
nicklas |
413 |
createIncaStatisticsReportFile(sc, incaFile, checkType, dateFilter, cancerTypeFilter); |
5289 |
11 Feb 19 |
nicklas |
414 |
|
5287 |
08 Feb 19 |
nicklas |
415 |
JSONObject jsonIncaFile = incaFile.getFileInfoInJSON(new JSONObject()); |
5287 |
08 Feb 19 |
nicklas |
416 |
|
5287 |
08 Feb 19 |
nicklas |
417 |
json.put("fileInfo", jsonIncaFile); |
5287 |
08 Feb 19 |
nicklas |
418 |
json.put("checkType", checkType); |
5287 |
08 Feb 19 |
nicklas |
419 |
progress.display(100, "Completed"); |
5287 |
08 Feb 19 |
nicklas |
420 |
|
3963 |
20 May 16 |
olle |
421 |
} |
4032 |
28 Jul 16 |
olle |
422 |
else if ("DeleteIncaReportFile".equals(cmd)) |
4032 |
28 Jul 16 |
olle |
423 |
{ |
6336 |
16 Jun 21 |
nicklas |
424 |
dc = sc.newDbControl(":INCA import"); |
4032 |
28 Jul 16 |
olle |
425 |
|
4032 |
28 Jul 16 |
olle |
426 |
ReggieRole.checkPermission(dc, "'" + cmd + "' wizard", ReggieRole.PATIENT_CURATOR, ReggieRole.ADMINISTRATOR); |
4032 |
28 Jul 16 |
olle |
427 |
String reportType = req.getParameter("reporttype"); |
4032 |
28 Jul 16 |
olle |
428 |
|
4032 |
28 Jul 16 |
olle |
// Currently restrict report file removal to INCA import output CSV file |
4032 |
28 Jul 16 |
olle |
430 |
if (reportType != null && reportType.equals(REPORT_TYPE_IMPORT_OUTPUT_CSV)) |
4032 |
28 Jul 16 |
olle |
431 |
{ |
5294 |
12 Feb 19 |
nicklas |
432 |
String reportFilePath = getReportCacheKey(sc, reportType); |
5294 |
12 Feb 19 |
nicklas |
433 |
boolean deleted = Application.getStaticCache().delete(reportFilePath, 5); |
5294 |
12 Feb 19 |
nicklas |
434 |
if (deleted) |
4032 |
28 Jul 16 |
olle |
435 |
{ |
5294 |
12 Feb 19 |
nicklas |
436 |
jsonMessages.add("The file has been deleted"); |
4032 |
28 Jul 16 |
olle |
437 |
} |
5294 |
12 Feb 19 |
nicklas |
438 |
else |
4032 |
28 Jul 16 |
olle |
439 |
{ |
5294 |
12 Feb 19 |
nicklas |
440 |
jsonMessages.add("The file could not be deleted"); |
4032 |
28 Jul 16 |
olle |
441 |
} |
4032 |
28 Jul 16 |
olle |
442 |
} |
5294 |
12 Feb 19 |
nicklas |
443 |
else |
4032 |
28 Jul 16 |
olle |
444 |
{ |
5294 |
12 Feb 19 |
nicklas |
445 |
jsonMessages.add("Not allowed"); |
4032 |
28 Jul 16 |
olle |
446 |
} |
4032 |
28 Jul 16 |
olle |
447 |
} |
4032 |
28 Jul 16 |
olle |
448 |
json.put("cmd", cmd); |
3786 |
17 Mar 16 |
olle |
449 |
json.put("messages", jsonMessages); |
3786 |
17 Mar 16 |
olle |
450 |
CounterService.getInstance().setForceCount(); |
3786 |
17 Mar 16 |
olle |
451 |
} |
3786 |
17 Mar 16 |
olle |
452 |
catch (Throwable t) |
3786 |
17 Mar 16 |
olle |
453 |
{ |
3786 |
17 Mar 16 |
olle |
454 |
t.printStackTrace(); |
3786 |
17 Mar 16 |
olle |
455 |
json.clear(); |
3786 |
17 Mar 16 |
olle |
456 |
json.put("status", "error"); |
3786 |
17 Mar 16 |
olle |
457 |
json.put("message", t.getMessage()); |
3786 |
17 Mar 16 |
olle |
458 |
json.put("stacktrace", ThrowableUtil.stackTraceToString(t)); |
3786 |
17 Mar 16 |
olle |
459 |
} |
3786 |
17 Mar 16 |
olle |
460 |
finally |
3786 |
17 Mar 16 |
olle |
461 |
{ |
3786 |
17 Mar 16 |
olle |
462 |
if (dc != null) dc.close(); |
5288 |
08 Feb 19 |
nicklas |
463 |
if (sc != null && progressId != null) |
3786 |
17 Mar 16 |
olle |
464 |
{ |
5288 |
08 Feb 19 |
nicklas |
465 |
sc.setSessionSetting(progressId, null); |
3786 |
17 Mar 16 |
olle |
466 |
} |
5288 |
08 Feb 19 |
nicklas |
467 |
json.writeJSONString(resp.getWriter()); |
3786 |
17 Mar 16 |
olle |
468 |
} |
3786 |
17 Mar 16 |
olle |
469 |
} |
3786 |
17 Mar 16 |
olle |
470 |
|
5295 |
12 Feb 19 |
nicklas |
471 |
|
5295 |
12 Feb 19 |
nicklas |
472 |
|
5295 |
12 Feb 19 |
nicklas |
473 |
/** |
5293 |
12 Feb 19 |
nicklas |
Creates an INCA output file in CSV format with columns |
5275 |
31 Jan 19 |
nicklas |
corresponding to INCA annotation types, plus: |
5275 |
31 Jan 19 |
nicklas |
PersonalNo: a column with patient item name for entries that could be mapped to SCAN-B patients items |
5275 |
31 Jan 19 |
nicklas |
PAT_ID: a column with the temporary patient id in the import file |
5293 |
12 Feb 19 |
nicklas |
HasSpecimen: Yes or No if the line was mapped to a case, empty otherwise |
5293 |
12 Feb 19 |
nicklas |
Flag: Reasons for excluding a data line in the import or statistics |
5275 |
31 Jan 19 |
nicklas |
480 |
*/ |
5293 |
12 Feb 19 |
nicklas |
481 |
private void createIncaOutputFile(SessionControl sc, IncaFile incaFile, String reportType, ProgressReporter progress) |
4026 |
25 Jul 16 |
olle |
482 |
{ |
5279 |
05 Feb 19 |
nicklas |
483 |
progress.display(0, "Writing output CSV file..."); |
5293 |
12 Feb 19 |
nicklas |
484 |
String outputFilePath = getReportCacheKey(sc, reportType); |
5275 |
31 Jan 19 |
nicklas |
485 |
TableWriter writer = null; |
5279 |
05 Feb 19 |
nicklas |
486 |
OutputStream out = null; |
5275 |
31 Jan 19 |
nicklas |
487 |
|
5275 |
31 Jan 19 |
nicklas |
// A line is all mapped annotations + 3 extra columns |
5293 |
12 Feb 19 |
nicklas |
489 |
Object[] data = new Object[4+incaFile.mappedHeaders.size()]; |
4026 |
25 Jul 16 |
olle |
490 |
try |
4026 |
25 Jul 16 |
olle |
491 |
{ |
5332 |
20 Mar 19 |
nicklas |
492 |
Cipher c = CryptoUtil.createSessionCipher(sc, Cipher.ENCRYPT_MODE, reportType); |
5295 |
12 Feb 19 |
nicklas |
493 |
out = new CipherOutputStream(Application.getStaticCache().write(outputFilePath, 5), c); |
5295 |
12 Feb 19 |
nicklas |
494 |
|
5279 |
05 Feb 19 |
nicklas |
495 |
writer = new TableWriter(new OutputStreamWriter(out, Charset.forName("UTF-8"))); |
5275 |
31 Jan 19 |
nicklas |
496 |
writer.setDataSeparator("\t"); |
5275 |
31 Jan 19 |
nicklas |
497 |
writer.setNullValue(""); |
5275 |
31 Jan 19 |
nicklas |
498 |
writer.setEncoder(new TabCrLfEncoderDecoder(true)); |
4026 |
25 Jul 16 |
olle |
499 |
|
5275 |
31 Jan 19 |
nicklas |
500 |
data[0] = "PersonalNo"; |
5275 |
31 Jan 19 |
nicklas |
501 |
data[1] = "PAT_ID"; |
5293 |
12 Feb 19 |
nicklas |
502 |
data[2] = "HasSpecimen"; |
5293 |
12 Feb 19 |
nicklas |
503 |
data[3] = "Flag"; |
5293 |
12 Feb 19 |
nicklas |
504 |
int i = 4; |
5275 |
31 Jan 19 |
nicklas |
505 |
for (Header h : incaFile.headers) |
4026 |
25 Jul 16 |
olle |
506 |
{ |
5275 |
31 Jan 19 |
nicklas |
507 |
if (h.annotationType == null) continue; |
5275 |
31 Jan 19 |
nicklas |
508 |
data[i] = h.name; |
5275 |
31 Jan 19 |
nicklas |
509 |
i++; |
4026 |
25 Jul 16 |
olle |
510 |
} |
5275 |
31 Jan 19 |
nicklas |
511 |
writer.tablePrintData(data); |
4026 |
25 Jul 16 |
olle |
512 |
|
5279 |
05 Feb 19 |
nicklas |
513 |
int numLines = incaFile.lines.size(); |
5275 |
31 Jan 19 |
nicklas |
514 |
for (IncaLine line : incaFile.lines) |
4026 |
25 Jul 16 |
olle |
515 |
{ |
5275 |
31 Jan 19 |
nicklas |
516 |
data[0] = line.patientName; |
5275 |
31 Jan 19 |
nicklas |
517 |
data[1] = line.patIdInca; |
5293 |
12 Feb 19 |
nicklas |
518 |
data[2] = line.caseId == 0 ? null : (line.hasSpecimen ? "Yes" : "No"); |
5293 |
12 Feb 19 |
nicklas |
519 |
data[3] = line.exclude; |
5293 |
12 Feb 19 |
nicklas |
520 |
i = 4; |
5275 |
31 Jan 19 |
nicklas |
521 |
for (Header h : incaFile.headers) |
4026 |
25 Jul 16 |
olle |
522 |
{ |
5275 |
31 Jan 19 |
nicklas |
523 |
if (h.annotationType == null) continue; |
5275 |
31 Jan 19 |
nicklas |
524 |
data[i] = line.columns[h.index]; |
5275 |
31 Jan 19 |
nicklas |
525 |
i++; |
4026 |
25 Jul 16 |
olle |
526 |
} |
5275 |
31 Jan 19 |
nicklas |
527 |
writer.tablePrintData(data); |
5279 |
05 Feb 19 |
nicklas |
528 |
if (line.lineNo % 100 == 0) |
5279 |
05 Feb 19 |
nicklas |
529 |
{ |
5279 |
05 Feb 19 |
nicklas |
530 |
progress.display(100 * line.lineNo / numLines, |
5279 |
05 Feb 19 |
nicklas |
531 |
"Writing output CSV file... ("+line.lineNo + " of " + numLines + ")"); |
5279 |
05 Feb 19 |
nicklas |
532 |
} |
4026 |
25 Jul 16 |
olle |
533 |
} |
4026 |
25 Jul 16 |
olle |
534 |
|
4026 |
25 Jul 16 |
olle |
535 |
writer.flush(); |
5279 |
05 Feb 19 |
nicklas |
536 |
out.flush(); |
5279 |
05 Feb 19 |
nicklas |
537 |
progress.display(100, "Output CSV file created with " + numLines + " data lines."); |
4026 |
25 Jul 16 |
olle |
538 |
} |
5295 |
12 Feb 19 |
nicklas |
539 |
catch (Exception ex) |
4026 |
25 Jul 16 |
olle |
540 |
{ |
4026 |
25 Jul 16 |
olle |
541 |
System.out.println(new Date() + " createIncaImportOutputFile(): Could not create writer for outputFilePath = " + outputFilePath + ". Exception e: " + ex); |
4026 |
25 Jul 16 |
olle |
//ex.printStackTrace(); |
4026 |
25 Jul 16 |
olle |
543 |
} |
5279 |
05 Feb 19 |
nicklas |
544 |
finally |
5279 |
05 Feb 19 |
nicklas |
545 |
{ |
5279 |
05 Feb 19 |
nicklas |
546 |
FileUtil.close(writer); |
5279 |
05 Feb 19 |
nicklas |
547 |
FileUtil.close(out); |
5279 |
05 Feb 19 |
nicklas |
548 |
} |
4026 |
25 Jul 16 |
olle |
549 |
} |
4026 |
25 Jul 16 |
olle |
550 |
|
5273 |
31 Jan 19 |
nicklas |
551 |
|
4708 |
19 Mar 18 |
nicklas |
552 |
|
3901 |
29 Apr 16 |
olle |
553 |
/** |
3951 |
18 May 16 |
olle |
* Returns name to be used for report file. |
3951 |
18 May 16 |
olle |
555 |
* |
3952 |
18 May 16 |
olle |
* @param reportType String The type of report to get file name for. |
3951 |
18 May 16 |
olle |
* @return String name to be used for report file. |
3951 |
18 May 16 |
olle |
558 |
*/ |
3951 |
18 May 16 |
olle |
559 |
private String fetchReportFileName(String reportType) |
3951 |
18 May 16 |
olle |
560 |
{ |
3951 |
18 May 16 |
olle |
561 |
String reportFileName = null; |
3951 |
18 May 16 |
olle |
562 |
if (reportType != null) |
3951 |
18 May 16 |
olle |
563 |
{ |
3951 |
18 May 16 |
olle |
564 |
if (reportType.equals(REPORT_TYPE_IMPORT)) |
3951 |
18 May 16 |
olle |
565 |
{ |
3951 |
18 May 16 |
olle |
566 |
reportFileName = INCA_IMPORT_REPORT_FILENAME; |
3951 |
18 May 16 |
olle |
567 |
} |
4026 |
25 Jul 16 |
olle |
568 |
else if (reportType.equals(REPORT_TYPE_IMPORT_OUTPUT_CSV)) |
4026 |
25 Jul 16 |
olle |
569 |
{ |
4026 |
25 Jul 16 |
olle |
570 |
reportFileName = INCA_IMPORT_OUTPUT_CSV_FILENAME; |
4026 |
25 Jul 16 |
olle |
571 |
} |
3963 |
20 May 16 |
olle |
572 |
else if (reportType.equals(REPORT_TYPE_STATISTICS)) |
3963 |
20 May 16 |
olle |
573 |
{ |
3963 |
20 May 16 |
olle |
574 |
reportFileName = INCA_STATISTICS_REPORT_FILENAME; |
3963 |
20 May 16 |
olle |
575 |
} |
3980 |
27 May 16 |
olle |
576 |
else if (reportType.equals(REPORT_TYPE_STATISTICS_CSV)) |
3980 |
27 May 16 |
olle |
577 |
{ |
3980 |
27 May 16 |
olle |
578 |
reportFileName = INCA_STATISTICS_CSV_FILENAME; |
3980 |
27 May 16 |
olle |
579 |
} |
3951 |
18 May 16 |
olle |
580 |
} |
3951 |
18 May 16 |
olle |
581 |
return reportFileName; |
3951 |
18 May 16 |
olle |
582 |
} |
3951 |
18 May 16 |
olle |
583 |
|
5295 |
12 Feb 19 |
nicklas |
584 |
/** |
5295 |
12 Feb 19 |
nicklas |
Get the cache key to use for storing the report. |
5295 |
12 Feb 19 |
nicklas |
The path is a MD5 value calculated from the current session |
5295 |
12 Feb 19 |
nicklas |
and user id. |
5295 |
12 Feb 19 |
nicklas |
588 |
*/ |
5279 |
05 Feb 19 |
nicklas |
589 |
private String getReportCacheKey(SessionControl sc, String reportType) |
5279 |
05 Feb 19 |
nicklas |
590 |
{ |
5332 |
20 Mar 19 |
nicklas |
591 |
return "/reggie/inca/" + MD5.getHashString(sc.getId()+"-"+sc.getLoggedInUserId()) + "/" + fetchReportFileName(reportType); |
5279 |
05 Feb 19 |
nicklas |
592 |
} |
3856 |
18 Apr 16 |
olle |
593 |
|
4032 |
28 Jul 16 |
olle |
594 |
/** |
4032 |
28 Jul 16 |
olle |
* Checks if a report file of right type exists. |
4032 |
28 Jul 16 |
olle |
596 |
* |
4032 |
28 Jul 16 |
olle |
* @param reportType String The type of report to check for. |
4032 |
28 Jul 16 |
olle |
* @return boolean Flag indicating if a report file of right type exists. |
4032 |
28 Jul 16 |
olle |
599 |
*/ |
5294 |
12 Feb 19 |
nicklas |
600 |
boolean checkForReportFile(SessionControl sc, String reportType) |
4032 |
28 Jul 16 |
olle |
601 |
{ |
5294 |
12 Feb 19 |
nicklas |
602 |
String reportFilePath = getReportCacheKey(sc, reportType); |
5294 |
12 Feb 19 |
nicklas |
603 |
return Application.getStaticCache().exists(reportFilePath); |
4032 |
28 Jul 16 |
olle |
604 |
} |
4032 |
28 Jul 16 |
olle |
605 |
|
5279 |
05 Feb 19 |
nicklas |
606 |
private void createIncaImportReportFile(SessionControl sc, IncaFile incaFile, String checkType) |
3856 |
18 Apr 16 |
olle |
607 |
{ |
5279 |
05 Feb 19 |
nicklas |
608 |
String reportFilePath = getReportCacheKey(sc, REPORT_TYPE_IMPORT); |
5275 |
31 Jan 19 |
nicklas |
609 |
Writer report = null; |
5279 |
05 Feb 19 |
nicklas |
610 |
OutputStream out = null; |
5279 |
05 Feb 19 |
nicklas |
611 |
|
3786 |
17 Mar 16 |
olle |
612 |
try |
3786 |
17 Mar 16 |
olle |
613 |
{ |
5332 |
20 Mar 19 |
nicklas |
614 |
Cipher c = CryptoUtil.createSessionCipher(sc, Cipher.ENCRYPT_MODE, REPORT_TYPE_IMPORT); |
5295 |
12 Feb 19 |
nicklas |
615 |
out = new CipherOutputStream(Application.getStaticCache().write(reportFilePath, 5), c); |
5295 |
12 Feb 19 |
nicklas |
616 |
|
5279 |
05 Feb 19 |
nicklas |
617 |
report = new OutputStreamWriter(out, Charset.forName("UTF-8")); |
5293 |
12 Feb 19 |
nicklas |
618 |
TableWriter table = new TableWriter(report); |
5293 |
12 Feb 19 |
nicklas |
619 |
|
5268 |
25 Jan 19 |
nicklas |
620 |
report.write("INCA import report file\n"); |
5268 |
25 Jan 19 |
nicklas |
621 |
report.write("=======================\n"); |
5270 |
29 Jan 19 |
nicklas |
622 |
report.write("INCA data file: " + incaFile.filename + "\n"); |
5270 |
29 Jan 19 |
nicklas |
623 |
report.write("INCA export date: " + Reggie.CONVERTER_DATE_TO_STRING_WITH_SEPARATOR.convert(incaFile.exportDate) + "\n"); |
5287 |
08 Feb 19 |
nicklas |
624 |
report.write("Start time: " + Reggie.CONVERTER_DATETIME_TO_STRING_WITH_SEPARATOR.convert(incaFile.startDate) + "\n"); |
5287 |
08 Feb 19 |
nicklas |
625 |
report.write("End time: " + Reggie.CONVERTER_DATETIME_TO_STRING_WITH_SEPARATOR.convert(incaFile.endDate) + "\n"); |
5270 |
29 Jan 19 |
nicklas |
626 |
if (checkType == null) |
5270 |
29 Jan 19 |
nicklas |
627 |
{ |
5270 |
29 Jan 19 |
nicklas |
628 |
report.write("Mode: Import\n"); |
5270 |
29 Jan 19 |
nicklas |
629 |
report.write("Checked cases: " + incaFile.numCasesChecked + "\n"); |
5270 |
29 Jan 19 |
nicklas |
630 |
report.write("Updated cases: " + incaFile.numCasesUpdated + "\n"); |
5273 |
31 Jan 19 |
nicklas |
631 |
report.write("Added INCA annotations: " + incaFile.numAnnotations[Change.ADDED.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
632 |
report.write("Modified INCA annotations: " + incaFile.numAnnotations[Change.UPDATED.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
633 |
report.write("Removed INCA annotations: " + incaFile.numAnnotations[Change.DELETED.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
634 |
report.write("Unchanged INCA annotations: " + incaFile.numAnnotations[Change.NO_CHANGE.ordinal()] + "\n"); |
5270 |
29 Jan 19 |
nicklas |
635 |
} |
5270 |
29 Jan 19 |
nicklas |
636 |
else |
5270 |
29 Jan 19 |
nicklas |
637 |
{ |
5270 |
29 Jan 19 |
nicklas |
638 |
report.write("Mode: " + checkType + "\n"); |
5270 |
29 Jan 19 |
nicklas |
639 |
} |
5268 |
25 Jan 19 |
nicklas |
640 |
report.write("\n"); |
3786 |
17 Mar 16 |
olle |
641 |
|
5273 |
31 Jan 19 |
nicklas |
642 |
report.write("Header columns summary\n"); |
5273 |
31 Jan 19 |
nicklas |
643 |
report.write("======================\n"); |
5270 |
29 Jan 19 |
nicklas |
644 |
report.write("Header columns: " + incaFile.headers.length + "\n"); |
5270 |
29 Jan 19 |
nicklas |
645 |
report.write("Missing headers: " + incaFile.missingHeaders.size() + "\n"); |
5270 |
29 Jan 19 |
nicklas |
646 |
report.write("Duplicate headers: "+ incaFile.duplicateHeaders.size() + "\n"); |
5270 |
29 Jan 19 |
nicklas |
647 |
report.write("Unknown headers: " + incaFile.unknownHeaders.size() + "\n"); |
5270 |
29 Jan 19 |
nicklas |
648 |
report.write("Unmapped INCA2 annotations: " + incaFile.unmappedHeaders.size() + "\n"); |
5268 |
25 Jan 19 |
nicklas |
649 |
|
5273 |
31 Jan 19 |
nicklas |
650 |
report.write("\n"); |
5273 |
31 Jan 19 |
nicklas |
651 |
report.write("Data lines summary\n"); |
5273 |
31 Jan 19 |
nicklas |
652 |
report.write("==================\n"); |
5273 |
31 Jan 19 |
nicklas |
653 |
report.write("Lines of data: " + incaFile.lines.size() + "\n"); |
5273 |
31 Jan 19 |
nicklas |
654 |
report.write("Lines that can be imported: " + (incaFile.lines.size() - incaFile.totalExcludedLines) + "\n"); |
5273 |
31 Jan 19 |
nicklas |
655 |
report.write("Excluded lines: " + incaFile.totalExcludedLines + "\n"); |
5273 |
31 Jan 19 |
nicklas |
656 |
if (incaFile.totalExcludedLines > 0) |
5273 |
31 Jan 19 |
nicklas |
657 |
{ |
5273 |
31 Jan 19 |
nicklas |
658 |
report.write(" too many columns: " + incaFile.excludedLineCount[ExcludedLine.TOO_MANY_COLUMNS.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
659 |
report.write(" too few columns: " + incaFile.excludedLineCount[ExcludedLine.TOO_FEW_COLUMNS.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
660 |
report.write(" missing personal number: " + incaFile.excludedLineCount[ExcludedLine.MISSING_PERSONAL_NO.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
661 |
report.write(" missing or invalid laterality: " + incaFile.excludedLineCount[ExcludedLine.MISSING_LATERALITY.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
662 |
report.write(" duplicate laterality: " + incaFile.excludedLineCount[ExcludedLine.DUPLICATE_LATERALITY.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
663 |
report.write(" invalid data value: " + incaFile.excludedLineCount[ExcludedLine.INVALID_DATA_VALUE.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
664 |
if (incaFile.isFollowUp) |
5273 |
31 Jan 19 |
nicklas |
665 |
{ |
5273 |
31 Jan 19 |
nicklas |
666 |
report.write(" missing follow-up date: " + incaFile.excludedLineCount[ExcludedLine.MISSING_FOLLOWUP_DATE.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
667 |
report.write(" duplicate follow-up: " + incaFile.excludedLineCount[ExcludedLine.DUPLICATE_FOLLOWUP.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
668 |
} |
5275 |
31 Jan 19 |
nicklas |
669 |
report.write(" patient not found: " + incaFile.excludedLineCount[ExcludedLine.PATIENT_NOTIN_DATABASE.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
670 |
report.write(" case not found: " + incaFile.excludedLineCount[ExcludedLine.CASE_NOTIN_DATABASE.ordinal()] + "\n"); |
5273 |
31 Jan 19 |
nicklas |
671 |
} |
5273 |
31 Jan 19 |
nicklas |
672 |
|
5268 |
25 Jan 19 |
nicklas |
673 |
if (incaFile.missingHeaders.size() > 0) |
5268 |
25 Jan 19 |
nicklas |
674 |
{ |
5268 |
25 Jan 19 |
nicklas |
675 |
report.write("\n"); |
5268 |
25 Jan 19 |
nicklas |
676 |
report.write("Missing headers\n"); |
5268 |
25 Jan 19 |
nicklas |
677 |
report.write("----------------\n"); |
5268 |
25 Jan 19 |
nicklas |
678 |
report.write(Values.getString(incaFile.missingHeaders, "\n", true)); |
5268 |
25 Jan 19 |
nicklas |
679 |
report.write("\n"); |
5268 |
25 Jan 19 |
nicklas |
680 |
} |
5268 |
25 Jan 19 |
nicklas |
681 |
|
5268 |
25 Jan 19 |
nicklas |
682 |
if (incaFile.duplicateHeaders.size() > 0) |
5268 |
25 Jan 19 |
nicklas |
683 |
{ |
5268 |
25 Jan 19 |
nicklas |
684 |
report.write("\n"); |
5268 |
25 Jan 19 |
nicklas |
685 |
report.write("Duplicate headers\n"); |
5268 |
25 Jan 19 |
nicklas |
686 |
report.write("-----------------\n"); |
5268 |
25 Jan 19 |
nicklas |
687 |
report.write(Values.getString(incaFile.duplicateHeaders, "\n", true)); |
5268 |
25 Jan 19 |
nicklas |
688 |
report.write("\n"); |
5268 |
25 Jan 19 |
nicklas |
689 |
} |
5268 |
25 Jan 19 |
nicklas |
690 |
|
5268 |
25 Jan 19 |
nicklas |
691 |
if (incaFile.unknownHeaders.size() > 0) |
5268 |
25 Jan 19 |
nicklas |
692 |
{ |
5268 |
25 Jan 19 |
nicklas |
693 |
report.write("\n"); |
5268 |
25 Jan 19 |
nicklas |
694 |
report.write("Unknown headers\n"); |
5268 |
25 Jan 19 |
nicklas |
695 |
report.write("---------------\n"); |
5293 |
12 Feb 19 |
nicklas |
696 |
table.tablePrintData("Column", "Header"); |
5274 |
31 Jan 19 |
nicklas |
697 |
for (Header h : incaFile.headers) |
5274 |
31 Jan 19 |
nicklas |
698 |
{ |
5274 |
31 Jan 19 |
nicklas |
699 |
if (h.annotationType == null && h.keyColumn == null) |
5274 |
31 Jan 19 |
nicklas |
700 |
{ |
5466 |
04 Jun 19 |
nicklas |
701 |
table.tablePrintData(h.colNo, h.name); |
5274 |
31 Jan 19 |
nicklas |
702 |
} |
5274 |
31 Jan 19 |
nicklas |
703 |
} |
5274 |
31 Jan 19 |
nicklas |
704 |
} |
5274 |
31 Jan 19 |
nicklas |
705 |
|
5274 |
31 Jan 19 |
nicklas |
706 |
if (incaFile.mappedHeaders.size() > 0) |
5274 |
31 Jan 19 |
nicklas |
707 |
{ |
5268 |
25 Jan 19 |
nicklas |
708 |
report.write("\n"); |
5274 |
31 Jan 19 |
nicklas |
709 |
report.write("Mapped INCA annotations\n"); |
5274 |
31 Jan 19 |
nicklas |
710 |
report.write("-----------------------\n"); |
5293 |
12 Feb 19 |
nicklas |
711 |
table.tablePrintData("Column", "Header", "Annotation"); |
5274 |
31 Jan 19 |
nicklas |
712 |
for (Header h : incaFile.headers) |
5274 |
31 Jan 19 |
nicklas |
713 |
{ |
5274 |
31 Jan 19 |
nicklas |
714 |
if (h.annotationType != null) |
5274 |
31 Jan 19 |
nicklas |
715 |
{ |
5466 |
04 Jun 19 |
nicklas |
716 |
table.tablePrintData(h.colNo, h.name, h.annotationType.getName()); |
5274 |
31 Jan 19 |
nicklas |
717 |
} |
5274 |
31 Jan 19 |
nicklas |
718 |
} |
5268 |
25 Jan 19 |
nicklas |
719 |
} |
5268 |
25 Jan 19 |
nicklas |
720 |
|
5268 |
25 Jan 19 |
nicklas |
721 |
if (incaFile.unmappedHeaders.size() > 0) |
5268 |
25 Jan 19 |
nicklas |
722 |
{ |
5268 |
25 Jan 19 |
nicklas |
723 |
report.write("\n"); |
5274 |
31 Jan 19 |
nicklas |
724 |
report.write("Unmapped INCA annotations\n"); |
5274 |
31 Jan 19 |
nicklas |
725 |
report.write("-------------------------\n"); |
5268 |
25 Jan 19 |
nicklas |
726 |
report.write(Values.getString(incaFile.unmappedHeaders, "\n", true)); |
5268 |
25 Jan 19 |
nicklas |
727 |
report.write("\n"); |
5268 |
25 Jan 19 |
nicklas |
728 |
} |
5273 |
31 Jan 19 |
nicklas |
729 |
|
5268 |
25 Jan 19 |
nicklas |
730 |
if (incaFile.excludedLineCount[ExcludedLine.TOO_MANY_COLUMNS.ordinal()] > 0 || incaFile.excludedLineCount[ExcludedLine.TOO_FEW_COLUMNS.ordinal()] > 0) |
5268 |
25 Jan 19 |
nicklas |
731 |
{ |
5273 |
31 Jan 19 |
nicklas |
732 |
report.write("\n"); |
5466 |
04 Jun 19 |
nicklas |
733 |
report.write("Lines with incorrect number of columns (expected " +incaFile.headers.length + ")\n"); |
5466 |
04 Jun 19 |
nicklas |
734 |
report.write("-----------------------------------------------------\n"); |
5293 |
12 Feb 19 |
nicklas |
735 |
table.tablePrintData("LineNo", "Columns"); |
5273 |
31 Jan 19 |
nicklas |
736 |
for (IncaLine line : incaFile.lines) |
5268 |
25 Jan 19 |
nicklas |
737 |
{ |
5268 |
25 Jan 19 |
nicklas |
738 |
if (line.exclude == ExcludedLine.TOO_MANY_COLUMNS || line.exclude == ExcludedLine.TOO_FEW_COLUMNS) |
5268 |
25 Jan 19 |
nicklas |
739 |
{ |
5293 |
12 Feb 19 |
nicklas |
740 |
table.tablePrintData(line.lineNo, line.columns.length); |
5268 |
25 Jan 19 |
nicklas |
741 |
} |
5268 |
25 Jan 19 |
nicklas |
742 |
} |
5268 |
25 Jan 19 |
nicklas |
743 |
report.write("\n"); |
5268 |
25 Jan 19 |
nicklas |
744 |
} |
5268 |
25 Jan 19 |
nicklas |
745 |
|
5314 |
28 Feb 19 |
nicklas |
// Columns are included in some reports (if they exists) |
5314 |
28 Feb 19 |
nicklas |
747 |
int diagDateCol = incaFile.keyColumns[KeyColumn.A_DIAG_DAT.ordinal()]; |
5314 |
28 Feb 19 |
nicklas |
748 |
int inrSjhKodCol = incaFile.keyColumns[KeyColumn.A_INR_SJHKOD.ordinal()]; |
6134 |
16 Feb 21 |
nicklas |
749 |
int diagBesDateCol = incaFile.keyColumns[KeyColumn.A_DIAG_BESDAT.ordinal()]; |
6134 |
16 Feb 21 |
nicklas |
750 |
int aPadPrepNrCol = incaFile.keyColumns[KeyColumn.A_PAD_PREPNR.ordinal()]; |
6134 |
16 Feb 21 |
nicklas |
751 |
int aPadPrepArCol = incaFile.keyColumns[KeyColumn.A_PAD_PREPAR.ordinal()]; |
6134 |
16 Feb 21 |
nicklas |
752 |
int opPadPrepNrCol = incaFile.keyColumns[KeyColumn.OP_PAD_PREPNR.ordinal()]; |
6134 |
16 Feb 21 |
nicklas |
753 |
int opPadPrepArCol = incaFile.keyColumns[KeyColumn.OP_PAD_PREPAR.ordinal()]; |
5314 |
28 Feb 19 |
nicklas |
754 |
|
5268 |
25 Jan 19 |
nicklas |
// Report lines that has a "personal number" but missing, invalid or duplicate laterality |
5273 |
31 Jan 19 |
nicklas |
756 |
int sumExcludedLines = incaFile.sumExcludedLines(ExcludedLine.MISSING_LATERALITY, ExcludedLine.DUPLICATE_LATERALITY); |
5273 |
31 Jan 19 |
nicklas |
757 |
if (sumExcludedLines > 0) |
5268 |
25 Jan 19 |
nicklas |
758 |
{ |
5273 |
31 Jan 19 |
nicklas |
759 |
report.write("\n"); |
5268 |
25 Jan 19 |
nicklas |
760 |
report.write("Lines with personal number but missing, invalid or duplicate laterality\n"); |
5314 |
28 Feb 19 |
nicklas |
761 |
if (diagDateCol == -1) report.write("Warning: '"+KeyColumn.A_DIAG_DAT.key +"' was not found in the file and will not be reported."); |
5314 |
28 Feb 19 |
nicklas |
762 |
if (inrSjhKodCol == -1) report.write("Warning: '"+KeyColumn.A_INR_SJHKOD.key +"' was not found in the file and will not be reported."); |
6134 |
16 Feb 21 |
nicklas |
763 |
if (diagBesDateCol == -1) report.write("Warning: '"+KeyColumn.A_DIAG_BESDAT.key +"' was not found in the file and will not be reported."); |
6134 |
16 Feb 21 |
nicklas |
764 |
if (aPadPrepNrCol == -1) report.write("Warning: '"+KeyColumn.A_PAD_PREPNR.key +"' was not found in the file and will not be reported."); |
6134 |
16 Feb 21 |
nicklas |
765 |
if (opPadPrepNrCol == -1) report.write("Warning: '"+KeyColumn.OP_PAD_PREPNR.key +"' was not found in the file and will not be reported."); |
5268 |
25 Jan 19 |
nicklas |
766 |
report.write("-----------------------------------------------------------------------\n"); |
6134 |
16 Feb 21 |
nicklas |
767 |
table.tablePrintData("LineNo", "PersonalNo", "Laterality", |
6134 |
16 Feb 21 |
nicklas |
768 |
KeyColumn.A_DIAG_DAT.key, KeyColumn.A_DIAG_BESDAT.key, KeyColumn.A_INR_SJHKOD.key, |
6134 |
16 Feb 21 |
nicklas |
769 |
KeyColumn.A_PAD_PREPNR.key, KeyColumn.A_PAD_PREPAR.key, |
6134 |
16 Feb 21 |
nicklas |
770 |
KeyColumn.OP_PAD_PREPNR.key, KeyColumn.OP_PAD_PREPAR.key, |
6134 |
16 Feb 21 |
nicklas |
771 |
"Flag"); |
5273 |
31 Jan 19 |
nicklas |
772 |
for (IncaLine line : incaFile.lines) |
5268 |
25 Jan 19 |
nicklas |
773 |
{ |
6134 |
16 Feb 21 |
nicklas |
774 |
String flag = null; |
6134 |
16 Feb 21 |
nicklas |
775 |
String laterality = null; |
5268 |
25 Jan 19 |
nicklas |
776 |
if (line.exclude == ExcludedLine.MISSING_LATERALITY) |
5268 |
25 Jan 19 |
nicklas |
777 |
{ |
6134 |
16 Feb 21 |
nicklas |
778 |
laterality = line.col(incaFile.keyColumns[KeyColumn.LATERALITY.ordinal()]); |
6134 |
16 Feb 21 |
nicklas |
779 |
flag = laterality == null ? "MISSING_LATERALITY" : "INVALID_LATERALITY"; |
5268 |
25 Jan 19 |
nicklas |
780 |
} |
5268 |
25 Jan 19 |
nicklas |
781 |
else if (line.exclude == ExcludedLine.DUPLICATE_LATERALITY) |
5268 |
25 Jan 19 |
nicklas |
782 |
{ |
6134 |
16 Feb 21 |
nicklas |
783 |
flag = "DUPLICATE_LATERALITY"; |
6134 |
16 Feb 21 |
nicklas |
784 |
laterality = line.laterality; |
5268 |
25 Jan 19 |
nicklas |
785 |
} |
6134 |
16 Feb 21 |
nicklas |
786 |
if (flag != null) |
6134 |
16 Feb 21 |
nicklas |
787 |
{ |
6134 |
16 Feb 21 |
nicklas |
788 |
table.tablePrintData(line.lineNo, line.personalNo, laterality, |
6134 |
16 Feb 21 |
nicklas |
789 |
line.col(diagDateCol), line.col(diagBesDateCol), line.col(inrSjhKodCol), |
6134 |
16 Feb 21 |
nicklas |
790 |
line.col(aPadPrepNrCol), line.col(aPadPrepArCol), line.col(opPadPrepNrCol), line.col(opPadPrepArCol), |
6134 |
16 Feb 21 |
nicklas |
791 |
flag); |
6134 |
16 Feb 21 |
nicklas |
792 |
} |
5268 |
25 Jan 19 |
nicklas |
793 |
} |
5268 |
25 Jan 19 |
nicklas |
794 |
report.write("\n"); |
5268 |
25 Jan 19 |
nicklas |
795 |
} |
5268 |
25 Jan 19 |
nicklas |
796 |
|
5268 |
25 Jan 19 |
nicklas |
// Report lines that has invalid data values |
5268 |
25 Jan 19 |
nicklas |
798 |
if (incaFile.excludedLineCount[ExcludedLine.INVALID_DATA_VALUE.ordinal()] > 0) |
5268 |
25 Jan 19 |
nicklas |
799 |
{ |
5273 |
31 Jan 19 |
nicklas |
800 |
report.write("\n"); |
5279 |
05 Feb 19 |
nicklas |
801 |
report.write("Lines with personal number and invalid data values\n"); |
5279 |
05 Feb 19 |
nicklas |
802 |
report.write("--------------------------------------------------\n"); |
5293 |
12 Feb 19 |
nicklas |
803 |
table.tablePrintData("LineNo", "PersonalNo", "Laterality", "Column", "Value", "Expected type", "Comment"); |
5273 |
31 Jan 19 |
nicklas |
804 |
for (IncaLine line : incaFile.lines) |
5268 |
25 Jan 19 |
nicklas |
805 |
{ |
5268 |
25 Jan 19 |
nicklas |
806 |
if (line.exclude == ExcludedLine.INVALID_DATA_VALUE) |
5268 |
25 Jan 19 |
nicklas |
807 |
{ |
5269 |
28 Jan 19 |
nicklas |
808 |
for (int colNo = 0; colNo < incaFile.headers.length; colNo++) |
5268 |
25 Jan 19 |
nicklas |
809 |
{ |
5268 |
25 Jan 19 |
nicklas |
810 |
Object d = line.data[colNo]; |
5268 |
25 Jan 19 |
nicklas |
811 |
if (d instanceof Exception) |
5268 |
25 Jan 19 |
nicklas |
812 |
{ |
5268 |
25 Jan 19 |
nicklas |
813 |
Exception ex = (Exception)d; |
5269 |
28 Jan 19 |
nicklas |
814 |
Header h = incaFile.headers[colNo]; |
5293 |
12 Feb 19 |
nicklas |
815 |
table.tablePrintData(line.lineNo, line.personalNo, line.laterality, |
5293 |
12 Feb 19 |
nicklas |
816 |
h.name, line.columns[colNo], h.annotationType.getValueType().name(), |
5293 |
12 Feb 19 |
nicklas |
817 |
ex.getMessage()); |
5268 |
25 Jan 19 |
nicklas |
818 |
} |
5268 |
25 Jan 19 |
nicklas |
819 |
} |
5268 |
25 Jan 19 |
nicklas |
820 |
} |
5268 |
25 Jan 19 |
nicklas |
821 |
} |
5268 |
25 Jan 19 |
nicklas |
822 |
report.write("\n"); |
5268 |
25 Jan 19 |
nicklas |
823 |
} |
5268 |
25 Jan 19 |
nicklas |
824 |
|
5273 |
31 Jan 19 |
nicklas |
// Report lines that has a "personal number" but no mapped patient or case in the database |
5275 |
31 Jan 19 |
nicklas |
826 |
sumExcludedLines = incaFile.sumExcludedLines(ExcludedLine.PATIENT_NOTIN_DATABASE, ExcludedLine.CASE_NOTIN_DATABASE); |
5273 |
31 Jan 19 |
nicklas |
827 |
if (sumExcludedLines > 0) |
5273 |
31 Jan 19 |
nicklas |
828 |
{ |
5273 |
31 Jan 19 |
nicklas |
829 |
report.write("\n"); |
5273 |
31 Jan 19 |
nicklas |
830 |
report.write("Lines with personal number but no mapped patient or case in the database\n"); |
5314 |
28 Feb 19 |
nicklas |
831 |
if (diagDateCol == -1) report.write("Warning: '"+KeyColumn.A_DIAG_DAT.key +"' was not found in the file and will not be reported."); |
5314 |
28 Feb 19 |
nicklas |
832 |
if (inrSjhKodCol == -1) report.write("Warning: '"+KeyColumn.A_INR_SJHKOD.key +"' was not found in the file and will not be reported."); |
6134 |
16 Feb 21 |
nicklas |
833 |
if (diagBesDateCol == -1) report.write("Warning: '"+KeyColumn.A_DIAG_BESDAT.key +"' was not found in the file and will not be reported."); |
6134 |
16 Feb 21 |
nicklas |
834 |
if (aPadPrepNrCol == -1) report.write("Warning: '"+KeyColumn.A_PAD_PREPNR.key +"' was not found in the file and will not be reported."); |
6134 |
16 Feb 21 |
nicklas |
835 |
if (opPadPrepNrCol == -1) report.write("Warning: '"+KeyColumn.OP_PAD_PREPNR.key +"' was not found in the file and will not be reported."); |
5273 |
31 Jan 19 |
nicklas |
836 |
report.write("------------------------------------------------------------------------\n"); |
6134 |
16 Feb 21 |
nicklas |
837 |
table.tablePrintData("LineNo", "PersonalNo", "Laterality", |
6134 |
16 Feb 21 |
nicklas |
838 |
KeyColumn.A_DIAG_DAT.key, KeyColumn.A_DIAG_BESDAT.key, KeyColumn.A_INR_SJHKOD.key, |
6134 |
16 Feb 21 |
nicklas |
839 |
KeyColumn.A_PAD_PREPNR.key, KeyColumn.A_PAD_PREPAR.key, |
6134 |
16 Feb 21 |
nicklas |
840 |
KeyColumn.OP_PAD_PREPNR.key, KeyColumn.OP_PAD_PREPAR.key, |
6134 |
16 Feb 21 |
nicklas |
841 |
"Flag", "Unmapped items", "Mapped items"); |
5273 |
31 Jan 19 |
nicklas |
842 |
for (IncaLine line : incaFile.lines) |
5273 |
31 Jan 19 |
nicklas |
843 |
{ |
5275 |
31 Jan 19 |
nicklas |
844 |
if (line.exclude == ExcludedLine.PATIENT_NOTIN_DATABASE) |
5273 |
31 Jan 19 |
nicklas |
845 |
{ |
6134 |
16 Feb 21 |
nicklas |
846 |
table.tablePrintData(line.lineNo, line.personalNo, line.laterality, |
6134 |
16 Feb 21 |
nicklas |
847 |
line.col(diagDateCol), line.col(diagBesDateCol), line.col(inrSjhKodCol), |
6134 |
16 Feb 21 |
nicklas |
848 |
line.col(aPadPrepNrCol), line.col(aPadPrepArCol), line.col(opPadPrepNrCol), line.col(opPadPrepArCol), |
6134 |
16 Feb 21 |
nicklas |
849 |
"PATIENT_NOTIN_DATABASE"); |
5273 |
31 Jan 19 |
nicklas |
850 |
} |
5273 |
31 Jan 19 |
nicklas |
851 |
else if (line.exclude == ExcludedLine.CASE_NOTIN_DATABASE) |
5273 |
31 Jan 19 |
nicklas |
852 |
{ |
5279 |
05 Feb 19 |
nicklas |
853 |
String otherMapped = "not found"; |
5279 |
05 Feb 19 |
nicklas |
854 |
if (line.otherCase != null && line.otherCase.caseId != 0) |
5279 |
05 Feb 19 |
nicklas |
855 |
{ |
5279 |
05 Feb 19 |
nicklas |
856 |
otherMapped = line.otherCase.caseName + " (" + line.otherCase.laterality + "; Line " + line.otherCase.lineNo + ")"; |
5279 |
05 Feb 19 |
nicklas |
857 |
} |
5279 |
05 Feb 19 |
nicklas |
858 |
String unmapped = "not found"; |
5279 |
05 Feb 19 |
nicklas |
859 |
if (line.unmappedItems != null && line.unmappedItems.size() > 0) |
5279 |
05 Feb 19 |
nicklas |
860 |
{ |
5279 |
05 Feb 19 |
nicklas |
861 |
unmapped = Values.getString(line.unmappedItems, ", ", true); |
5279 |
05 Feb 19 |
nicklas |
862 |
} |
6134 |
16 Feb 21 |
nicklas |
863 |
table.tablePrintData(line.lineNo, line.personalNo, line.laterality, |
6134 |
16 Feb 21 |
nicklas |
864 |
line.col(diagDateCol), line.col(diagBesDateCol), line.col(inrSjhKodCol), |
6134 |
16 Feb 21 |
nicklas |
865 |
line.col(aPadPrepNrCol), line.col(aPadPrepArCol), line.col(opPadPrepNrCol), line.col(opPadPrepArCol), |
6134 |
16 Feb 21 |
nicklas |
866 |
"CASE_NOTIN_DATABASE", unmapped, otherMapped); |
5273 |
31 Jan 19 |
nicklas |
867 |
} |
5273 |
31 Jan 19 |
nicklas |
868 |
} |
5273 |
31 Jan 19 |
nicklas |
869 |
report.write("\n"); |
5273 |
31 Jan 19 |
nicklas |
870 |
} |
5273 |
31 Jan 19 |
nicklas |
871 |
|
5268 |
25 Jan 19 |
nicklas |
872 |
report.flush(); |
5295 |
12 Feb 19 |
nicklas |
873 |
out.flush(); |
3786 |
17 Mar 16 |
olle |
874 |
} |
5295 |
12 Feb 19 |
nicklas |
875 |
catch (Exception ex) |
3786 |
17 Mar 16 |
olle |
876 |
{ |
3856 |
18 Apr 16 |
olle |
877 |
System.out.println(new Date() + " IncaServlet::doPost(): Could not create filewriter for reportFilePath = " + reportFilePath); |
3786 |
17 Mar 16 |
olle |
878 |
} |
5268 |
25 Jan 19 |
nicklas |
879 |
finally |
5268 |
25 Jan 19 |
nicklas |
880 |
{ |
5268 |
25 Jan 19 |
nicklas |
881 |
FileUtil.close(report); |
5279 |
05 Feb 19 |
nicklas |
882 |
FileUtil.close(out); |
5268 |
25 Jan 19 |
nicklas |
883 |
} |
3786 |
17 Mar 16 |
olle |
884 |
} |
3786 |
17 Mar 16 |
olle |
885 |
|
3963 |
20 May 16 |
olle |
886 |
|
5290 |
11 Feb 19 |
nicklas |
887 |
private void createIncaStatisticsReportFile(SessionControl sc, IncaFile incaFile, String checkType, DateFilter dateFilter, ColumnValueFilter cancerTypeFilter) |
3963 |
20 May 16 |
olle |
888 |
{ |
5289 |
11 Feb 19 |
nicklas |
889 |
String reportFilePath = getReportCacheKey(sc, REPORT_TYPE_STATISTICS); |
5289 |
11 Feb 19 |
nicklas |
890 |
Writer report = null; |
5289 |
11 Feb 19 |
nicklas |
891 |
OutputStream out = null; |
5289 |
11 Feb 19 |
nicklas |
892 |
|
5289 |
11 Feb 19 |
nicklas |
893 |
try |
5289 |
11 Feb 19 |
nicklas |
894 |
{ |
5332 |
20 Mar 19 |
nicklas |
895 |
Cipher c = CryptoUtil.createSessionCipher(sc, Cipher.ENCRYPT_MODE, REPORT_TYPE_STATISTICS); |
5295 |
12 Feb 19 |
nicklas |
896 |
out = new CipherOutputStream(Application.getStaticCache().write(reportFilePath, 5), c); |
5295 |
12 Feb 19 |
nicklas |
897 |
|
5289 |
11 Feb 19 |
nicklas |
898 |
report = new OutputStreamWriter(out, Charset.forName("UTF-8")); |
5293 |
12 Feb 19 |
nicklas |
899 |
TableWriter table = new TableWriter(report); |
5289 |
11 Feb 19 |
nicklas |
900 |
|
5289 |
11 Feb 19 |
nicklas |
901 |
report.write("INCA statistics report file\n"); |
5289 |
11 Feb 19 |
nicklas |
902 |
report.write("===========================\n"); |
5289 |
11 Feb 19 |
nicklas |
903 |
report.write("INCA data file: " + incaFile.filename + "\n"); |
5289 |
11 Feb 19 |
nicklas |
904 |
report.write("Start time: " + Reggie.CONVERTER_DATETIME_TO_STRING_WITH_SEPARATOR.convert(incaFile.startDate) + "\n"); |
5289 |
11 Feb 19 |
nicklas |
905 |
report.write("End time: " + Reggie.CONVERTER_DATETIME_TO_STRING_WITH_SEPARATOR.convert(incaFile.endDate) + "\n"); |
5290 |
11 Feb 19 |
nicklas |
906 |
|
5292 |
12 Feb 19 |
nicklas |
907 |
boolean simpleMode = "simplestatistics".equals(checkType); |
5292 |
12 Feb 19 |
nicklas |
908 |
report.write("Mode: " + checkType + "\n"); |
5290 |
11 Feb 19 |
nicklas |
909 |
if (dateFilter != null) |
5290 |
11 Feb 19 |
nicklas |
910 |
{ |
5290 |
11 Feb 19 |
nicklas |
911 |
report.write("Date filter: " + incaFile.headers[dateFilter.column].name+"\n"); |
5290 |
11 Feb 19 |
nicklas |
912 |
report.write("Start date: " + Reggie.CONVERTER_DATE_TO_STRING_WITH_SEPARATOR.convert(dateFilter.start)+"\n"); |
5290 |
11 Feb 19 |
nicklas |
913 |
report.write("End date: " + Reggie.CONVERTER_DATE_TO_STRING_WITH_SEPARATOR.convert(dateFilter.end)+"\n"); |
5290 |
11 Feb 19 |
nicklas |
914 |
} |
5290 |
11 Feb 19 |
nicklas |
915 |
if (cancerTypeFilter != null) |
5290 |
11 Feb 19 |
nicklas |
916 |
{ |
5290 |
11 Feb 19 |
nicklas |
917 |
report.write("Cancer type filter: " + (cancerTypeFilter.value.equals(1) ? "invasive" : "insitu")+"\n"); |
5290 |
11 Feb 19 |
nicklas |
918 |
} |
5289 |
11 Feb 19 |
nicklas |
919 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
920 |
|
5292 |
12 Feb 19 |
nicklas |
921 |
|
5289 |
11 Feb 19 |
nicklas |
922 |
report.write("Header columns summary\n"); |
5289 |
11 Feb 19 |
nicklas |
923 |
report.write("======================\n"); |
5289 |
11 Feb 19 |
nicklas |
924 |
report.write("Header columns: " + incaFile.headers.length + "\n"); |
5289 |
11 Feb 19 |
nicklas |
925 |
report.write("Missing headers: " + incaFile.missingHeaders.size() + "\n"); |
5289 |
11 Feb 19 |
nicklas |
926 |
report.write("Missing INCA annotations: " + incaFile.missingAnnotationTypes.size() + "\n"); |
5289 |
11 Feb 19 |
nicklas |
927 |
report.write("Duplicate headers: "+ incaFile.duplicateHeaders.size() + "\n"); |
5289 |
11 Feb 19 |
nicklas |
928 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
929 |
|
5289 |
11 Feb 19 |
nicklas |
930 |
report.write("Data lines summary\n"); |
5289 |
11 Feb 19 |
nicklas |
931 |
report.write("==================\n"); |
5289 |
11 Feb 19 |
nicklas |
932 |
report.write("Lines of data: " + incaFile.lines.size() + "\n"); |
5289 |
11 Feb 19 |
nicklas |
933 |
report.write("Lines that can be imported: " + (incaFile.lines.size() - incaFile.totalExcludedLines) + "\n"); |
5289 |
11 Feb 19 |
nicklas |
934 |
report.write("Excluded lines: " + incaFile.totalExcludedLines + "\n"); |
5289 |
11 Feb 19 |
nicklas |
935 |
if (incaFile.totalExcludedLines > 0) |
5289 |
11 Feb 19 |
nicklas |
936 |
{ |
5289 |
11 Feb 19 |
nicklas |
937 |
report.write(" too many columns: " + incaFile.excludedLineCount[ExcludedLine.TOO_MANY_COLUMNS.ordinal()] + "\n"); |
5289 |
11 Feb 19 |
nicklas |
938 |
report.write(" too few columns: " + incaFile.excludedLineCount[ExcludedLine.TOO_FEW_COLUMNS.ordinal()] + "\n"); |
5289 |
11 Feb 19 |
nicklas |
939 |
report.write(" missing or invalid laterality: " + incaFile.excludedLineCount[ExcludedLine.MISSING_LATERALITY.ordinal()] + "\n"); |
5289 |
11 Feb 19 |
nicklas |
940 |
report.write(" duplicate laterality: " + incaFile.excludedLineCount[ExcludedLine.DUPLICATE_LATERALITY.ordinal()] + "\n"); |
5289 |
11 Feb 19 |
nicklas |
941 |
report.write(" invalid data value: " + incaFile.excludedLineCount[ExcludedLine.INVALID_DATA_VALUE.ordinal()] + "\n"); |
5289 |
11 Feb 19 |
nicklas |
942 |
report.write(" filtered by date: " + incaFile.excludedLineCount[ExcludedLine.FILTERED_BY_DATE.ordinal()] + "\n"); |
5289 |
11 Feb 19 |
nicklas |
943 |
report.write(" filtered by cancer type: " + incaFile.excludedLineCount[ExcludedLine.FILTERED_BY_CANCER_TYPE.ordinal()] + "\n"); |
5289 |
11 Feb 19 |
nicklas |
944 |
} |
5290 |
11 Feb 19 |
nicklas |
945 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
946 |
|
5290 |
11 Feb 19 |
nicklas |
947 |
report.write("Statistics summary\n"); |
5290 |
11 Feb 19 |
nicklas |
948 |
report.write("==================\n"); |
5292 |
12 Feb 19 |
nicklas |
949 |
if (simpleMode) |
5292 |
12 Feb 19 |
nicklas |
950 |
{ |
5293 |
12 Feb 19 |
nicklas |
951 |
table.tablePrintData("Variable", "Value", "Total", "Accrued", "NotAccrued"); |
5292 |
12 Feb 19 |
nicklas |
952 |
} |
5292 |
12 Feb 19 |
nicklas |
953 |
else |
5292 |
12 Feb 19 |
nicklas |
954 |
{ |
5293 |
12 Feb 19 |
nicklas |
955 |
table.tablePrintData("Variable", "Value", "Total", "AccruedSpecimen", "AccruedNoSpecimen", "NotAccrued"); |
5292 |
12 Feb 19 |
nicklas |
956 |
} |
5290 |
11 Feb 19 |
nicklas |
957 |
for (StatisticalVariable var : incaFile.statistics) |
5290 |
11 Feb 19 |
nicklas |
958 |
{ |
5290 |
11 Feb 19 |
nicklas |
959 |
for (StatisticalGroup group : var.groups) |
5290 |
11 Feb 19 |
nicklas |
960 |
{ |
5292 |
12 Feb 19 |
nicklas |
961 |
if (simpleMode) |
5292 |
12 Feb 19 |
nicklas |
962 |
{ |
5293 |
12 Feb 19 |
nicklas |
963 |
table.tablePrintData(var.name, group.name, group.numTotal, group.numAccruedNoSpecimen, group.numNotAccrued); |
5292 |
12 Feb 19 |
nicklas |
964 |
} |
5292 |
12 Feb 19 |
nicklas |
965 |
else |
5292 |
12 Feb 19 |
nicklas |
966 |
{ |
5293 |
12 Feb 19 |
nicklas |
967 |
table.tablePrintData(var.name, group.name, group.numTotal, group.numAccruedSpecimen, group.numAccruedNoSpecimen, group.numNotAccrued); |
5292 |
12 Feb 19 |
nicklas |
968 |
} |
5290 |
11 Feb 19 |
nicklas |
969 |
} |
5290 |
11 Feb 19 |
nicklas |
970 |
report.write("\n"); |
5290 |
11 Feb 19 |
nicklas |
971 |
} |
5290 |
11 Feb 19 |
nicklas |
972 |
report.write("\n"); |
5290 |
11 Feb 19 |
nicklas |
973 |
|
5289 |
11 Feb 19 |
nicklas |
974 |
if (incaFile.missingHeaders.size() > 0) |
5289 |
11 Feb 19 |
nicklas |
975 |
{ |
5289 |
11 Feb 19 |
nicklas |
976 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
977 |
report.write("Missing headers\n"); |
5290 |
11 Feb 19 |
nicklas |
978 |
report.write("---------------\n"); |
5289 |
11 Feb 19 |
nicklas |
979 |
report.write(Values.getString(incaFile.missingHeaders, "\n", true)); |
5289 |
11 Feb 19 |
nicklas |
980 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
981 |
} |
5289 |
11 Feb 19 |
nicklas |
982 |
|
5289 |
11 Feb 19 |
nicklas |
983 |
if (incaFile.duplicateHeaders.size() > 0) |
5289 |
11 Feb 19 |
nicklas |
984 |
{ |
5289 |
11 Feb 19 |
nicklas |
985 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
986 |
report.write("Duplicate headers\n"); |
5289 |
11 Feb 19 |
nicklas |
987 |
report.write("-----------------\n"); |
5289 |
11 Feb 19 |
nicklas |
988 |
report.write(Values.getString(incaFile.duplicateHeaders, "\n", true)); |
5289 |
11 Feb 19 |
nicklas |
989 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
990 |
} |
5289 |
11 Feb 19 |
nicklas |
991 |
|
5289 |
11 Feb 19 |
nicklas |
992 |
if (incaFile.missingAnnotationTypes.size() > 0) |
5289 |
11 Feb 19 |
nicklas |
993 |
{ |
5289 |
11 Feb 19 |
nicklas |
994 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
995 |
report.write("Missing INCA annotation types\n"); |
5289 |
11 Feb 19 |
nicklas |
996 |
report.write("-----------------------------\n"); |
5289 |
11 Feb 19 |
nicklas |
997 |
report.write(Values.getString(incaFile.missingAnnotationTypes, "\n", true)); |
5289 |
11 Feb 19 |
nicklas |
998 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
999 |
} |
5289 |
11 Feb 19 |
nicklas |
1000 |
|
5289 |
11 Feb 19 |
nicklas |
1001 |
if (incaFile.mappedHeaders.size() > 0) |
5289 |
11 Feb 19 |
nicklas |
1002 |
{ |
5289 |
11 Feb 19 |
nicklas |
1003 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
1004 |
report.write("Mapped INCA annotations\n"); |
5289 |
11 Feb 19 |
nicklas |
1005 |
report.write("-----------------------\n"); |
5293 |
12 Feb 19 |
nicklas |
1006 |
table.tablePrintData("Column", "Header", "Annotation"); |
5289 |
11 Feb 19 |
nicklas |
1007 |
for (Header h : incaFile.headers) |
5289 |
11 Feb 19 |
nicklas |
1008 |
{ |
5289 |
11 Feb 19 |
nicklas |
1009 |
if (h.annotationType != null) |
5289 |
11 Feb 19 |
nicklas |
1010 |
{ |
5466 |
04 Jun 19 |
nicklas |
1011 |
table.tablePrintData(h.colNo, h.name, h.annotationType.getName()); |
5289 |
11 Feb 19 |
nicklas |
1012 |
} |
5289 |
11 Feb 19 |
nicklas |
1013 |
} |
5289 |
11 Feb 19 |
nicklas |
1014 |
} |
5289 |
11 Feb 19 |
nicklas |
1015 |
|
5289 |
11 Feb 19 |
nicklas |
1016 |
if (incaFile.excludedLineCount[ExcludedLine.TOO_MANY_COLUMNS.ordinal()] > 0 || incaFile.excludedLineCount[ExcludedLine.TOO_FEW_COLUMNS.ordinal()] > 0) |
5289 |
11 Feb 19 |
nicklas |
1017 |
{ |
5289 |
11 Feb 19 |
nicklas |
1018 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
1019 |
report.write("Lines with incorrect number of columns\n"); |
5289 |
11 Feb 19 |
nicklas |
1020 |
report.write("--------------------------------------\n"); |
5293 |
12 Feb 19 |
nicklas |
1021 |
table.tablePrintData("LineNo", "Columns"); |
5289 |
11 Feb 19 |
nicklas |
1022 |
for (IncaLine line : incaFile.lines) |
5289 |
11 Feb 19 |
nicklas |
1023 |
{ |
5289 |
11 Feb 19 |
nicklas |
1024 |
if (line.exclude == ExcludedLine.TOO_MANY_COLUMNS || line.exclude == ExcludedLine.TOO_FEW_COLUMNS) |
5289 |
11 Feb 19 |
nicklas |
1025 |
{ |
5293 |
12 Feb 19 |
nicklas |
1026 |
table.tablePrintData(line.lineNo, line.columns.length); |
5289 |
11 Feb 19 |
nicklas |
1027 |
} |
5289 |
11 Feb 19 |
nicklas |
1028 |
} |
5289 |
11 Feb 19 |
nicklas |
1029 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
1030 |
} |
5289 |
11 Feb 19 |
nicklas |
1031 |
|
5289 |
11 Feb 19 |
nicklas |
// Report lines that has a "personal number" but missing, invalid or duplicate laterality |
5289 |
11 Feb 19 |
nicklas |
1033 |
int sumExcludedLines = incaFile.sumExcludedLines(ExcludedLine.MISSING_LATERALITY, ExcludedLine.DUPLICATE_LATERALITY); |
5289 |
11 Feb 19 |
nicklas |
1034 |
if (sumExcludedLines > 0) |
5289 |
11 Feb 19 |
nicklas |
1035 |
{ |
5289 |
11 Feb 19 |
nicklas |
1036 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
1037 |
report.write("Lines with with missing, invalid or duplicate laterality\n"); |
5289 |
11 Feb 19 |
nicklas |
1038 |
report.write("--------------------------------------------------------\n"); |
5293 |
12 Feb 19 |
nicklas |
1039 |
table.tablePrintData("LineNo", "PAT_ID", "PersonalNo", "Laterality", "Flag"); |
5289 |
11 Feb 19 |
nicklas |
1040 |
for (IncaLine line : incaFile.lines) |
5289 |
11 Feb 19 |
nicklas |
1041 |
{ |
5289 |
11 Feb 19 |
nicklas |
1042 |
if (line.exclude == ExcludedLine.MISSING_LATERALITY) |
5289 |
11 Feb 19 |
nicklas |
1043 |
{ |
5289 |
11 Feb 19 |
nicklas |
1044 |
String lat = line.col(incaFile.keyColumns[KeyColumn.LATERALITY.ordinal()]); |
5289 |
11 Feb 19 |
nicklas |
1045 |
String flag = lat == null ? "MISSING_LATERALITY" : "INVALID_LATERALITY"; |
5293 |
12 Feb 19 |
nicklas |
1046 |
table.tablePrintData(line.lineNo, line.patIdInca, line.personalNo, lat, flag); |
5289 |
11 Feb 19 |
nicklas |
1047 |
} |
5289 |
11 Feb 19 |
nicklas |
1048 |
else if (line.exclude == ExcludedLine.DUPLICATE_LATERALITY) |
5289 |
11 Feb 19 |
nicklas |
1049 |
{ |
5293 |
12 Feb 19 |
nicklas |
1050 |
table.tablePrintData(line.lineNo, line.patIdInca, line.personalNo, line.laterality, "DUPLICATE_LATERALITY"); |
5289 |
11 Feb 19 |
nicklas |
1051 |
} |
5289 |
11 Feb 19 |
nicklas |
1052 |
} |
5289 |
11 Feb 19 |
nicklas |
1053 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
1054 |
} |
5289 |
11 Feb 19 |
nicklas |
1055 |
|
5289 |
11 Feb 19 |
nicklas |
// Report lines that has invalid data values |
5289 |
11 Feb 19 |
nicklas |
1057 |
if (incaFile.excludedLineCount[ExcludedLine.INVALID_DATA_VALUE.ordinal()] > 0) |
5289 |
11 Feb 19 |
nicklas |
1058 |
{ |
5289 |
11 Feb 19 |
nicklas |
1059 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
1060 |
report.write("Lines with invalid data values\n"); |
5289 |
11 Feb 19 |
nicklas |
1061 |
report.write("------------------------------\n"); |
5293 |
12 Feb 19 |
nicklas |
1062 |
table.tablePrintData("LineNo", "PAT_ID", "PersonalNo", "Laterality", "Column", "Value", "Expected type", "Comment"); |
5289 |
11 Feb 19 |
nicklas |
1063 |
for (IncaLine line : incaFile.lines) |
5289 |
11 Feb 19 |
nicklas |
1064 |
{ |
5289 |
11 Feb 19 |
nicklas |
1065 |
if (line.exclude == ExcludedLine.INVALID_DATA_VALUE) |
5289 |
11 Feb 19 |
nicklas |
1066 |
{ |
5289 |
11 Feb 19 |
nicklas |
1067 |
for (int colNo = 0; colNo < incaFile.headers.length; colNo++) |
5289 |
11 Feb 19 |
nicklas |
1068 |
{ |
5289 |
11 Feb 19 |
nicklas |
1069 |
Object d = line.data[colNo]; |
5289 |
11 Feb 19 |
nicklas |
1070 |
if (d instanceof Exception) |
5289 |
11 Feb 19 |
nicklas |
1071 |
{ |
5289 |
11 Feb 19 |
nicklas |
1072 |
Exception ex = (Exception)d; |
5289 |
11 Feb 19 |
nicklas |
1073 |
Header h = incaFile.headers[colNo]; |
5293 |
12 Feb 19 |
nicklas |
1074 |
table.tablePrintData(line.lineNo, line.patIdInca, line.personalNo, line.laterality, |
5293 |
12 Feb 19 |
nicklas |
1075 |
h.name, line.columns[colNo], h.annotationType.getValueType().name(), |
5293 |
12 Feb 19 |
nicklas |
1076 |
ex.getMessage()); |
5289 |
11 Feb 19 |
nicklas |
1077 |
} |
5289 |
11 Feb 19 |
nicklas |
1078 |
} |
5289 |
11 Feb 19 |
nicklas |
1079 |
} |
5289 |
11 Feb 19 |
nicklas |
1080 |
} |
5289 |
11 Feb 19 |
nicklas |
1081 |
report.write("\n"); |
5289 |
11 Feb 19 |
nicklas |
1082 |
} |
5295 |
12 Feb 19 |
nicklas |
1083 |
report.flush(); |
5295 |
12 Feb 19 |
nicklas |
1084 |
out.flush(); |
5289 |
11 Feb 19 |
nicklas |
1085 |
} |
5295 |
12 Feb 19 |
nicklas |
1086 |
catch (Exception ex) |
5289 |
11 Feb 19 |
nicklas |
1087 |
{ |
5289 |
11 Feb 19 |
nicklas |
1088 |
System.out.println(new Date() + " IncaStatisticsServlet::doPost(): Could not create filewriter for reportFilePath = " + reportFilePath); |
5289 |
11 Feb 19 |
nicklas |
1089 |
} |
5289 |
11 Feb 19 |
nicklas |
1090 |
finally |
5289 |
11 Feb 19 |
nicklas |
1091 |
{ |
5289 |
11 Feb 19 |
nicklas |
1092 |
FileUtil.close(report); |
5289 |
11 Feb 19 |
nicklas |
1093 |
FileUtil.close(out); |
5289 |
11 Feb 19 |
nicklas |
1094 |
} |
5289 |
11 Feb 19 |
nicklas |
1095 |
|
3963 |
20 May 16 |
olle |
1096 |
} |
3963 |
20 May 16 |
olle |
1097 |
|
3963 |
20 May 16 |
olle |
1098 |
|
3836 |
12 Apr 16 |
olle |
1099 |
/** |
5269 |
28 Jan 19 |
nicklas |
Class for parsing, processing and collecting data about an INCA |
5269 |
28 Jan 19 |
nicklas |
file. |
5269 |
28 Jan 19 |
nicklas |
1102 |
*/ |
5269 |
28 Jan 19 |
nicklas |
1103 |
static class IncaFile |
3836 |
12 Apr 16 |
olle |
1104 |
{ |
5256 |
21 Jan 19 |
nicklas |
1105 |
final String filename; |
5288 |
08 Feb 19 |
nicklas |
1106 |
final boolean forStatistics; |
5269 |
28 Jan 19 |
nicklas |
// Decoder for converting back \n, \r, \t, etc. to newline, tab, etc. |
5273 |
31 Jan 19 |
nicklas |
1108 |
final TabCrLfEncoderDecoder decoder = new TabCrLfEncoderDecoder(true); |
5269 |
28 Jan 19 |
nicklas |
1109 |
|
5269 |
28 Jan 19 |
nicklas |
1110 |
private Header[] headers; |
5260 |
22 Jan 19 |
nicklas |
1111 |
private int[] keyColumns = new int[KeyColumn.values().length]; |
3836 |
12 Apr 16 |
olle |
1112 |
|
5262 |
24 Jan 19 |
nicklas |
1113 |
boolean isFollowUp = false; |
5260 |
22 Jan 19 |
nicklas |
1114 |
|
5288 |
08 Feb 19 |
nicklas |
// Required headers that was not found in the INCA file |
5263 |
24 Jan 19 |
nicklas |
1116 |
Set<String> missingHeaders = new TreeSet<>(); |
5288 |
08 Feb 19 |
nicklas |
// Required headers that has no mathching annotation type in BASE |
5288 |
08 Feb 19 |
nicklas |
1118 |
Set<String> missingAnnotationTypes = new TreeSet<>(); |
5260 |
22 Jan 19 |
nicklas |
// Headers that appear more than once in the INCA file |
5263 |
24 Jan 19 |
nicklas |
1120 |
Set<String> duplicateHeaders = new TreeSet<>(); |
5260 |
22 Jan 19 |
nicklas |
// Headers that are in the INCA file but doesn't have a matching annotation type |
5263 |
24 Jan 19 |
nicklas |
1122 |
Set<String> unknownHeaders = new TreeSet<>(); |
5260 |
22 Jan 19 |
nicklas |
// Annotation types in BASE that are not found in the INCA file |
5263 |
24 Jan 19 |
nicklas |
1124 |
Set<String> unmappedHeaders = new TreeSet<>(); |
5274 |
31 Jan 19 |
nicklas |
// Annotation types in BASE that are found in the INCA file |
5274 |
31 Jan 19 |
nicklas |
1126 |
Set<String> mappedHeaders = new TreeSet<>(); |
5260 |
22 Jan 19 |
nicklas |
1127 |
|
5261 |
23 Jan 19 |
nicklas |
1128 |
List<IncaLine> lines = new ArrayList<>(); |
3836 |
12 Apr 16 |
olle |
1129 |
|
5263 |
24 Jan 19 |
nicklas |
1130 |
int[] excludedLineCount = new int[ExcludedLine.values().length]; |
5268 |
25 Jan 19 |
nicklas |
1131 |
int totalExcludedLines = 0; |
5256 |
21 Jan 19 |
nicklas |
1132 |
|
5273 |
31 Jan 19 |
nicklas |
1133 |
Map<String, LinePair> mappedLinePairs = new HashMap<>(); |
5258 |
22 Jan 19 |
nicklas |
1134 |
|
5287 |
08 Feb 19 |
nicklas |
// Some metadata about the import/statistics |
5287 |
08 Feb 19 |
nicklas |
1136 |
Date startDate; |
5287 |
08 Feb 19 |
nicklas |
1137 |
Date endDate; |
5270 |
29 Jan 19 |
nicklas |
1138 |
Date exportDate; |
5270 |
29 Jan 19 |
nicklas |
1139 |
|
5273 |
31 Jan 19 |
nicklas |
// Statistics about changed cases and annotations |
5270 |
29 Jan 19 |
nicklas |
1141 |
int numCasesChecked; |
5270 |
29 Jan 19 |
nicklas |
1142 |
int numCasesUpdated; |
5273 |
31 Jan 19 |
nicklas |
1143 |
int[] numAnnotations = new int[AnnotationBatcher.Change.values().length]; |
5270 |
29 Jan 19 |
nicklas |
1144 |
|
5290 |
11 Feb 19 |
nicklas |
1145 |
List<StatisticalVariable> statistics = new ArrayList<>(); |
5290 |
11 Feb 19 |
nicklas |
1146 |
|
5269 |
28 Jan 19 |
nicklas |
1147 |
/** |
5269 |
28 Jan 19 |
nicklas |
Create a new empty instance. |
5269 |
28 Jan 19 |
nicklas |
1149 |
*/ |
5288 |
08 Feb 19 |
nicklas |
1150 |
IncaFile(String filename, boolean forStatistics) |
3836 |
12 Apr 16 |
olle |
1151 |
{ |
3836 |
12 Apr 16 |
olle |
1152 |
this.filename = filename; |
5288 |
08 Feb 19 |
nicklas |
1153 |
this.forStatistics = forStatistics; |
5287 |
08 Feb 19 |
nicklas |
1154 |
this.startDate = new Date(); |
5260 |
22 Jan 19 |
nicklas |
1155 |
Arrays.fill(keyColumns, -1); |
3836 |
12 Apr 16 |
olle |
1156 |
} |
3836 |
12 Apr 16 |
olle |
1157 |
|
5269 |
28 Jan 19 |
nicklas |
1158 |
/** |
5269 |
28 Jan 19 |
nicklas |
Parse the input stream and start to collect data. |
5269 |
28 Jan 19 |
nicklas |
The first line is expected to be a header line with columns |
5269 |
28 Jan 19 |
nicklas |
headers separated by {tab} characters. Some checks are made: |
5269 |
28 Jan 19 |
nicklas |
1162 |
|
5269 |
28 Jan 19 |
nicklas |
- Duplicate headers are detected and stored in 'duplicateHeaders' |
5269 |
28 Jan 19 |
nicklas |
- Required headers are checked and if missing stored in 'missingHeaders' |
5269 |
28 Jan 19 |
nicklas |
1165 |
|
5269 |
28 Jan 19 |
nicklas |
The rest of the lines are data lines and should have the same number of |
5269 |
28 Jan 19 |
nicklas |
columns as the header line. |
5269 |
28 Jan 19 |
nicklas |
1168 |
*/ |
5269 |
28 Jan 19 |
nicklas |
1169 |
void parse(InputStream in) |
5269 |
28 Jan 19 |
nicklas |
1170 |
throws IOException |
3836 |
12 Apr 16 |
olle |
1171 |
{ |
5269 |
28 Jan 19 |
nicklas |
1172 |
BufferedReader reader = null; |
5269 |
28 Jan 19 |
nicklas |
1173 |
try |
5269 |
28 Jan 19 |
nicklas |
1174 |
{ |
5269 |
28 Jan 19 |
nicklas |
1175 |
reader = new BufferedReader(new InputStreamReader(in, Charset.forName("UTF-8"))); |
5269 |
28 Jan 19 |
nicklas |
1176 |
|
5269 |
28 Jan 19 |
nicklas |
// First line contains column header names |
5269 |
28 Jan 19 |
nicklas |
1178 |
String headerLine = reader.readLine(); |
5269 |
28 Jan 19 |
nicklas |
1179 |
setHeaders(headerLine); |
5269 |
28 Jan 19 |
nicklas |
1180 |
|
5269 |
28 Jan 19 |
nicklas |
1181 |
String line = reader.readLine(); |
5269 |
28 Jan 19 |
nicklas |
1182 |
while (line != null) |
5269 |
28 Jan 19 |
nicklas |
1183 |
{ |
5269 |
28 Jan 19 |
nicklas |
1184 |
addLine(line); |
5269 |
28 Jan 19 |
nicklas |
1185 |
line = reader.readLine(); |
5269 |
28 Jan 19 |
nicklas |
1186 |
} |
5269 |
28 Jan 19 |
nicklas |
1187 |
} |
5269 |
28 Jan 19 |
nicklas |
1188 |
finally |
5269 |
28 Jan 19 |
nicklas |
1189 |
{ |
5288 |
08 Feb 19 |
nicklas |
// !Important here that we read to the end of the file. It is not enough to close it! |
5288 |
08 Feb 19 |
nicklas |
// Otherwise, an error will cause the browser to try to re-submit |
5288 |
08 Feb 19 |
nicklas |
// the file a couple of times and in the end only a generic "Error" message |
5288 |
08 Feb 19 |
nicklas |
// without information is displayed |
5288 |
08 Feb 19 |
nicklas |
1194 |
try |
5288 |
08 Feb 19 |
nicklas |
1195 |
{ |
5288 |
08 Feb 19 |
nicklas |
1196 |
FileUtil.read(in); |
5288 |
08 Feb 19 |
nicklas |
1197 |
} |
5288 |
08 Feb 19 |
nicklas |
1198 |
catch (IOException ex) |
5288 |
08 Feb 19 |
nicklas |
1199 |
{} |
5269 |
28 Jan 19 |
nicklas |
1200 |
FileUtil.close(in); |
5269 |
28 Jan 19 |
nicklas |
1201 |
FileUtil.close(reader); |
5269 |
28 Jan 19 |
nicklas |
1202 |
} |
5269 |
28 Jan 19 |
nicklas |
1203 |
} |
5269 |
28 Jan 19 |
nicklas |
1204 |
|
5269 |
28 Jan 19 |
nicklas |
1205 |
/** |
5269 |
28 Jan 19 |
nicklas |
Parse the header line. See the parse() method for more information. |
5269 |
28 Jan 19 |
nicklas |
1207 |
*/ |
5269 |
28 Jan 19 |
nicklas |
1208 |
private void setHeaders(String headerRow) |
5269 |
28 Jan 19 |
nicklas |
1209 |
{ |
5260 |
22 Jan 19 |
nicklas |
1210 |
String[] tmp = headerRow.split("\t"); |
5269 |
28 Jan 19 |
nicklas |
1211 |
headers = new Header[tmp.length]; |
5269 |
28 Jan 19 |
nicklas |
1212 |
|
5269 |
28 Jan 19 |
nicklas |
1213 |
Set<String> names = new HashSet<>(); // To check for duplicates |
5269 |
28 Jan 19 |
nicklas |
1214 |
for (int colNo = 0; colNo < tmp.length; colNo++) |
5256 |
21 Jan 19 |
nicklas |
1215 |
{ |
5269 |
28 Jan 19 |
nicklas |
1216 |
Header h = new Header(colNo, tmp[colNo]); |
5269 |
28 Jan 19 |
nicklas |
1217 |
headers[colNo] = h; |
5269 |
28 Jan 19 |
nicklas |
1218 |
|
5260 |
22 Jan 19 |
nicklas |
1219 |
if (h.keyColumn != null) |
5256 |
21 Jan 19 |
nicklas |
1220 |
{ |
5269 |
28 Jan 19 |
nicklas |
// If the 'keyColumn' is set, this header is one of the predifined 'KeyColumn':s |
5269 |
28 Jan 19 |
nicklas |
// We store the 'colNo' for quick access |
5269 |
28 Jan 19 |
nicklas |
1223 |
keyColumns[h.keyColumn.ordinal()] = colNo; |
5260 |
22 Jan 19 |
nicklas |
1224 |
} |
5260 |
22 Jan 19 |
nicklas |
1225 |
if (!names.add(h.name)) |
5260 |
22 Jan 19 |
nicklas |
1226 |
{ |
5260 |
22 Jan 19 |
nicklas |
1227 |
duplicateHeaders.add(h.name); |
5256 |
21 Jan 19 |
nicklas |
1228 |
} |
5256 |
21 Jan 19 |
nicklas |
1229 |
} |
5269 |
28 Jan 19 |
nicklas |
1230 |
|
5262 |
24 Jan 19 |
nicklas |
1231 |
isFollowUp = keyColumns[KeyColumn.FOLLOWUP_DATE.ordinal()] >= 0; |
5263 |
24 Jan 19 |
nicklas |
1232 |
|
5269 |
28 Jan 19 |
nicklas |
// Check that required columns are present |
5288 |
08 Feb 19 |
nicklas |
1234 |
checkKeyColumns(false, KeyColumn.PAT_ID, KeyColumn.PERSONAL_NO); |
5288 |
08 Feb 19 |
nicklas |
// Special case for LATERALITY in follow-up files |
5263 |
24 Jan 19 |
nicklas |
1236 |
if (keyColumns[KeyColumn.LATERALITY.ordinal()] == -1) |
5263 |
24 Jan 19 |
nicklas |
1237 |
{ |
5263 |
24 Jan 19 |
nicklas |
1238 |
missingHeaders.add(isFollowUp ? KeyColumn.LATERALITY.alternate : KeyColumn.LATERALITY.key); |
5263 |
24 Jan 19 |
nicklas |
1239 |
} |
3836 |
12 Apr 16 |
olle |
1240 |
} |
3836 |
12 Apr 16 |
olle |
1241 |
|
5288 |
08 Feb 19 |
nicklas |
1242 |
/** |
5288 |
08 Feb 19 |
nicklas |
Checks that the required key columns are present in the file. |
5288 |
08 Feb 19 |
nicklas |
Columns that are missing are added to the 'missingHeaders' collections |
5288 |
08 Feb 19 |
nicklas |
or to the 'missingAnnotationTypes' collection. |
5288 |
08 Feb 19 |
nicklas |
@return An array with Header:s matching the KeyColumn input |
5288 |
08 Feb 19 |
nicklas |
1247 |
*/ |
5288 |
08 Feb 19 |
nicklas |
1248 |
Header[] checkKeyColumns(boolean requireAnnotationType, KeyColumn... columns) |
5288 |
08 Feb 19 |
nicklas |
1249 |
{ |
5288 |
08 Feb 19 |
nicklas |
1250 |
Header[] matched = new Header[columns.length]; |
5288 |
08 Feb 19 |
nicklas |
1251 |
for (int index = 0; index < columns.length; index++) |
5288 |
08 Feb 19 |
nicklas |
1252 |
{ |
5288 |
08 Feb 19 |
nicklas |
1253 |
KeyColumn kc = columns[index]; |
5288 |
08 Feb 19 |
nicklas |
1254 |
int headerIndex = keyColumns[kc.ordinal()]; |
5288 |
08 Feb 19 |
nicklas |
1255 |
if (headerIndex == -1) |
5288 |
08 Feb 19 |
nicklas |
1256 |
{ |
5288 |
08 Feb 19 |
nicklas |
1257 |
missingHeaders.add(kc.key); |
5288 |
08 Feb 19 |
nicklas |
1258 |
} |
5288 |
08 Feb 19 |
nicklas |
1259 |
else if (requireAnnotationType && headers[headerIndex].annotationType == null) |
5288 |
08 Feb 19 |
nicklas |
1260 |
{ |
5288 |
08 Feb 19 |
nicklas |
1261 |
missingAnnotationTypes.add("INCA2_"+kc.key); |
5288 |
08 Feb 19 |
nicklas |
1262 |
} |
5288 |
08 Feb 19 |
nicklas |
1263 |
else |
5288 |
08 Feb 19 |
nicklas |
1264 |
{ |
5288 |
08 Feb 19 |
nicklas |
1265 |
matched[index] = headers[headerIndex]; |
5288 |
08 Feb 19 |
nicklas |
1266 |
} |
5288 |
08 Feb 19 |
nicklas |
1267 |
} |
5288 |
08 Feb 19 |
nicklas |
1268 |
return matched; |
5288 |
08 Feb 19 |
nicklas |
1269 |
} |
5288 |
08 Feb 19 |
nicklas |
1270 |
|
5269 |
28 Jan 19 |
nicklas |
1271 |
/** |
5269 |
28 Jan 19 |
nicklas |
Add a data line. The text will be split into |
5269 |
28 Jan 19 |
nicklas |
columns and some initial checks are made: |
5269 |
28 Jan 19 |
nicklas |
1274 |
|
5269 |
28 Jan 19 |
nicklas |
- that the number of columns match the number of headers |
5269 |
28 Jan 19 |
nicklas |
- that required columns have a value (eg. personal no, laterality) |
5269 |
28 Jan 19 |
nicklas |
1277 |
*/ |
5269 |
28 Jan 19 |
nicklas |
1278 |
void addLine(String text) |
5269 |
28 Jan 19 |
nicklas |
1279 |
{ |
5466 |
04 Jun 19 |
nicklas |
1280 |
IncaLine line = new IncaLine(lines.size()+2, text); // +2 because lineNo=1 is header line |
5269 |
28 Jan 19 |
nicklas |
1281 |
lines.add(line); |
5269 |
28 Jan 19 |
nicklas |
1282 |
line.checkColumns(this); |
5269 |
28 Jan 19 |
nicklas |
1283 |
} |
5269 |
28 Jan 19 |
nicklas |
1284 |
|
5269 |
28 Jan 19 |
nicklas |
1285 |
/** |
5269 |
28 Jan 19 |
nicklas |
Map columns to annotation types. This will populate |
5269 |
28 Jan 19 |
nicklas |
the {@link Header#annotationType} for all headers |
5269 |
28 Jan 19 |
nicklas |
with a name that matches the name of the annotation type |
5269 |
28 Jan 19 |
nicklas |
(after removing the INCA2_ prefix). |
5269 |
28 Jan 19 |
nicklas |
1290 |
|
5269 |
28 Jan 19 |
nicklas |
This method will also populate the 'unmappedHeader' with |
5269 |
28 Jan 19 |
nicklas |
the name of all annotation types that hasn't been matched. |
5269 |
28 Jan 19 |
nicklas |
and 'unknownHeaders' with the names of all headers that |
5269 |
28 Jan 19 |
nicklas |
are not mapped to a KeyColumn or AnnotationType. |
5269 |
28 Jan 19 |
nicklas |
1295 |
*/ |
5289 |
11 Feb 19 |
nicklas |
1296 |
void mapAnnotationColumns(List<AnnotationType> annotationTypes, boolean onlyKeyColumns) |
5260 |
22 Jan 19 |
nicklas |
1297 |
{ |
5260 |
22 Jan 19 |
nicklas |
1298 |
for (AnnotationType at : annotationTypes) |
5260 |
22 Jan 19 |
nicklas |
1299 |
{ |
5260 |
22 Jan 19 |
nicklas |
1300 |
String nameToMatch = at.getName().replace("INCA2_", ""); |
5260 |
22 Jan 19 |
nicklas |
1301 |
boolean hasMatched = false; |
5260 |
22 Jan 19 |
nicklas |
1302 |
|
5260 |
22 Jan 19 |
nicklas |
1303 |
for (Header h : headers) |
5260 |
22 Jan 19 |
nicklas |
1304 |
{ |
5289 |
11 Feb 19 |
nicklas |
1305 |
if (onlyKeyColumns && h.keyColumn == null) continue; |
5289 |
11 Feb 19 |
nicklas |
1306 |
|
5260 |
22 Jan 19 |
nicklas |
1307 |
if (h.name.equals(nameToMatch)) |
5260 |
22 Jan 19 |
nicklas |
1308 |
{ |
5260 |
22 Jan 19 |
nicklas |
1309 |
h.annotationType = at; |
5260 |
22 Jan 19 |
nicklas |
1310 |
hasMatched = true; |
5260 |
22 Jan 19 |
nicklas |
1311 |
} |
5260 |
22 Jan 19 |
nicklas |
1312 |
} |
5274 |
31 Jan 19 |
nicklas |
1313 |
if (!hasMatched) |
5274 |
31 Jan 19 |
nicklas |
1314 |
{ |
5274 |
31 Jan 19 |
nicklas |
1315 |
unmappedHeaders.add(at.getName()); |
5274 |
31 Jan 19 |
nicklas |
1316 |
} |
5274 |
31 Jan 19 |
nicklas |
1317 |
else |
5274 |
31 Jan 19 |
nicklas |
1318 |
{ |
5274 |
31 Jan 19 |
nicklas |
1319 |
mappedHeaders.add(at.getName()); |
5274 |
31 Jan 19 |
nicklas |
1320 |
} |
5260 |
22 Jan 19 |
nicklas |
1321 |
} |
5260 |
22 Jan 19 |
nicklas |
1322 |
|
5269 |
28 Jan 19 |
nicklas |
// Check all headers if they are mapped to either a 'KeyColumn' or 'AnnotationType' |
5260 |
22 Jan 19 |
nicklas |
1324 |
for (Header h : headers) |
5260 |
22 Jan 19 |
nicklas |
1325 |
{ |
5260 |
22 Jan 19 |
nicklas |
1326 |
if (h.annotationType == null && h.keyColumn == null) |
5260 |
22 Jan 19 |
nicklas |
1327 |
{ |
5260 |
22 Jan 19 |
nicklas |
1328 |
unknownHeaders.add(h.name); |
5260 |
22 Jan 19 |
nicklas |
1329 |
} |
5260 |
22 Jan 19 |
nicklas |
1330 |
} |
5260 |
22 Jan 19 |
nicklas |
1331 |
} |
5258 |
22 Jan 19 |
nicklas |
1332 |
|
5260 |
22 Jan 19 |
nicklas |
1333 |
|
5269 |
28 Jan 19 |
nicklas |
1334 |
/** |
5269 |
28 Jan 19 |
nicklas |
Check for data lines that have identical personal number and laterality. |
5269 |
28 Jan 19 |
nicklas |
This is expected if the INCA file is a follow-up file in which case we |
5269 |
28 Jan 19 |
nicklas |
also check the follow-up date and choose the line with the latest date. |
5273 |
31 Jan 19 |
nicklas |
It is not expected in a regular file and we exclude both lines. |
5273 |
31 Jan 19 |
nicklas |
1339 |
|
5273 |
31 Jan 19 |
nicklas |
This method also populates the 'mappedLinePairs' map which is needed |
5273 |
31 Jan 19 |
nicklas |
by the 'doDatabaseMapping' method. |
5269 |
28 Jan 19 |
nicklas |
1342 |
*/ |
5262 |
24 Jan 19 |
nicklas |
1343 |
void doLateralityCheck() |
5262 |
24 Jan 19 |
nicklas |
1344 |
{ |
5262 |
24 Jan 19 |
nicklas |
1345 |
for (IncaLine line : lines) |
5262 |
24 Jan 19 |
nicklas |
1346 |
{ |
5263 |
24 Jan 19 |
nicklas |
1347 |
if (line.exclude != null) continue; |
5262 |
24 Jan 19 |
nicklas |
1348 |
|
5292 |
12 Feb 19 |
nicklas |
1349 |
String personalId = line.personalNo != null ? line.personalNo : line.patIdInca; |
5288 |
08 Feb 19 |
nicklas |
1350 |
|
5288 |
08 Feb 19 |
nicklas |
1351 |
LinePair pair = mappedLinePairs.get(personalId); |
5273 |
31 Jan 19 |
nicklas |
1352 |
if (pair == null) |
5262 |
24 Jan 19 |
nicklas |
1353 |
{ |
5273 |
31 Jan 19 |
nicklas |
// First time we see this personal number |
5273 |
31 Jan 19 |
nicklas |
1355 |
pair = new LinePair(); |
5273 |
31 Jan 19 |
nicklas |
1356 |
pair.set(line); |
5288 |
08 Feb 19 |
nicklas |
1357 |
mappedLinePairs.put(personalId, pair); |
5273 |
31 Jan 19 |
nicklas |
1358 |
} |
5273 |
31 Jan 19 |
nicklas |
1359 |
else |
5273 |
31 Jan 19 |
nicklas |
1360 |
{ |
5273 |
31 Jan 19 |
nicklas |
// We have seen this personal number before, see if the other line |
5273 |
31 Jan 19 |
nicklas |
// is the same laterality |
5273 |
31 Jan 19 |
nicklas |
1363 |
IncaLine other = pair.get(line.laterality); |
5273 |
31 Jan 19 |
nicklas |
1364 |
if (other == null) |
5262 |
24 Jan 19 |
nicklas |
1365 |
{ |
5273 |
31 Jan 19 |
nicklas |
// The other line is not the same laterality so we can store this line |
5273 |
31 Jan 19 |
nicklas |
1367 |
pair.set(line); |
5273 |
31 Jan 19 |
nicklas |
1368 |
} |
5273 |
31 Jan 19 |
nicklas |
1369 |
else |
5273 |
31 Jan 19 |
nicklas |
1370 |
{ |
5273 |
31 Jan 19 |
nicklas |
// The other line is the same laterality as this one... |
5273 |
31 Jan 19 |
nicklas |
1372 |
if (isFollowUp) |
5262 |
24 Jan 19 |
nicklas |
1373 |
{ |
5273 |
31 Jan 19 |
nicklas |
// In follow-up files we keep the line with the latest date |
5273 |
31 Jan 19 |
nicklas |
1375 |
if (line.date.after(other.date)) |
5273 |
31 Jan 19 |
nicklas |
1376 |
{ |
5273 |
31 Jan 19 |
nicklas |
1377 |
other.exclude = ExcludedLine.DUPLICATE_FOLLOWUP; |
5273 |
31 Jan 19 |
nicklas |
1378 |
pair.set(line); |
5273 |
31 Jan 19 |
nicklas |
1379 |
} |
5273 |
31 Jan 19 |
nicklas |
1380 |
else |
5273 |
31 Jan 19 |
nicklas |
1381 |
{ |
5273 |
31 Jan 19 |
nicklas |
1382 |
line.exclude = ExcludedLine.DUPLICATE_FOLLOWUP; |
5273 |
31 Jan 19 |
nicklas |
1383 |
} |
5262 |
24 Jan 19 |
nicklas |
1384 |
} |
5262 |
24 Jan 19 |
nicklas |
1385 |
else |
5262 |
24 Jan 19 |
nicklas |
1386 |
{ |
5273 |
31 Jan 19 |
nicklas |
// In regular INCA file this is not allowed and we exclude both lines |
5273 |
31 Jan 19 |
nicklas |
1388 |
other.exclude = ExcludedLine.DUPLICATE_LATERALITY; |
5273 |
31 Jan 19 |
nicklas |
1389 |
line.exclude = ExcludedLine.DUPLICATE_LATERALITY; |
5262 |
24 Jan 19 |
nicklas |
1390 |
} |
5262 |
24 Jan 19 |
nicklas |
1391 |
} |
5262 |
24 Jan 19 |
nicklas |
1392 |
} |
5262 |
24 Jan 19 |
nicklas |
1393 |
} |
5263 |
24 Jan 19 |
nicklas |
1394 |
} |
5263 |
24 Jan 19 |
nicklas |
1395 |
|
5269 |
28 Jan 19 |
nicklas |
1396 |
/** |
5269 |
28 Jan 19 |
nicklas |
Check the data values for all columns mapped to annotation types |
5269 |
28 Jan 19 |
nicklas |
on all lines that are not excluded. |
5269 |
28 Jan 19 |
nicklas |
1399 |
|
5269 |
28 Jan 19 |
nicklas |
If the value is ok, it is copied to the {@link IncaLine.data} array. |
5269 |
28 Jan 19 |
nicklas |
If the value is not ok, the line is marked as excluded and the |
5269 |
28 Jan 19 |
nicklas |
exception is stored in the 'data' array. |
5269 |
28 Jan 19 |
nicklas |
1403 |
*/ |
5288 |
08 Feb 19 |
nicklas |
1404 |
void doDataCheck(Header[] columnsToCheck) |
5267 |
25 Jan 19 |
nicklas |
1405 |
{ |
5288 |
08 Feb 19 |
nicklas |
1406 |
if (columnsToCheck == null) columnsToCheck = headers; |
5288 |
08 Feb 19 |
nicklas |
1407 |
|
5267 |
25 Jan 19 |
nicklas |
1408 |
for (IncaLine line : lines) |
5267 |
25 Jan 19 |
nicklas |
1409 |
{ |
5267 |
25 Jan 19 |
nicklas |
1410 |
if (line.exclude != null) continue; |
5267 |
25 Jan 19 |
nicklas |
1411 |
|
5267 |
25 Jan 19 |
nicklas |
1412 |
line.data = new Object[line.columns.length]; |
5267 |
25 Jan 19 |
nicklas |
1413 |
|
5288 |
08 Feb 19 |
nicklas |
1414 |
for (Header h : columnsToCheck) |
5267 |
25 Jan 19 |
nicklas |
1415 |
{ |
5288 |
08 Feb 19 |
nicklas |
1416 |
if (h != null && h.annotationType != null) |
5267 |
25 Jan 19 |
nicklas |
1417 |
{ |
5267 |
25 Jan 19 |
nicklas |
1418 |
String s = Values.getStringOrNull(line.columns[h.index]); |
5267 |
25 Jan 19 |
nicklas |
1419 |
if (s != null) |
5267 |
25 Jan 19 |
nicklas |
1420 |
{ |
5267 |
25 Jan 19 |
nicklas |
1421 |
try |
5267 |
25 Jan 19 |
nicklas |
1422 |
{ |
5267 |
25 Jan 19 |
nicklas |
1423 |
Object value = h.annotationType.getValueType().parseString(s); |
5267 |
25 Jan 19 |
nicklas |
1424 |
h.annotationType.validateAnnotationValue(value); |
5267 |
25 Jan 19 |
nicklas |
1425 |
line.data[h.index] = value; |
5267 |
25 Jan 19 |
nicklas |
1426 |
} |
5267 |
25 Jan 19 |
nicklas |
1427 |
catch (RuntimeException ex) |
5267 |
25 Jan 19 |
nicklas |
1428 |
{ |
5268 |
25 Jan 19 |
nicklas |
1429 |
line.data[h.index] = ex; |
5267 |
25 Jan 19 |
nicklas |
1430 |
line.exclude = ExcludedLine.INVALID_DATA_VALUE; |
5267 |
25 Jan 19 |
nicklas |
1431 |
} |
5267 |
25 Jan 19 |
nicklas |
1432 |
} |
5267 |
25 Jan 19 |
nicklas |
1433 |
} |
5267 |
25 Jan 19 |
nicklas |
1434 |
} |
5267 |
25 Jan 19 |
nicklas |
1435 |
} |
5267 |
25 Jan 19 |
nicklas |
1436 |
} |
5267 |
25 Jan 19 |
nicklas |
1437 |
|
5273 |
31 Jan 19 |
nicklas |
1438 |
/** |
5288 |
08 Feb 19 |
nicklas |
Check all lines that have not already been excluded with the filter. |
5288 |
08 Feb 19 |
nicklas |
Lines that doens't pass are excluded. |
5288 |
08 Feb 19 |
nicklas |
1441 |
*/ |
5288 |
08 Feb 19 |
nicklas |
1442 |
void doFilter(Filter<IncaLine> filter, ExcludedLine reason) |
5288 |
08 Feb 19 |
nicklas |
1443 |
{ |
5288 |
08 Feb 19 |
nicklas |
1444 |
for (IncaLine line : lines) |
5288 |
08 Feb 19 |
nicklas |
1445 |
{ |
5288 |
08 Feb 19 |
nicklas |
1446 |
if (line.exclude != null) continue; |
5288 |
08 Feb 19 |
nicklas |
1447 |
if (!filter.evaluate(line)) |
5288 |
08 Feb 19 |
nicklas |
1448 |
{ |
5288 |
08 Feb 19 |
nicklas |
1449 |
line.exclude = reason; |
5288 |
08 Feb 19 |
nicklas |
1450 |
} |
5288 |
08 Feb 19 |
nicklas |
1451 |
} |
5288 |
08 Feb 19 |
nicklas |
1452 |
} |
5288 |
08 Feb 19 |
nicklas |
1453 |
|
5288 |
08 Feb 19 |
nicklas |
1454 |
/** |
5273 |
31 Jan 19 |
nicklas |
Try to find a mathing patient and case in the database for each |
5273 |
31 Jan 19 |
nicklas |
remaining data line. If no match is found found the line is excluded, |
5273 |
31 Jan 19 |
nicklas |
otherwise we store the patientId and caseId together with the data line. |
5273 |
31 Jan 19 |
nicklas |
1458 |
*/ |
5273 |
31 Jan 19 |
nicklas |
1459 |
void doDatabaseMapping(DbControl dc, ProgressReporter progress) |
5267 |
25 Jan 19 |
nicklas |
1460 |
{ |
5273 |
31 Jan 19 |
nicklas |
// Progress reporting |
5273 |
31 Jan 19 |
nicklas |
1462 |
progress.display(0, "Patient and case mapping..."); |
5273 |
31 Jan 19 |
nicklas |
1463 |
|
5273 |
31 Jan 19 |
nicklas |
// Query to locate cases for a given patient |
5273 |
31 Jan 19 |
nicklas |
1465 |
ItemQuery<Sample> caseQuery = Sample.getQuery(); |
5292 |
12 Feb 19 |
nicklas |
1466 |
caseQuery.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT); |
5273 |
31 Jan 19 |
nicklas |
1467 |
Subtype.CASE.addFilter(dc, caseQuery); |
5273 |
31 Jan 19 |
nicklas |
1468 |
caseQuery.restrict(Restrictions.eq( |
5273 |
31 Jan 19 |
nicklas |
1469 |
Hql.property("parent"), Hql.entityParameter("patient", Item.BIOSOURCE)) |
5273 |
31 Jan 19 |
nicklas |
1470 |
); |
5273 |
31 Jan 19 |
nicklas |
1471 |
|
5279 |
05 Feb 19 |
nicklas |
// Query to locate blood items for a given patient -- used if we can't map a data line to an existing case |
5279 |
05 Feb 19 |
nicklas |
1473 |
ItemQuery<Sample> bloodQuery = Sample.getQuery(); |
5292 |
12 Feb 19 |
nicklas |
1474 |
bloodQuery.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT); |
5279 |
05 Feb 19 |
nicklas |
1475 |
Subtype.BLOOD.addFilter(dc, bloodQuery); |
5279 |
05 Feb 19 |
nicklas |
1476 |
bloodQuery.restrict(Restrictions.eq( |
5279 |
05 Feb 19 |
nicklas |
1477 |
Hql.property("parent"), Hql.entityParameter("patient", Item.BIOSOURCE)) |
5279 |
05 Feb 19 |
nicklas |
1478 |
); |
5292 |
12 Feb 19 |
nicklas |
1479 |
|
5292 |
12 Feb 19 |
nicklas |
// Load all cases that has a specimen |
5292 |
12 Feb 19 |
nicklas |
1481 |
ItemQuery<Sample> caseWithSpecimen = Sample.getQuery(); |
5292 |
12 Feb 19 |
nicklas |
1482 |
caseWithSpecimen.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT); |
5292 |
12 Feb 19 |
nicklas |
1483 |
Subtype.CASE.addFilter(dc, caseWithSpecimen); |
5292 |
12 Feb 19 |
nicklas |
1484 |
caseWithSpecimen.join(Hql.innerJoin("childCreationEvents", "cce")); |
5292 |
12 Feb 19 |
nicklas |
1485 |
caseWithSpecimen.join(Hql.innerJoin("cce", "event", "evt")); |
5292 |
12 Feb 19 |
nicklas |
1486 |
caseWithSpecimen.join(Hql.innerJoin("evt", "bioMaterial", "child")); |
5292 |
12 Feb 19 |
nicklas |
1487 |
Subtype.SPECIMEN.addFilter(dc, caseWithSpecimen, "child"); |
5292 |
12 Feb 19 |
nicklas |
1488 |
Set<Integer> allCasesWithSpecimen = new HashSet<>(); |
5292 |
12 Feb 19 |
nicklas |
1489 |
allCasesWithSpecimen.addAll(caseWithSpecimen.idList(dc)); |
5279 |
05 Feb 19 |
nicklas |
1490 |
|
5273 |
31 Jan 19 |
nicklas |
// Load all patients, this is typically quicker than loading patient one-by-one for a large file |
5273 |
31 Jan 19 |
nicklas |
1492 |
ItemQuery<BioSource> patientQuery = BioSource.getQuery(); |
5273 |
31 Jan 19 |
nicklas |
1493 |
patientQuery.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT); |
5273 |
31 Jan 19 |
nicklas |
1494 |
Subtype.PATIENT.addFilter(dc, patientQuery); |
5273 |
31 Jan 19 |
nicklas |
1495 |
patientQuery.setCacheResult(true); |
5273 |
31 Jan 19 |
nicklas |
1496 |
patientQuery.setReturnTotalCount(true); |
5273 |
31 Jan 19 |
nicklas |
1497 |
|
5273 |
31 Jan 19 |
nicklas |
1498 |
ItemResultIterator<BioSource> it = patientQuery.iterate(dc); |
5273 |
31 Jan 19 |
nicklas |
1499 |
int numPatients = (int)it.getTotalCount(); |
5273 |
31 Jan 19 |
nicklas |
1500 |
int count = 0; |
5273 |
31 Jan 19 |
nicklas |
1501 |
while (it.hasNext()) |
5273 |
31 Jan 19 |
nicklas |
1502 |
{ |
5273 |
31 Jan 19 |
nicklas |
1503 |
count++; |
5273 |
31 Jan 19 |
nicklas |
1504 |
BioSource pat = it.next(); |
5273 |
31 Jan 19 |
nicklas |
1505 |
String personalNo = (String)Annotationtype.PERSONAL_NUMBER.getAnnotationValue(dc, pat); |
5273 |
31 Jan 19 |
nicklas |
1506 |
|
5273 |
31 Jan 19 |
nicklas |
1507 |
LinePair pair = mappedLinePairs.get(personalNo); |
5273 |
31 Jan 19 |
nicklas |
1508 |
if (pair == null) pair = mappedLinePairs.get(pat.getName()); // For debugging with files using PATID instead of PersonalNo |
5273 |
31 Jan 19 |
nicklas |
1509 |
if (pair != null) |
5273 |
31 Jan 19 |
nicklas |
1510 |
{ |
5279 |
05 Feb 19 |
nicklas |
1511 |
pair.mapPatient(pat.getId(), pat.getName()); |
5273 |
31 Jan 19 |
nicklas |
1512 |
caseQuery.setEntityParameter("patient", pat); |
5279 |
05 Feb 19 |
nicklas |
1513 |
Set<String> unmappedItems = new TreeSet<>(); |
5279 |
05 Feb 19 |
nicklas |
1514 |
Set<String> mappedCases = new HashSet<>(); |
5273 |
31 Jan 19 |
nicklas |
1515 |
for (Sample s : caseQuery.list(dc)) |
5273 |
31 Jan 19 |
nicklas |
1516 |
{ |
5273 |
31 Jan 19 |
nicklas |
1517 |
String laterality = (String)Annotationtype.LATERALITY.getAnnotationValue(dc, s); |
5292 |
12 Feb 19 |
nicklas |
1518 |
boolean hasSpecimen = allCasesWithSpecimen.contains(s.getId()); |
5292 |
12 Feb 19 |
nicklas |
1519 |
boolean mapped = pair.mapCase(laterality, s.getId(), s.getName(), hasSpecimen); |
5279 |
05 Feb 19 |
nicklas |
1520 |
if (!mapped) |
5279 |
05 Feb 19 |
nicklas |
1521 |
{ |
5279 |
05 Feb 19 |
nicklas |
// A case we have in the database that is not found in the INCA file |
6134 |
16 Feb 21 |
nicklas |
1523 |
String consentDate = Values.getString(Reggie.CONVERTER_DATE_TO_STRING_WITH_SEPARATOR.convert( |
6134 |
16 Feb 21 |
nicklas |
1524 |
(Date)Annotationtype.CONSENT_DATE.getAnnotationValue(dc, s)), "missing"); |
6134 |
16 Feb 21 |
nicklas |
1525 |
unmappedItems.add(s.getName()+" ("+Values.getString(laterality, "Laterality=missing")+"; Consent="+consentDate+")"); |
5279 |
05 Feb 19 |
nicklas |
1526 |
} |
5279 |
05 Feb 19 |
nicklas |
1527 |
else |
5279 |
05 Feb 19 |
nicklas |
1528 |
{ |
5279 |
05 Feb 19 |
nicklas |
// A case that was matched between the database and the INCA file |
5279 |
05 Feb 19 |
nicklas |
1530 |
mappedCases.add(s.getName()); |
5279 |
05 Feb 19 |
nicklas |
1531 |
} |
5273 |
31 Jan 19 |
nicklas |
1532 |
} |
5279 |
05 Feb 19 |
nicklas |
1533 |
|
5292 |
12 Feb 19 |
nicklas |
1534 |
if (pair.hasUnmappedCase() && !forStatistics) |
5279 |
05 Feb 19 |
nicklas |
1535 |
{ |
5279 |
05 Feb 19 |
nicklas |
1536 |
bloodQuery.setEntityParameter("patient", pat); |
5279 |
05 Feb 19 |
nicklas |
1537 |
for (Sample b : bloodQuery.list(dc)) |
5279 |
05 Feb 19 |
nicklas |
1538 |
{ |
5279 |
05 Feb 19 |
nicklas |
1539 |
if (!mappedCases.contains(b.getName().substring(0, 7))) |
5279 |
05 Feb 19 |
nicklas |
1540 |
{ |
6134 |
16 Feb 21 |
nicklas |
1541 |
String consentDate = Values.getString(Reggie.CONVERTER_DATE_TO_STRING_WITH_SEPARATOR.convert( |
6134 |
16 Feb 21 |
nicklas |
1542 |
(Date)Annotationtype.CONSENT_DATE.getAnnotationValue(dc, b)), "missing"); |
6134 |
16 Feb 21 |
nicklas |
1543 |
unmappedItems.add(b.getName() + " (BLOOD; Consent=" + consentDate + ")"); |
5279 |
05 Feb 19 |
nicklas |
1544 |
} |
5279 |
05 Feb 19 |
nicklas |
1545 |
} |
5279 |
05 Feb 19 |
nicklas |
1546 |
pair.setUnmappedItems(unmappedItems); |
5279 |
05 Feb 19 |
nicklas |
1547 |
} |
5273 |
31 Jan 19 |
nicklas |
1548 |
} |
5273 |
31 Jan 19 |
nicklas |
1549 |
|
5273 |
31 Jan 19 |
nicklas |
// Progress reporting |
5273 |
31 Jan 19 |
nicklas |
1551 |
if (count % 100 == 0) |
5273 |
31 Jan 19 |
nicklas |
1552 |
{ |
5273 |
31 Jan 19 |
nicklas |
1553 |
progress.display((100 * count) / numPatients, "Patient and case mapping... (" + count + " of " + numPatients + ")"); |
5273 |
31 Jan 19 |
nicklas |
1554 |
} |
5273 |
31 Jan 19 |
nicklas |
1555 |
} |
5273 |
31 Jan 19 |
nicklas |
1556 |
|
5273 |
31 Jan 19 |
nicklas |
// All IncaLines without a patientId and caseId need to be excluded now |
5292 |
12 Feb 19 |
nicklas |
1558 |
if (!forStatistics) |
5267 |
25 Jan 19 |
nicklas |
1559 |
{ |
5292 |
12 Feb 19 |
nicklas |
1560 |
for (IncaLine line : lines) |
5267 |
25 Jan 19 |
nicklas |
1561 |
{ |
5292 |
12 Feb 19 |
nicklas |
1562 |
if (line.exclude != null) continue; |
5292 |
12 Feb 19 |
nicklas |
1563 |
|
5292 |
12 Feb 19 |
nicklas |
1564 |
if (line.patientId == 0) |
5292 |
12 Feb 19 |
nicklas |
1565 |
{ |
5292 |
12 Feb 19 |
nicklas |
1566 |
line.exclude = ExcludedLine.PATIENT_NOTIN_DATABASE; |
5292 |
12 Feb 19 |
nicklas |
1567 |
} |
5292 |
12 Feb 19 |
nicklas |
1568 |
else if (line.caseId == 0) |
5292 |
12 Feb 19 |
nicklas |
1569 |
{ |
5292 |
12 Feb 19 |
nicklas |
1570 |
line.exclude = ExcludedLine.CASE_NOTIN_DATABASE; |
5292 |
12 Feb 19 |
nicklas |
1571 |
} |
5273 |
31 Jan 19 |
nicklas |
1572 |
} |
5273 |
31 Jan 19 |
nicklas |
1573 |
} |
5273 |
31 Jan 19 |
nicklas |
1574 |
|
5273 |
31 Jan 19 |
nicklas |
1575 |
progress.display(100, "Patient and case mapping... (" + count + " of " + numPatients + ")"); |
5273 |
31 Jan 19 |
nicklas |
1576 |
} |
5273 |
31 Jan 19 |
nicklas |
1577 |
|
5273 |
31 Jan 19 |
nicklas |
1578 |
/** |
5273 |
31 Jan 19 |
nicklas |
Import INCA annotations in the database. All lines must have been fully checked and |
5273 |
31 Jan 19 |
nicklas |
mapped to the database before this method can be called. |
5273 |
31 Jan 19 |
nicklas |
1581 |
*/ |
5273 |
31 Jan 19 |
nicklas |
1582 |
void doImport(DbControl dc, ProgressReporter progress) |
5273 |
31 Jan 19 |
nicklas |
1583 |
{ |
5273 |
31 Jan 19 |
nicklas |
// Load annotation types |
5273 |
31 Jan 19 |
nicklas |
1585 |
AnnotationType atIncaExportDate = Annotationtype.INCA_EXPORT_DATE.load(dc); |
5273 |
31 Jan 19 |
nicklas |
1586 |
AnnotationType atIncaImportDate = Annotationtype.INCA_IMPORT_DATE.load(dc); |
5273 |
31 Jan 19 |
nicklas |
1587 |
AnnotationType refDate = Annotationtype.REFERENCE_DATE.load(dc); |
5273 |
31 Jan 19 |
nicklas |
1588 |
AnnotationType refDateSource = Annotationtype.REFERENCE_DATE_SOURCE.load(dc); |
5273 |
31 Jan 19 |
nicklas |
1589 |
AnnotationType incaDiaDat = Annotationtype.INCA2_a_diag_dat.load(dc); |
5273 |
31 Jan 19 |
nicklas |
1590 |
|
5273 |
31 Jan 19 |
nicklas |
// Re-create INCA annotation types using new DbControl |
5273 |
31 Jan 19 |
nicklas |
1592 |
List<AnnotationType> annotationTypes = new ArrayList<AnnotationType>(); |
5273 |
31 Jan 19 |
nicklas |
1593 |
for (Header h : headers) |
5273 |
31 Jan 19 |
nicklas |
1594 |
{ |
5273 |
31 Jan 19 |
nicklas |
1595 |
if (h.annotationType != null) |
5273 |
31 Jan 19 |
nicklas |
1596 |
{ |
5273 |
31 Jan 19 |
nicklas |
1597 |
h.annotationType = AnnotationType.getById(dc, h.annotationType.getId()); |
5273 |
31 Jan 19 |
nicklas |
1598 |
annotationTypes.add(h.annotationType); |
5273 |
31 Jan 19 |
nicklas |
1599 |
} |
5273 |
31 Jan 19 |
nicklas |
1600 |
} |
5273 |
31 Jan 19 |
nicklas |
1601 |
annotationTypes.add(atIncaExportDate); |
5273 |
31 Jan 19 |
nicklas |
1602 |
annotationTypes.add(atIncaImportDate); |
5273 |
31 Jan 19 |
nicklas |
1603 |
annotationTypes.add(refDate); |
5273 |
31 Jan 19 |
nicklas |
1604 |
annotationTypes.add(refDateSource); |
5273 |
31 Jan 19 |
nicklas |
1605 |
|
5273 |
31 Jan 19 |
nicklas |
// Use AnnotationBatcher in order to decrease heap memory use and increase commit speed |
5273 |
31 Jan 19 |
nicklas |
1607 |
AnnotationBatcher batcher = new AnnotationBatcher(dc, Item.SAMPLE); |
5273 |
31 Jan 19 |
nicklas |
1608 |
batcher.addAnnotationTypes(annotationTypes); |
5273 |
31 Jan 19 |
nicklas |
1609 |
|
5273 |
31 Jan 19 |
nicklas |
1610 |
int casesToCheck = lines.size() - totalExcludedLines; |
5273 |
31 Jan 19 |
nicklas |
1611 |
|
5273 |
31 Jan 19 |
nicklas |
1612 |
for (IncaLine line : lines) |
5273 |
31 Jan 19 |
nicklas |
1613 |
{ |
5273 |
31 Jan 19 |
nicklas |
1614 |
if (line.exclude != null) continue; |
5273 |
31 Jan 19 |
nicklas |
1615 |
|
5273 |
31 Jan 19 |
nicklas |
1616 |
numCasesChecked++; |
5273 |
31 Jan 19 |
nicklas |
1617 |
if (numCasesChecked % 100 == 0) |
5273 |
31 Jan 19 |
nicklas |
1618 |
{ |
5273 |
31 Jan 19 |
nicklas |
1619 |
progress.display(100 * numCasesChecked / casesToCheck, |
5273 |
31 Jan 19 |
nicklas |
1620 |
"Importing... (" + numCasesChecked + " of " + casesToCheck + ")"); |
5273 |
31 Jan 19 |
nicklas |
1621 |
} |
5273 |
31 Jan 19 |
nicklas |
1622 |
|
5273 |
31 Jan 19 |
nicklas |
1623 |
Sample theCase = Sample.getById(dc, line.caseId); |
5273 |
31 Jan 19 |
nicklas |
1624 |
batcher.setCurrentItem(theCase); |
5273 |
31 Jan 19 |
nicklas |
1625 |
|
5273 |
31 Jan 19 |
nicklas |
1626 |
boolean entryChanged = false; |
5273 |
31 Jan 19 |
nicklas |
1627 |
for (Header h : headers) |
5273 |
31 Jan 19 |
nicklas |
1628 |
{ |
5273 |
31 Jan 19 |
nicklas |
1629 |
if (h.annotationType == null) continue; |
5267 |
25 Jan 19 |
nicklas |
1630 |
|
5273 |
31 Jan 19 |
nicklas |
1631 |
Object value = line.data[h.index]; |
5273 |
31 Jan 19 |
nicklas |
1632 |
Change change = batcher.setValue(h.annotationType, value, null, false); |
5267 |
25 Jan 19 |
nicklas |
1633 |
|
5273 |
31 Jan 19 |
nicklas |
1634 |
numAnnotations[change.ordinal()]++; |
5273 |
31 Jan 19 |
nicklas |
1635 |
if (change != Change.NO_CHANGE) |
5267 |
25 Jan 19 |
nicklas |
1636 |
{ |
5273 |
31 Jan 19 |
nicklas |
1637 |
entryChanged = true; |
5273 |
31 Jan 19 |
nicklas |
1638 |
|
5273 |
31 Jan 19 |
nicklas |
// We always update the ReferenceDate annotation if the a_diag_dat annotation has changed |
5273 |
31 Jan 19 |
nicklas |
1640 |
if (h.annotationType.equals(incaDiaDat)) |
5267 |
25 Jan 19 |
nicklas |
1641 |
{ |
5273 |
31 Jan 19 |
nicklas |
1642 |
batcher.setValue(refDate, value, null, false); |
5273 |
31 Jan 19 |
nicklas |
1643 |
batcher.setValue(refDateSource, ReferenceDateSource.INCA_DIAGNOSIS_DATE.getTitle(), null, false); |
5267 |
25 Jan 19 |
nicklas |
1644 |
} |
5267 |
25 Jan 19 |
nicklas |
1645 |
} |
5267 |
25 Jan 19 |
nicklas |
1646 |
} |
5273 |
31 Jan 19 |
nicklas |
1647 |
if (entryChanged) numCasesUpdated++; |
5273 |
31 Jan 19 |
nicklas |
1648 |
|
5273 |
31 Jan 19 |
nicklas |
// Update INCA export and import dates |
5273 |
31 Jan 19 |
nicklas |
1650 |
batcher.setValue(atIncaExportDate, exportDate, null, false); |
5287 |
08 Feb 19 |
nicklas |
1651 |
batcher.setValue(atIncaImportDate, startDate, null, false); |
5273 |
31 Jan 19 |
nicklas |
1652 |
} |
5273 |
31 Jan 19 |
nicklas |
1653 |
} |
5273 |
31 Jan 19 |
nicklas |
1654 |
|
5290 |
11 Feb 19 |
nicklas |
1655 |
void doStatistics(StatisticalVariable... variables) |
5290 |
11 Feb 19 |
nicklas |
1656 |
{ |
5290 |
11 Feb 19 |
nicklas |
1657 |
for (StatisticalVariable v : variables) |
5290 |
11 Feb 19 |
nicklas |
1658 |
{ |
5290 |
11 Feb 19 |
nicklas |
1659 |
statistics.add(v); |
5290 |
11 Feb 19 |
nicklas |
1660 |
} |
5290 |
11 Feb 19 |
nicklas |
1661 |
for (IncaLine line : lines) |
5290 |
11 Feb 19 |
nicklas |
1662 |
{ |
5290 |
11 Feb 19 |
nicklas |
1663 |
if (line.exclude != null) continue; |
5290 |
11 Feb 19 |
nicklas |
1664 |
|
5290 |
11 Feb 19 |
nicklas |
1665 |
for (StatisticalVariable v : variables) |
5290 |
11 Feb 19 |
nicklas |
1666 |
{ |
5290 |
11 Feb 19 |
nicklas |
1667 |
v.update(line); |
5290 |
11 Feb 19 |
nicklas |
1668 |
} |
5290 |
11 Feb 19 |
nicklas |
1669 |
} |
5290 |
11 Feb 19 |
nicklas |
1670 |
} |
5290 |
11 Feb 19 |
nicklas |
1671 |
|
5273 |
31 Jan 19 |
nicklas |
1672 |
/** |
5273 |
31 Jan 19 |
nicklas |
Check all lines that have been excluded and summarize this |
5273 |
31 Jan 19 |
nicklas |
in the 'excludedLines' array and 'totalExcludedLines'. |
5273 |
31 Jan 19 |
nicklas |
1675 |
*/ |
5273 |
31 Jan 19 |
nicklas |
1676 |
void summarizeExcludedLines() |
5273 |
31 Jan 19 |
nicklas |
1677 |
{ |
5273 |
31 Jan 19 |
nicklas |
// Reset to 0 |
5273 |
31 Jan 19 |
nicklas |
1679 |
totalExcludedLines = 0; |
5273 |
31 Jan 19 |
nicklas |
1680 |
for (ExcludedLine ex : ExcludedLine.values()) |
5273 |
31 Jan 19 |
nicklas |
1681 |
{ |
5273 |
31 Jan 19 |
nicklas |
1682 |
excludedLineCount[ex.ordinal()] = 0; |
5273 |
31 Jan 19 |
nicklas |
1683 |
} |
5273 |
31 Jan 19 |
nicklas |
1684 |
|
5273 |
31 Jan 19 |
nicklas |
1685 |
for (IncaLine line : lines) |
5273 |
31 Jan 19 |
nicklas |
1686 |
{ |
5273 |
31 Jan 19 |
nicklas |
1687 |
if (line.exclude != null) |
5267 |
25 Jan 19 |
nicklas |
1688 |
{ |
5273 |
31 Jan 19 |
nicklas |
1689 |
excludedLineCount[line.exclude.ordinal()]++; |
5273 |
31 Jan 19 |
nicklas |
1690 |
totalExcludedLines++; |
5267 |
25 Jan 19 |
nicklas |
1691 |
} |
5267 |
25 Jan 19 |
nicklas |
1692 |
} |
5267 |
25 Jan 19 |
nicklas |
1693 |
} |
5267 |
25 Jan 19 |
nicklas |
1694 |
|
5273 |
31 Jan 19 |
nicklas |
1695 |
/** |
5273 |
31 Jan 19 |
nicklas |
Helper method for counting excluded lines. |
5273 |
31 Jan 19 |
nicklas |
1697 |
*/ |
5273 |
31 Jan 19 |
nicklas |
1698 |
int sumExcludedLines(ExcludedLine... options) |
5273 |
31 Jan 19 |
nicklas |
1699 |
{ |
5273 |
31 Jan 19 |
nicklas |
1700 |
int sum = 0; |
5273 |
31 Jan 19 |
nicklas |
1701 |
for (ExcludedLine el : options) |
5273 |
31 Jan 19 |
nicklas |
1702 |
{ |
5273 |
31 Jan 19 |
nicklas |
1703 |
sum += excludedLineCount[el.ordinal()]; |
5273 |
31 Jan 19 |
nicklas |
1704 |
} |
5273 |
31 Jan 19 |
nicklas |
1705 |
return sum; |
5273 |
31 Jan 19 |
nicklas |
1706 |
} |
5273 |
31 Jan 19 |
nicklas |
1707 |
|
5263 |
24 Jan 19 |
nicklas |
1708 |
JSONObject getFileInfoInJSON(JSONObject json) |
5263 |
24 Jan 19 |
nicklas |
1709 |
{ |
5263 |
24 Jan 19 |
nicklas |
1710 |
if (json == null) json = new JSONObject(); |
5263 |
24 Jan 19 |
nicklas |
1711 |
json.put("filename", filename); |
5263 |
24 Jan 19 |
nicklas |
1712 |
json.put("isFollowup", isFollowUp); |
5287 |
08 Feb 19 |
nicklas |
1713 |
json.put("startDate", Reggie.CONVERTER_DATETIME_TO_STRING.convert(startDate)); |
5287 |
08 Feb 19 |
nicklas |
1714 |
json.put("endDate", Reggie.CONVERTER_DATETIME_TO_STRING.convert(endDate)); |
5273 |
31 Jan 19 |
nicklas |
1715 |
json.put("exportDate", Reggie.CONVERTER_DATE_TO_STRING.convert(exportDate)); |
5262 |
24 Jan 19 |
nicklas |
1716 |
|
5273 |
31 Jan 19 |
nicklas |
1717 |
JSONObject jsonImport = new JSONObject(); |
5273 |
31 Jan 19 |
nicklas |
1718 |
jsonImport.put("numCasesChecked", numCasesChecked); |
5273 |
31 Jan 19 |
nicklas |
1719 |
jsonImport.put("numCasesUpdated", numCasesUpdated); |
5273 |
31 Jan 19 |
nicklas |
1720 |
for (AnnotationBatcher.Change c : AnnotationBatcher.Change.values()) |
5273 |
31 Jan 19 |
nicklas |
1721 |
{ |
5273 |
31 Jan 19 |
nicklas |
1722 |
jsonImport.put(c.name(), numAnnotations[c.ordinal()]); |
5273 |
31 Jan 19 |
nicklas |
1723 |
} |
5273 |
31 Jan 19 |
nicklas |
1724 |
json.put("import", jsonImport); |
5273 |
31 Jan 19 |
nicklas |
1725 |
|
5263 |
24 Jan 19 |
nicklas |
// Information about headers in the selected file |
5263 |
24 Jan 19 |
nicklas |
1727 |
JSONObject jsonHeaders = new JSONObject(); |
5269 |
28 Jan 19 |
nicklas |
1728 |
jsonHeaders.put("count", headers.length); |
5263 |
24 Jan 19 |
nicklas |
1729 |
jsonHeaders.put("missing", missingHeaders.size()); |
5288 |
08 Feb 19 |
nicklas |
1730 |
jsonHeaders.put("missingAnnotationTypes", missingAnnotationTypes.size()); |
5263 |
24 Jan 19 |
nicklas |
1731 |
jsonHeaders.put("duplicates", duplicateHeaders.size()); |
5263 |
24 Jan 19 |
nicklas |
1732 |
jsonHeaders.put("unknown", unknownHeaders.size()); |
5274 |
31 Jan 19 |
nicklas |
1733 |
jsonHeaders.put("mapped", mappedHeaders.size()); |
5268 |
25 Jan 19 |
nicklas |
1734 |
jsonHeaders.put("unmapped", unmappedHeaders.size()); |
5263 |
24 Jan 19 |
nicklas |
1735 |
json.put("headers", jsonHeaders); |
5263 |
24 Jan 19 |
nicklas |
1736 |
|
5263 |
24 Jan 19 |
nicklas |
1737 |
if (missingHeaders.size() > 0) |
5263 |
24 Jan 19 |
nicklas |
1738 |
{ |
5263 |
24 Jan 19 |
nicklas |
1739 |
jsonHeaders.put("missingNames", JsonUtil.toArray(missingHeaders, new IdentityConverter<>())); |
5263 |
24 Jan 19 |
nicklas |
1740 |
} |
5288 |
08 Feb 19 |
nicklas |
1741 |
if (missingAnnotationTypes.size() > 0) |
5288 |
08 Feb 19 |
nicklas |
1742 |
{ |
5288 |
08 Feb 19 |
nicklas |
1743 |
jsonHeaders.put("missingAnnotationTypeNames", JsonUtil.toArray(missingAnnotationTypes, new IdentityConverter<>())); |
5288 |
08 Feb 19 |
nicklas |
1744 |
} |
5263 |
24 Jan 19 |
nicklas |
1745 |
if (duplicateHeaders.size() > 0) |
5263 |
24 Jan 19 |
nicklas |
1746 |
{ |
5263 |
24 Jan 19 |
nicklas |
1747 |
jsonHeaders.put("duplicateNames", JsonUtil.toArray(duplicateHeaders, new IdentityConverter<>())); |
5263 |
24 Jan 19 |
nicklas |
1748 |
} |
5274 |
31 Jan 19 |
nicklas |
1749 |
if (unknownHeaders.size() > 0) |
5274 |
31 Jan 19 |
nicklas |
1750 |
{ |
5274 |
31 Jan 19 |
nicklas |
1751 |
jsonHeaders.put("unknownNames", JsonUtil.toArray(unknownHeaders, new IdentityConverter<>())); |
5274 |
31 Jan 19 |
nicklas |
1752 |
} |
5274 |
31 Jan 19 |
nicklas |
1753 |
if (mappedHeaders.size() > 0) |
5274 |
31 Jan 19 |
nicklas |
1754 |
{ |
5274 |
31 Jan 19 |
nicklas |
1755 |
jsonHeaders.put("mappedNames", JsonUtil.toArray(mappedHeaders, new IdentityConverter<>())); |
5274 |
31 Jan 19 |
nicklas |
1756 |
} |
5268 |
25 Jan 19 |
nicklas |
1757 |
if (unmappedHeaders.size() > 0) |
5268 |
25 Jan 19 |
nicklas |
1758 |
{ |
5268 |
25 Jan 19 |
nicklas |
1759 |
jsonHeaders.put("unmappedNames", JsonUtil.toArray(unmappedHeaders, new IdentityConverter<>())); |
5268 |
25 Jan 19 |
nicklas |
1760 |
} |
5263 |
24 Jan 19 |
nicklas |
1761 |
|
5263 |
24 Jan 19 |
nicklas |
// Information about data lines in the selected file |
5263 |
24 Jan 19 |
nicklas |
1763 |
JSONObject jsonData = new JSONObject(); |
5263 |
24 Jan 19 |
nicklas |
1764 |
jsonData.put("count", lines.size()); |
5263 |
24 Jan 19 |
nicklas |
1765 |
for (ExcludedLine ex : ExcludedLine.values()) |
5263 |
24 Jan 19 |
nicklas |
1766 |
{ |
5263 |
24 Jan 19 |
nicklas |
1767 |
jsonData.put(ex.name(), excludedLineCount[ex.ordinal()]); |
5263 |
24 Jan 19 |
nicklas |
1768 |
} |
5273 |
31 Jan 19 |
nicklas |
1769 |
jsonData.put("totalExcluded", totalExcludedLines); |
5263 |
24 Jan 19 |
nicklas |
1770 |
json.put("data", jsonData); |
5263 |
24 Jan 19 |
nicklas |
1771 |
|
5291 |
11 Feb 19 |
nicklas |
1772 |
if (forStatistics) |
5291 |
11 Feb 19 |
nicklas |
1773 |
{ |
5291 |
11 Feb 19 |
nicklas |
// Statistics |
5291 |
11 Feb 19 |
nicklas |
1775 |
JSONArray jsonStatistics = new JSONArray(); |
5291 |
11 Feb 19 |
nicklas |
1776 |
json.put("statistics", jsonStatistics); |
5291 |
11 Feb 19 |
nicklas |
1777 |
for (StatisticalVariable var : statistics) |
5291 |
11 Feb 19 |
nicklas |
1778 |
{ |
5291 |
11 Feb 19 |
nicklas |
1779 |
JSONObject jsonVar = new JSONObject(); |
5291 |
11 Feb 19 |
nicklas |
1780 |
jsonStatistics.add(jsonVar); |
5291 |
11 Feb 19 |
nicklas |
1781 |
jsonVar.put("name", var.name); |
5292 |
12 Feb 19 |
nicklas |
1782 |
jsonVar.put("unit", var.unit); |
5302 |
15 Feb 19 |
nicklas |
1783 |
jsonVar.put("hasNaGroup", var.hasNaGroup); |
5302 |
15 Feb 19 |
nicklas |
1784 |
|
5302 |
15 Feb 19 |
nicklas |
1785 |
JSONObject jsonNotNa = new JSONObject(); |
5302 |
15 Feb 19 |
nicklas |
1786 |
jsonNotNa.put("total", var.totalNotNa.numTotal); |
5302 |
15 Feb 19 |
nicklas |
1787 |
jsonNotNa.put("accruedSpecimen", var.totalNotNa.numAccruedSpecimen); |
5302 |
15 Feb 19 |
nicklas |
1788 |
jsonNotNa.put("accruedNoSpecimen", var.totalNotNa.numAccruedNoSpecimen); |
5302 |
15 Feb 19 |
nicklas |
1789 |
jsonNotNa.put("notAccrued", var.totalNotNa.numNotAccrued); |
5302 |
15 Feb 19 |
nicklas |
1790 |
jsonVar.put("notNa", jsonNotNa); |
5302 |
15 Feb 19 |
nicklas |
1791 |
|
5291 |
11 Feb 19 |
nicklas |
1792 |
JSONArray jsonGroups = new JSONArray(); |
5291 |
11 Feb 19 |
nicklas |
1793 |
jsonVar.put("groups", jsonGroups); |
5291 |
11 Feb 19 |
nicklas |
1794 |
for (StatisticalGroup grp : var.groups) |
5291 |
11 Feb 19 |
nicklas |
1795 |
{ |
5291 |
11 Feb 19 |
nicklas |
1796 |
JSONObject jsonGrp = new JSONObject(); |
5291 |
11 Feb 19 |
nicklas |
1797 |
jsonGrp.put("name", grp.name); |
5302 |
15 Feb 19 |
nicklas |
1798 |
jsonGrp.put("isNa", grp.isNa); |
5291 |
11 Feb 19 |
nicklas |
1799 |
jsonGrp.put("total", grp.numTotal); |
5292 |
12 Feb 19 |
nicklas |
1800 |
jsonGrp.put("accruedSpecimen", grp.numAccruedSpecimen); |
5291 |
11 Feb 19 |
nicklas |
1801 |
jsonGrp.put("accruedNoSpecimen", grp.numAccruedNoSpecimen); |
5291 |
11 Feb 19 |
nicklas |
1802 |
jsonGrp.put("notAccrued", grp.numNotAccrued); |
5291 |
11 Feb 19 |
nicklas |
1803 |
jsonGroups.add(jsonGrp); |
5291 |
11 Feb 19 |
nicklas |
1804 |
} |
5291 |
11 Feb 19 |
nicklas |
1805 |
} |
5291 |
11 Feb 19 |
nicklas |
1806 |
} |
5291 |
11 Feb 19 |
nicklas |
1807 |
|
5263 |
24 Jan 19 |
nicklas |
1808 |
return json; |
5262 |
24 Jan 19 |
nicklas |
1809 |
} |
5268 |
25 Jan 19 |
nicklas |
1810 |
|
3836 |
12 Apr 16 |
olle |
1811 |
} |
3836 |
12 Apr 16 |
olle |
1812 |
|
5269 |
28 Jan 19 |
nicklas |
1813 |
/** |
5269 |
28 Jan 19 |
nicklas |
Represents a header column in the INCA file. We use it to |
5269 |
28 Jan 19 |
nicklas |
store information and mapping for the column. |
5269 |
28 Jan 19 |
nicklas |
1816 |
*/ |
5260 |
22 Jan 19 |
nicklas |
1817 |
static class Header |
5260 |
22 Jan 19 |
nicklas |
1818 |
{ |
5269 |
28 Jan 19 |
nicklas |
// The column index of the header, 0-based |
5260 |
22 Jan 19 |
nicklas |
1820 |
final int index; |
5466 |
04 Jun 19 |
nicklas |
// The column number, 1-based |
5466 |
04 Jun 19 |
nicklas |
1822 |
final int colNo; |
5269 |
28 Jan 19 |
nicklas |
// The name of the column as it appears in the file |
5260 |
22 Jan 19 |
nicklas |
1824 |
final String name; |
5269 |
28 Jan 19 |
nicklas |
// Set if this column is one of the pre-defined KeyColumns:s |
5260 |
22 Jan 19 |
nicklas |
1826 |
final KeyColumn keyColumn; |
5269 |
28 Jan 19 |
nicklas |
// Set if the column has been mapped to an INCA2 annotation type |
5269 |
28 Jan 19 |
nicklas |
// see IncaFile#mapAnnotationColumns |
5260 |
22 Jan 19 |
nicklas |
1829 |
AnnotationType annotationType; |
5260 |
22 Jan 19 |
nicklas |
1830 |
|
5260 |
22 Jan 19 |
nicklas |
1831 |
Header(int index, String name) |
5260 |
22 Jan 19 |
nicklas |
1832 |
{ |
5260 |
22 Jan 19 |
nicklas |
1833 |
this.index = index; |
5466 |
04 Jun 19 |
nicklas |
1834 |
this.colNo = index+1; |
5260 |
22 Jan 19 |
nicklas |
1835 |
this.name = name; |
5260 |
22 Jan 19 |
nicklas |
1836 |
this.keyColumn = KeyColumn.find(name); |
5260 |
22 Jan 19 |
nicklas |
1837 |
} |
5260 |
22 Jan 19 |
nicklas |
1838 |
} |
5260 |
22 Jan 19 |
nicklas |
1839 |
|
3836 |
12 Apr 16 |
olle |
1840 |
/** |
5260 |
22 Jan 19 |
nicklas |
Pre-defined key columns in the file that are of special interest. |
5258 |
22 Jan 19 |
nicklas |
1842 |
*/ |
5260 |
22 Jan 19 |
nicklas |
1843 |
static enum KeyColumn |
5258 |
22 Jan 19 |
nicklas |
1844 |
{ |
5262 |
24 Jan 19 |
nicklas |
1845 |
PAT_ID("PAT_ID", "PATID"), |
5262 |
24 Jan 19 |
nicklas |
1846 |
PERSONAL_NO("PersonalNo", "PERSNR"), |
5262 |
24 Jan 19 |
nicklas |
1847 |
LATERALITY("a_pat_sida_Beskrivning", "u_pat_sida_Beskrivning"), |
5258 |
22 Jan 19 |
nicklas |
1848 |
FOLLOWUP_DATE("u_dat"), |
5258 |
22 Jan 19 |
nicklas |
1849 |
CANCER_TYPE("op_pad_invasiv_Värde"), |
5258 |
22 Jan 19 |
nicklas |
1850 |
A_DIAG_DAT("a_diag_dat"), |
5258 |
22 Jan 19 |
nicklas |
1851 |
OP_KIR_DAT("op_kir_dat"), |
5258 |
22 Jan 19 |
nicklas |
1852 |
ER_STATUS("op_pad_er_Värde"), |
5258 |
22 Jan 19 |
nicklas |
1853 |
HER2_STATUS("op_pad_her2ish_Värde"), |
5258 |
22 Jan 19 |
nicklas |
1854 |
NHG_STATUS("op_pad_nhg_Värde"), |
5258 |
22 Jan 19 |
nicklas |
1855 |
PGR_STATUS("op_pad_pr_Värde"), |
5258 |
22 Jan 19 |
nicklas |
1856 |
AGE("a_pat_alder"), |
5314 |
28 Feb 19 |
nicklas |
1857 |
TUMOR_SIZE("op_pad_invstl"), |
6134 |
16 Feb 21 |
nicklas |
1858 |
A_INR_SJHKOD("a_inr_sjhkod"), |
6134 |
16 Feb 21 |
nicklas |
1859 |
A_DIAG_BESDAT("a_diag_besdat"), |
6134 |
16 Feb 21 |
nicklas |
1860 |
A_PAD_PREPNR("a_pad_prepnr"), |
6134 |
16 Feb 21 |
nicklas |
1861 |
A_PAD_PREPAR("a_pad_prepar"), |
6134 |
16 Feb 21 |
nicklas |
1862 |
OP_PAD_PREPNR("op_pad_prepnr"), |
6134 |
16 Feb 21 |
nicklas |
1863 |
OP_PAD_PREPAR("op_pad_prepar") |
5258 |
22 Jan 19 |
nicklas |
1864 |
; |
5258 |
22 Jan 19 |
nicklas |
1865 |
|
5258 |
22 Jan 19 |
nicklas |
1866 |
private final String key; |
5262 |
24 Jan 19 |
nicklas |
1867 |
private final String alternate; |
5262 |
24 Jan 19 |
nicklas |
1868 |
|
5260 |
22 Jan 19 |
nicklas |
1869 |
KeyColumn(String key) |
5258 |
22 Jan 19 |
nicklas |
1870 |
{ |
5262 |
24 Jan 19 |
nicklas |
1871 |
this(key, null); |
5262 |
24 Jan 19 |
nicklas |
1872 |
} |
5262 |
24 Jan 19 |
nicklas |
1873 |
|
5262 |
24 Jan 19 |
nicklas |
1874 |
KeyColumn(String key, String alternate) |
5262 |
24 Jan 19 |
nicklas |
1875 |
{ |
5258 |
22 Jan 19 |
nicklas |
1876 |
this.key = key; |
5262 |
24 Jan 19 |
nicklas |
1877 |
this.alternate = alternate; |
5269 |
28 Jan 19 |
nicklas |
1878 |
} |
5260 |
22 Jan 19 |
nicklas |
1879 |
|
5269 |
28 Jan 19 |
nicklas |
1880 |
/** |
5269 |
28 Jan 19 |
nicklas |
Find a KeyColumn based on the name (from the INCA file). |
5269 |
28 Jan 19 |
nicklas |
It will match both primary and alternate names for the column. |
5269 |
28 Jan 19 |
nicklas |
1883 |
*/ |
5269 |
28 Jan 19 |
nicklas |
1884 |
static KeyColumn find(String name) |
5260 |
22 Jan 19 |
nicklas |
1885 |
{ |
5269 |
28 Jan 19 |
nicklas |
1886 |
if (name == null) return null; |
5260 |
22 Jan 19 |
nicklas |
1887 |
for (KeyColumn keyCol : values()) |
5260 |
22 Jan 19 |
nicklas |
1888 |
{ |
5269 |
28 Jan 19 |
nicklas |
1889 |
if (name.equals(keyCol.key) || name.equals(keyCol.alternate)) |
5260 |
22 Jan 19 |
nicklas |
1890 |
{ |
5260 |
22 Jan 19 |
nicklas |
1891 |
return keyCol; |
5260 |
22 Jan 19 |
nicklas |
1892 |
} |
5260 |
22 Jan 19 |
nicklas |
1893 |
} |
5260 |
22 Jan 19 |
nicklas |
1894 |
return null; |
5260 |
22 Jan 19 |
nicklas |
1895 |
} |
5258 |
22 Jan 19 |
nicklas |
1896 |
} |
5258 |
22 Jan 19 |
nicklas |
1897 |
|
5269 |
28 Jan 19 |
nicklas |
1898 |
/** |
5269 |
28 Jan 19 |
nicklas |
Represents a single data line from an INCA file. We store the line |
5269 |
28 Jan 19 |
nicklas |
number and data split into columns. Some important values are extracted |
5269 |
28 Jan 19 |
nicklas |
by the {@link #checkColumns(IncaFile)} method. A line may be 'excluded' |
5269 |
28 Jan 19 |
nicklas |
for several reasons. |
5269 |
28 Jan 19 |
nicklas |
1903 |
*/ |
5261 |
23 Jan 19 |
nicklas |
1904 |
static class IncaLine |
5261 |
23 Jan 19 |
nicklas |
1905 |
{ |
5261 |
23 Jan 19 |
nicklas |
1906 |
final int lineNo; |
5261 |
23 Jan 19 |
nicklas |
1907 |
final String[] columns; |
5262 |
24 Jan 19 |
nicklas |
1908 |
|
5269 |
28 Jan 19 |
nicklas |
// Populated in #checkColumns method |
5275 |
31 Jan 19 |
nicklas |
1910 |
String patIdInca; |
5261 |
23 Jan 19 |
nicklas |
1911 |
String personalNo; |
5262 |
24 Jan 19 |
nicklas |
1912 |
String laterality; |
5262 |
24 Jan 19 |
nicklas |
1913 |
Date date; |
5261 |
23 Jan 19 |
nicklas |
1914 |
|
5269 |
28 Jan 19 |
nicklas |
// Populated after calling IncaFile.doDataCheck |
5267 |
25 Jan 19 |
nicklas |
1916 |
Object[] data; |
5267 |
25 Jan 19 |
nicklas |
1917 |
|
5269 |
28 Jan 19 |
nicklas |
// Populated after calling IncaFile.doDatabaseMapping |
5275 |
31 Jan 19 |
nicklas |
1919 |
String patientName; |
5267 |
25 Jan 19 |
nicklas |
1920 |
int patientId; |
5279 |
05 Feb 19 |
nicklas |
1921 |
String caseName; |
5267 |
25 Jan 19 |
nicklas |
1922 |
int caseId; |
5292 |
12 Feb 19 |
nicklas |
1923 |
boolean hasSpecimen; |
5267 |
25 Jan 19 |
nicklas |
1924 |
|
5279 |
05 Feb 19 |
nicklas |
1925 |
IncaLine otherCase; // The case with the other laterality for the same patient (if it exists) |
5279 |
05 Feb 19 |
nicklas |
1926 |
|
5279 |
05 Feb 19 |
nicklas |
// Populated if a case can't be mapped to an existing item in the database |
5279 |
05 Feb 19 |
nicklas |
// This set should include blood and case items for the given patient that |
5279 |
05 Feb 19 |
nicklas |
// was not mapped to any data line in the INCA file |
5279 |
05 Feb 19 |
nicklas |
1930 |
Set<String> unmappedItems; |
5279 |
05 Feb 19 |
nicklas |
1931 |
|
5263 |
24 Jan 19 |
nicklas |
1932 |
ExcludedLine exclude = null; |
5262 |
24 Jan 19 |
nicklas |
1933 |
|
5261 |
23 Jan 19 |
nicklas |
1934 |
IncaLine(int lineNo, String line) |
5261 |
23 Jan 19 |
nicklas |
1935 |
{ |
5261 |
23 Jan 19 |
nicklas |
1936 |
this.lineNo = lineNo; |
5467 |
04 Jun 19 |
nicklas |
1937 |
this.columns = line.split("\t", -1); |
5261 |
23 Jan 19 |
nicklas |
1938 |
} |
5261 |
23 Jan 19 |
nicklas |
1939 |
|
5261 |
23 Jan 19 |
nicklas |
1940 |
|
5269 |
28 Jan 19 |
nicklas |
1941 |
/** |
5269 |
28 Jan 19 |
nicklas |
Checks that the number of columns match the number of |
5269 |
28 Jan 19 |
nicklas |
headers in the INCA file and that required columns have a |
5269 |
28 Jan 19 |
nicklas |
value. |
5269 |
28 Jan 19 |
nicklas |
1945 |
*/ |
5269 |
28 Jan 19 |
nicklas |
1946 |
void checkColumns(IncaFile file) |
5261 |
23 Jan 19 |
nicklas |
1947 |
{ |
5269 |
28 Jan 19 |
nicklas |
1948 |
if (columns.length > file.headers.length) |
5263 |
24 Jan 19 |
nicklas |
1949 |
{ |
5263 |
24 Jan 19 |
nicklas |
1950 |
exclude = ExcludedLine.TOO_MANY_COLUMNS; |
5263 |
24 Jan 19 |
nicklas |
1951 |
return; |
5263 |
24 Jan 19 |
nicklas |
1952 |
} |
5269 |
28 Jan 19 |
nicklas |
1953 |
if (columns.length < file.headers.length) |
5263 |
24 Jan 19 |
nicklas |
1954 |
{ |
5263 |
24 Jan 19 |
nicklas |
1955 |
exclude = ExcludedLine.TOO_FEW_COLUMNS; |
5263 |
24 Jan 19 |
nicklas |
1956 |
return; |
5263 |
24 Jan 19 |
nicklas |
1957 |
} |
5262 |
24 Jan 19 |
nicklas |
1958 |
|
5269 |
28 Jan 19 |
nicklas |
// Decode values |
5269 |
28 Jan 19 |
nicklas |
1960 |
for (int i = 0; i < columns.length; i++) |
5269 |
28 Jan 19 |
nicklas |
1961 |
{ |
5269 |
28 Jan 19 |
nicklas |
1962 |
columns[i] = file.decoder.decode(columns[i]); |
5269 |
28 Jan 19 |
nicklas |
1963 |
} |
5269 |
28 Jan 19 |
nicklas |
1964 |
|
5275 |
31 Jan 19 |
nicklas |
1965 |
patIdInca = col(file.keyColumns[KeyColumn.PAT_ID.ordinal()]); |
5275 |
31 Jan 19 |
nicklas |
1966 |
|
5263 |
24 Jan 19 |
nicklas |
1967 |
personalNo = col(file.keyColumns[KeyColumn.PERSONAL_NO.ordinal()]); |
5263 |
24 Jan 19 |
nicklas |
1968 |
if (personalNo == null) |
5263 |
24 Jan 19 |
nicklas |
1969 |
{ |
5288 |
08 Feb 19 |
nicklas |
1970 |
if (!file.forStatistics) |
5288 |
08 Feb 19 |
nicklas |
1971 |
{ |
5288 |
08 Feb 19 |
nicklas |
// Exclude lines without personal number when importing but not in statistics |
5288 |
08 Feb 19 |
nicklas |
1973 |
exclude = ExcludedLine.MISSING_PERSONAL_NO; |
5288 |
08 Feb 19 |
nicklas |
1974 |
return; |
5288 |
08 Feb 19 |
nicklas |
1975 |
} |
5263 |
24 Jan 19 |
nicklas |
1976 |
} |
5288 |
08 Feb 19 |
nicklas |
1977 |
else |
5288 |
08 Feb 19 |
nicklas |
1978 |
{ |
5288 |
08 Feb 19 |
nicklas |
1979 |
personalNo = this.personalNo.replace("-", ""); |
5288 |
08 Feb 19 |
nicklas |
1980 |
} |
5262 |
24 Jan 19 |
nicklas |
1981 |
|
5267 |
25 Jan 19 |
nicklas |
1982 |
String tmp = col(file.keyColumns[KeyColumn.LATERALITY.ordinal()]); |
5267 |
25 Jan 19 |
nicklas |
1983 |
if ("Höger".equalsIgnoreCase(tmp)) |
5267 |
25 Jan 19 |
nicklas |
1984 |
{ |
5267 |
25 Jan 19 |
nicklas |
1985 |
laterality = "RIGHT"; |
5267 |
25 Jan 19 |
nicklas |
1986 |
} |
5267 |
25 Jan 19 |
nicklas |
1987 |
else if ("Vänster".equalsIgnoreCase(tmp)) |
5267 |
25 Jan 19 |
nicklas |
1988 |
{ |
5267 |
25 Jan 19 |
nicklas |
1989 |
laterality = "LEFT"; |
5267 |
25 Jan 19 |
nicklas |
1990 |
} |
5263 |
24 Jan 19 |
nicklas |
1991 |
if (laterality == null) |
5262 |
24 Jan 19 |
nicklas |
1992 |
{ |
5263 |
24 Jan 19 |
nicklas |
1993 |
exclude = ExcludedLine.MISSING_LATERALITY; |
5263 |
24 Jan 19 |
nicklas |
1994 |
return; |
5262 |
24 Jan 19 |
nicklas |
1995 |
} |
5262 |
24 Jan 19 |
nicklas |
1996 |
|
5263 |
24 Jan 19 |
nicklas |
1997 |
if (file.isFollowUp) |
5263 |
24 Jan 19 |
nicklas |
1998 |
{ |
5263 |
24 Jan 19 |
nicklas |
1999 |
date = Reggie.CONVERTER_STRING_WITH_SEPARATOR_TO_DATE.convert(col(file.keyColumns[KeyColumn.FOLLOWUP_DATE.ordinal()])); |
5263 |
24 Jan 19 |
nicklas |
2000 |
if (date == null) |
5263 |
24 Jan 19 |
nicklas |
2001 |
{ |
5263 |
24 Jan 19 |
nicklas |
2002 |
exclude = ExcludedLine.MISSING_FOLLOWUP_DATE; |
5263 |
24 Jan 19 |
nicklas |
2003 |
return; |
5263 |
24 Jan 19 |
nicklas |
2004 |
} |
5263 |
24 Jan 19 |
nicklas |
2005 |
} |
5261 |
23 Jan 19 |
nicklas |
2006 |
} |
5261 |
23 Jan 19 |
nicklas |
2007 |
|
5261 |
23 Jan 19 |
nicklas |
2008 |
private String col(int index) |
5261 |
23 Jan 19 |
nicklas |
2009 |
{ |
5273 |
31 Jan 19 |
nicklas |
2010 |
return index >= 0 && index < columns.length ? columns[index] : null; |
5261 |
23 Jan 19 |
nicklas |
2011 |
} |
5273 |
31 Jan 19 |
nicklas |
2012 |
} |
5273 |
31 Jan 19 |
nicklas |
2013 |
|
5279 |
05 Feb 19 |
nicklas |
2014 |
/** |
5279 |
05 Feb 19 |
nicklas |
Represents a pair of INCA data lines for the same patient. One |
5279 |
05 Feb 19 |
nicklas |
line should be the LEFT case and the other line the RIGHT case. |
5279 |
05 Feb 19 |
nicklas |
2017 |
*/ |
5273 |
31 Jan 19 |
nicklas |
2018 |
static class LinePair |
5273 |
31 Jan 19 |
nicklas |
2019 |
{ |
5273 |
31 Jan 19 |
nicklas |
2020 |
IncaLine leftCase; |
5273 |
31 Jan 19 |
nicklas |
2021 |
IncaLine rightCase; |
5261 |
23 Jan 19 |
nicklas |
2022 |
|
5279 |
05 Feb 19 |
nicklas |
2023 |
LinePair() |
5279 |
05 Feb 19 |
nicklas |
2024 |
{} |
5279 |
05 Feb 19 |
nicklas |
2025 |
|
5279 |
05 Feb 19 |
nicklas |
2026 |
/** |
5279 |
05 Feb 19 |
nicklas |
Sets the LEFT of RIGHT case. |
5279 |
05 Feb 19 |
nicklas |
2028 |
*/ |
5273 |
31 Jan 19 |
nicklas |
2029 |
void set(IncaLine line) |
5273 |
31 Jan 19 |
nicklas |
2030 |
{ |
5273 |
31 Jan 19 |
nicklas |
2031 |
if ("LEFT".equals(line.laterality)) |
5273 |
31 Jan 19 |
nicklas |
2032 |
{ |
5273 |
31 Jan 19 |
nicklas |
2033 |
leftCase = line; |
5273 |
31 Jan 19 |
nicklas |
2034 |
} |
6124 |
10 Feb 21 |
nicklas |
2035 |
else if ("RIGHT".equals(line.laterality)) |
5273 |
31 Jan 19 |
nicklas |
2036 |
{ |
5273 |
31 Jan 19 |
nicklas |
2037 |
rightCase = line; |
5273 |
31 Jan 19 |
nicklas |
2038 |
} |
5273 |
31 Jan 19 |
nicklas |
2039 |
} |
5273 |
31 Jan 19 |
nicklas |
2040 |
|
5273 |
31 Jan 19 |
nicklas |
2041 |
IncaLine get(String laterality) |
5273 |
31 Jan 19 |
nicklas |
2042 |
{ |
6124 |
10 Feb 21 |
nicklas |
2043 |
if ("LEFT".equals(laterality)) return leftCase; |
6124 |
10 Feb 21 |
nicklas |
2044 |
if ("RIGHT".equals(laterality)) return rightCase; |
6124 |
10 Feb 21 |
nicklas |
2045 |
return null; |
5273 |
31 Jan 19 |
nicklas |
2046 |
} |
5273 |
31 Jan 19 |
nicklas |
2047 |
|
5279 |
05 Feb 19 |
nicklas |
2048 |
/** |
5279 |
05 Feb 19 |
nicklas |
Add patient information to the lines. |
5279 |
05 Feb 19 |
nicklas |
@param patientId Internal ID of the Patient item in the database |
5279 |
05 Feb 19 |
nicklas |
@param patientName Name of the Patient item (=PATNNNN) |
5279 |
05 Feb 19 |
nicklas |
2052 |
*/ |
5279 |
05 Feb 19 |
nicklas |
2053 |
void mapPatient(int patientId, String patientName) |
5273 |
31 Jan 19 |
nicklas |
2054 |
{ |
5275 |
31 Jan 19 |
nicklas |
2055 |
if (leftCase != null) |
5275 |
31 Jan 19 |
nicklas |
2056 |
{ |
5275 |
31 Jan 19 |
nicklas |
2057 |
leftCase.patientId = patientId; |
5275 |
31 Jan 19 |
nicklas |
2058 |
leftCase.patientName = patientName; |
5275 |
31 Jan 19 |
nicklas |
2059 |
} |
5275 |
31 Jan 19 |
nicklas |
2060 |
if (rightCase != null) |
5275 |
31 Jan 19 |
nicklas |
2061 |
{ |
5275 |
31 Jan 19 |
nicklas |
2062 |
rightCase.patientId = patientId; |
5275 |
31 Jan 19 |
nicklas |
2063 |
rightCase.patientName = patientName; |
5275 |
31 Jan 19 |
nicklas |
2064 |
} |
5273 |
31 Jan 19 |
nicklas |
2065 |
} |
5273 |
31 Jan 19 |
nicklas |
2066 |
|
5279 |
05 Feb 19 |
nicklas |
2067 |
/** |
5279 |
05 Feb 19 |
nicklas |
Set case information to the matching line. Returns TRUE |
5279 |
05 Feb 19 |
nicklas |
if a line was found FALSE if not. It is typically expected that |
5279 |
05 Feb 19 |
nicklas |
we have more cases in our database than what is in the INCA file since |
5279 |
05 Feb 19 |
nicklas |
there is a delay. |
5279 |
05 Feb 19 |
nicklas |
@param laterality Laterlity from the database |
5279 |
05 Feb 19 |
nicklas |
@param caseId Internal ID of the Case item in the database |
5292 |
12 Feb 19 |
nicklas |
@param hasSpecimen Flag that indictes if the case has a specimen item |
5279 |
05 Feb 19 |
nicklas |
2075 |
*/ |
5292 |
12 Feb 19 |
nicklas |
2076 |
boolean mapCase(String laterality, int caseId, String caseName, boolean hasSpecimen) |
5273 |
31 Jan 19 |
nicklas |
2077 |
{ |
5279 |
05 Feb 19 |
nicklas |
2078 |
boolean wasMapped = false; |
6124 |
10 Feb 21 |
nicklas |
2079 |
IncaLine caseToMap = get(laterality); |
5292 |
12 Feb 19 |
nicklas |
2080 |
if (caseToMap != null) |
5273 |
31 Jan 19 |
nicklas |
2081 |
{ |
5292 |
12 Feb 19 |
nicklas |
2082 |
caseToMap.caseId = caseId; |
5292 |
12 Feb 19 |
nicklas |
2083 |
caseToMap.caseName = caseName; |
5292 |
12 Feb 19 |
nicklas |
2084 |
caseToMap.hasSpecimen = hasSpecimen; |
5292 |
12 Feb 19 |
nicklas |
2085 |
wasMapped = true; |
5273 |
31 Jan 19 |
nicklas |
2086 |
} |
5279 |
05 Feb 19 |
nicklas |
2087 |
return wasMapped; |
5273 |
31 Jan 19 |
nicklas |
2088 |
} |
5273 |
31 Jan 19 |
nicklas |
2089 |
|
5279 |
05 Feb 19 |
nicklas |
2090 |
/** |
5279 |
05 Feb 19 |
nicklas |
Checks if all lines have been mapped to a case. |
5279 |
05 Feb 19 |
nicklas |
2092 |
*/ |
5279 |
05 Feb 19 |
nicklas |
2093 |
boolean hasUnmappedCase() |
5279 |
05 Feb 19 |
nicklas |
2094 |
{ |
5279 |
05 Feb 19 |
nicklas |
2095 |
return leftCase != null && leftCase.caseId == 0 || rightCase != null && rightCase.caseId == 0; |
5279 |
05 Feb 19 |
nicklas |
2096 |
} |
5279 |
05 Feb 19 |
nicklas |
2097 |
|
5279 |
05 Feb 19 |
nicklas |
2098 |
|
5279 |
05 Feb 19 |
nicklas |
2099 |
/** |
5279 |
05 Feb 19 |
nicklas |
Registers items that we have in the database that was NOT mapped |
5279 |
05 Feb 19 |
nicklas |
to any of the lines in this pair. |
5279 |
05 Feb 19 |
nicklas |
2102 |
*/ |
5279 |
05 Feb 19 |
nicklas |
2103 |
void setUnmappedItems(Set<String> unmappedItems) |
5279 |
05 Feb 19 |
nicklas |
2104 |
{ |
5279 |
05 Feb 19 |
nicklas |
2105 |
if (leftCase != null && leftCase.caseId == 0) |
5279 |
05 Feb 19 |
nicklas |
2106 |
{ |
5279 |
05 Feb 19 |
nicklas |
2107 |
leftCase.unmappedItems = unmappedItems; |
5279 |
05 Feb 19 |
nicklas |
2108 |
leftCase.otherCase = rightCase; |
5279 |
05 Feb 19 |
nicklas |
2109 |
} |
5279 |
05 Feb 19 |
nicklas |
2110 |
if (rightCase != null && rightCase.caseId == 0) |
5279 |
05 Feb 19 |
nicklas |
2111 |
{ |
5279 |
05 Feb 19 |
nicklas |
2112 |
rightCase.unmappedItems = unmappedItems; |
5279 |
05 Feb 19 |
nicklas |
2113 |
rightCase.otherCase = leftCase; |
5279 |
05 Feb 19 |
nicklas |
2114 |
} |
5279 |
05 Feb 19 |
nicklas |
2115 |
} |
5279 |
05 Feb 19 |
nicklas |
2116 |
|
5261 |
23 Jan 19 |
nicklas |
2117 |
} |
5258 |
22 Jan 19 |
nicklas |
2118 |
|
5269 |
28 Jan 19 |
nicklas |
2119 |
/** |
5269 |
28 Jan 19 |
nicklas |
Reasons for excluding a data line. |
5269 |
28 Jan 19 |
nicklas |
2121 |
*/ |
5263 |
24 Jan 19 |
nicklas |
2122 |
static enum ExcludedLine |
5263 |
24 Jan 19 |
nicklas |
2123 |
{ |
5263 |
24 Jan 19 |
nicklas |
2124 |
TOO_MANY_COLUMNS, |
5263 |
24 Jan 19 |
nicklas |
2125 |
TOO_FEW_COLUMNS, |
5263 |
24 Jan 19 |
nicklas |
2126 |
MISSING_PERSONAL_NO, |
5263 |
24 Jan 19 |
nicklas |
2127 |
MISSING_LATERALITY, |
5263 |
24 Jan 19 |
nicklas |
2128 |
MISSING_FOLLOWUP_DATE, |
5263 |
24 Jan 19 |
nicklas |
2129 |
DUPLICATE_LATERALITY, |
5267 |
25 Jan 19 |
nicklas |
2130 |
DUPLICATE_FOLLOWUP, |
5267 |
25 Jan 19 |
nicklas |
2131 |
INVALID_DATA_VALUE, |
5275 |
31 Jan 19 |
nicklas |
2132 |
PATIENT_NOTIN_DATABASE, |
5288 |
08 Feb 19 |
nicklas |
2133 |
CASE_NOTIN_DATABASE, |
5288 |
08 Feb 19 |
nicklas |
2134 |
FILTERED_BY_DATE, |
5288 |
08 Feb 19 |
nicklas |
2135 |
FILTERED_BY_CANCER_TYPE; |
5263 |
24 Jan 19 |
nicklas |
2136 |
} |
5261 |
23 Jan 19 |
nicklas |
2137 |
|
5258 |
22 Jan 19 |
nicklas |
2138 |
/** |
5288 |
08 Feb 19 |
nicklas |
Filter implementation that accepts lines with a date between |
5288 |
08 Feb 19 |
nicklas |
the start and end dates. Both dates are optional. |
5288 |
08 Feb 19 |
nicklas |
2141 |
*/ |
5288 |
08 Feb 19 |
nicklas |
2142 |
static class DateFilter |
5288 |
08 Feb 19 |
nicklas |
2143 |
implements Filter<IncaLine> |
5288 |
08 Feb 19 |
nicklas |
2144 |
{ |
5288 |
08 Feb 19 |
nicklas |
2145 |
|
5288 |
08 Feb 19 |
nicklas |
2146 |
private final int column; |
5288 |
08 Feb 19 |
nicklas |
2147 |
private final Date start; |
5288 |
08 Feb 19 |
nicklas |
2148 |
private final Date end; |
5288 |
08 Feb 19 |
nicklas |
2149 |
|
5288 |
08 Feb 19 |
nicklas |
2150 |
DateFilter(int column, Date start, Date end) |
5288 |
08 Feb 19 |
nicklas |
2151 |
{ |
5288 |
08 Feb 19 |
nicklas |
2152 |
this.column = column; |
5288 |
08 Feb 19 |
nicklas |
2153 |
this.start = start; |
5288 |
08 Feb 19 |
nicklas |
2154 |
this.end = end; |
5288 |
08 Feb 19 |
nicklas |
2155 |
} |
5288 |
08 Feb 19 |
nicklas |
2156 |
|
5288 |
08 Feb 19 |
nicklas |
2157 |
@Override |
5288 |
08 Feb 19 |
nicklas |
2158 |
public boolean evaluate(IncaLine line) |
5288 |
08 Feb 19 |
nicklas |
2159 |
{ |
5288 |
08 Feb 19 |
nicklas |
2160 |
Date lineDate = (Date)line.data[column]; |
5288 |
08 Feb 19 |
nicklas |
2161 |
return lineDate != null && (start == null || start.before(lineDate)) && (end == null || end.after(lineDate)); |
5288 |
08 Feb 19 |
nicklas |
2162 |
} |
5288 |
08 Feb 19 |
nicklas |
2163 |
} |
5288 |
08 Feb 19 |
nicklas |
2164 |
|
5288 |
08 Feb 19 |
nicklas |
2165 |
/** |
5290 |
11 Feb 19 |
nicklas |
Filter implementation that accepts IncaLine:s where the |
5290 |
11 Feb 19 |
nicklas |
specified column exactly matches the given value. |
5288 |
08 Feb 19 |
nicklas |
2168 |
*/ |
5290 |
11 Feb 19 |
nicklas |
2169 |
static class ColumnValueFilter |
5288 |
08 Feb 19 |
nicklas |
2170 |
implements Filter<IncaLine> |
5288 |
08 Feb 19 |
nicklas |
2171 |
{ |
5288 |
08 Feb 19 |
nicklas |
2172 |
|
5288 |
08 Feb 19 |
nicklas |
2173 |
private final int column; |
5290 |
11 Feb 19 |
nicklas |
2174 |
private final Object value; |
5288 |
08 Feb 19 |
nicklas |
2175 |
|
5290 |
11 Feb 19 |
nicklas |
2176 |
ColumnValueFilter(int column, Object value) |
5288 |
08 Feb 19 |
nicklas |
2177 |
{ |
5288 |
08 Feb 19 |
nicklas |
2178 |
this.column = column; |
5290 |
11 Feb 19 |
nicklas |
2179 |
this.value = value; |
5288 |
08 Feb 19 |
nicklas |
2180 |
} |
5290 |
11 Feb 19 |
nicklas |
2181 |
|
5290 |
11 Feb 19 |
nicklas |
2182 |
@Override |
5290 |
11 Feb 19 |
nicklas |
2183 |
public boolean evaluate(IncaLine line) |
5290 |
11 Feb 19 |
nicklas |
2184 |
{ |
5290 |
11 Feb 19 |
nicklas |
2185 |
Object v = line.data[column]; |
5290 |
11 Feb 19 |
nicklas |
2186 |
return value.equals(v); |
5290 |
11 Feb 19 |
nicklas |
2187 |
} |
5290 |
11 Feb 19 |
nicklas |
2188 |
} |
5288 |
08 Feb 19 |
nicklas |
2189 |
|
5290 |
11 Feb 19 |
nicklas |
2190 |
/** |
5290 |
11 Feb 19 |
nicklas |
Filter implementation that accepts IncaLine:s where |
5290 |
11 Feb 19 |
nicklas |
the specified column has a value between a min and max |
5290 |
11 Feb 19 |
nicklas |
value (inclusive). The filter only works with integer values. |
5290 |
11 Feb 19 |
nicklas |
Use -1 to accept all values. |
5290 |
11 Feb 19 |
nicklas |
2195 |
*/ |
5290 |
11 Feb 19 |
nicklas |
2196 |
static class BetweenFilter |
5290 |
11 Feb 19 |
nicklas |
2197 |
implements Filter<IncaLine> |
5290 |
11 Feb 19 |
nicklas |
2198 |
{ |
5290 |
11 Feb 19 |
nicklas |
2199 |
|
5290 |
11 Feb 19 |
nicklas |
2200 |
private final int column; |
5290 |
11 Feb 19 |
nicklas |
2201 |
private final int min; |
5290 |
11 Feb 19 |
nicklas |
2202 |
private final int max; |
5290 |
11 Feb 19 |
nicklas |
2203 |
|
5290 |
11 Feb 19 |
nicklas |
2204 |
BetweenFilter(int column, int min, int max) |
5290 |
11 Feb 19 |
nicklas |
2205 |
{ |
5290 |
11 Feb 19 |
nicklas |
2206 |
this.column = column; |
5290 |
11 Feb 19 |
nicklas |
2207 |
this.min = min; |
5290 |
11 Feb 19 |
nicklas |
2208 |
this.max = max; |
5290 |
11 Feb 19 |
nicklas |
2209 |
} |
5290 |
11 Feb 19 |
nicklas |
2210 |
|
5288 |
08 Feb 19 |
nicklas |
2211 |
@Override |
5288 |
08 Feb 19 |
nicklas |
2212 |
public boolean evaluate(IncaLine line) |
5288 |
08 Feb 19 |
nicklas |
2213 |
{ |
5290 |
11 Feb 19 |
nicklas |
2214 |
Integer v = (Integer)line.data[column]; |
5290 |
11 Feb 19 |
nicklas |
2215 |
if (v != null) |
5290 |
11 Feb 19 |
nicklas |
2216 |
{ |
5290 |
11 Feb 19 |
nicklas |
2217 |
return (min == -1 || min <= v) && (max == -1 || v <= max); |
5290 |
11 Feb 19 |
nicklas |
2218 |
} |
5290 |
11 Feb 19 |
nicklas |
2219 |
return false; |
5288 |
08 Feb 19 |
nicklas |
2220 |
} |
5290 |
11 Feb 19 |
nicklas |
2221 |
|
5288 |
08 Feb 19 |
nicklas |
2222 |
} |
5290 |
11 Feb 19 |
nicklas |
2223 |
|
5290 |
11 Feb 19 |
nicklas |
2224 |
/** |
5290 |
11 Feb 19 |
nicklas |
Represents a statistical variable (for example 'ER Status') |
5290 |
11 Feb 19 |
nicklas |
that we can use for grouping IncaLine entries into subgroups. |
5290 |
11 Feb 19 |
nicklas |
Subgroups are added by any of the 'addXXX' method and are evaluated |
5290 |
11 Feb 19 |
nicklas |
in the same order. An IncaLine entry is counted for the first |
5290 |
11 Feb 19 |
nicklas |
group that matches. Use addNaGroup() to add a 'catch-all' group |
5290 |
11 Feb 19 |
nicklas |
as the last entry. |
5290 |
11 Feb 19 |
nicklas |
2231 |
*/ |
5290 |
11 Feb 19 |
nicklas |
2232 |
static class StatisticalVariable |
5290 |
11 Feb 19 |
nicklas |
2233 |
{ |
5290 |
11 Feb 19 |
nicklas |
2234 |
|
5292 |
12 Feb 19 |
nicklas |
2235 |
final String name; |
5292 |
12 Feb 19 |
nicklas |
2236 |
final int column; |
5292 |
12 Feb 19 |
nicklas |
2237 |
final String unit; |
5292 |
12 Feb 19 |
nicklas |
2238 |
final List<StatisticalGroup> groups; |
5302 |
15 Feb 19 |
nicklas |
2239 |
final StatisticalGroup totalNotNa; |
5302 |
15 Feb 19 |
nicklas |
2240 |
boolean hasNaGroup; |
5290 |
11 Feb 19 |
nicklas |
2241 |
|
5290 |
11 Feb 19 |
nicklas |
2242 |
/** |
5290 |
11 Feb 19 |
nicklas |
Creates a variable with the given name. Values are found in the |
5290 |
11 Feb 19 |
nicklas |
column with the given index. |
5290 |
11 Feb 19 |
nicklas |
2245 |
*/ |
5292 |
12 Feb 19 |
nicklas |
2246 |
StatisticalVariable(String name, int column, String unit) |
5290 |
11 Feb 19 |
nicklas |
2247 |
{ |
5290 |
11 Feb 19 |
nicklas |
2248 |
this.name = name; |
5290 |
11 Feb 19 |
nicklas |
2249 |
this.column = column; |
5292 |
12 Feb 19 |
nicklas |
2250 |
this.unit = unit; |
5290 |
11 Feb 19 |
nicklas |
2251 |
this.groups = new ArrayList<>(); |
5302 |
15 Feb 19 |
nicklas |
2252 |
this.totalNotNa = new StatisticalGroup("", false, new StaticFilter<>(true)); |
5302 |
15 Feb 19 |
nicklas |
2253 |
this.hasNaGroup = false; |
5290 |
11 Feb 19 |
nicklas |
2254 |
} |
5290 |
11 Feb 19 |
nicklas |
2255 |
|
5290 |
11 Feb 19 |
nicklas |
2256 |
/** |
5290 |
11 Feb 19 |
nicklas |
Define a subgroup for this variable using the ColumnValueFilter. |
5290 |
11 Feb 19 |
nicklas |
An IncaLine is counted for this subgroup if the column exactly matches |
5290 |
11 Feb 19 |
nicklas |
the given value. |
5290 |
11 Feb 19 |
nicklas |
2260 |
*/ |
5290 |
11 Feb 19 |
nicklas |
2261 |
StatisticalVariable addValueGroup(String name, Object value) |
5290 |
11 Feb 19 |
nicklas |
2262 |
{ |
5290 |
11 Feb 19 |
nicklas |
2263 |
addGroup(name, new ColumnValueFilter(column, value)); |
5290 |
11 Feb 19 |
nicklas |
2264 |
return this; |
5290 |
11 Feb 19 |
nicklas |
2265 |
} |
5290 |
11 Feb 19 |
nicklas |
2266 |
|
5290 |
11 Feb 19 |
nicklas |
2267 |
/** |
5290 |
11 Feb 19 |
nicklas |
Adds a catch-all subgroup that matches all lines. This should |
5290 |
11 Feb 19 |
nicklas |
be used as the last subgroup since. |
5290 |
11 Feb 19 |
nicklas |
2270 |
*/ |
5290 |
11 Feb 19 |
nicklas |
2271 |
StatisticalVariable addNaGroup() |
5290 |
11 Feb 19 |
nicklas |
2272 |
{ |
5302 |
15 Feb 19 |
nicklas |
2273 |
groups.add(new StatisticalGroup("N/A", true, new StaticFilter<>(true))); |
5302 |
15 Feb 19 |
nicklas |
2274 |
hasNaGroup = true; |
5290 |
11 Feb 19 |
nicklas |
2275 |
return this; |
5290 |
11 Feb 19 |
nicklas |
2276 |
} |
5290 |
11 Feb 19 |
nicklas |
2277 |
|
5290 |
11 Feb 19 |
nicklas |
2278 |
/** |
5290 |
11 Feb 19 |
nicklas |
Add a subgroup and use the given filter to match. |
5290 |
11 Feb 19 |
nicklas |
2280 |
*/ |
5290 |
11 Feb 19 |
nicklas |
2281 |
StatisticalVariable addGroup(String name, Filter<IncaLine> filter) |
5290 |
11 Feb 19 |
nicklas |
2282 |
{ |
5302 |
15 Feb 19 |
nicklas |
2283 |
groups.add(new StatisticalGroup(name, false, filter)); |
5290 |
11 Feb 19 |
nicklas |
2284 |
return this; |
5290 |
11 Feb 19 |
nicklas |
2285 |
} |
5290 |
11 Feb 19 |
nicklas |
2286 |
|
5290 |
11 Feb 19 |
nicklas |
2287 |
|
5290 |
11 Feb 19 |
nicklas |
2288 |
/** |
5290 |
11 Feb 19 |
nicklas |
Update the statistics for this variable with data from the given IncaLine. |
5290 |
11 Feb 19 |
nicklas |
All registered subgroups are evaluated in the order they were registered. |
5290 |
11 Feb 19 |
nicklas |
The line is counted for the first subgroup that matches. |
5290 |
11 Feb 19 |
nicklas |
2292 |
*/ |
5290 |
11 Feb 19 |
nicklas |
2293 |
void update(IncaLine line) |
5290 |
11 Feb 19 |
nicklas |
2294 |
{ |
5290 |
11 Feb 19 |
nicklas |
2295 |
for (StatisticalGroup g : groups) |
5290 |
11 Feb 19 |
nicklas |
2296 |
{ |
5302 |
15 Feb 19 |
nicklas |
2297 |
if (g.update(line)) |
5302 |
15 Feb 19 |
nicklas |
2298 |
{ |
5302 |
15 Feb 19 |
nicklas |
2299 |
if (!g.isNa) totalNotNa.update(line); |
5302 |
15 Feb 19 |
nicklas |
2300 |
break; |
5302 |
15 Feb 19 |
nicklas |
2301 |
} |
5290 |
11 Feb 19 |
nicklas |
2302 |
} |
5290 |
11 Feb 19 |
nicklas |
2303 |
} |
5290 |
11 Feb 19 |
nicklas |
2304 |
} |
5290 |
11 Feb 19 |
nicklas |
2305 |
|
5290 |
11 Feb 19 |
nicklas |
2306 |
/** |
5290 |
11 Feb 19 |
nicklas |
Represents a subgroup for a statistical variable. For example |
5290 |
11 Feb 19 |
nicklas |
'ER Status=Positive'. IncaLine entries that matches the registered |
5290 |
11 Feb 19 |
nicklas |
filter is considered to belong to the group. |
5290 |
11 Feb 19 |
nicklas |
2310 |
*/ |
5290 |
11 Feb 19 |
nicklas |
2311 |
static class StatisticalGroup |
5290 |
11 Feb 19 |
nicklas |
2312 |
{ |
5290 |
11 Feb 19 |
nicklas |
2313 |
final String name; |
5302 |
15 Feb 19 |
nicklas |
2314 |
final boolean isNa; |
5290 |
11 Feb 19 |
nicklas |
2315 |
final Filter<IncaLine> filter; |
5288 |
08 Feb 19 |
nicklas |
2316 |
|
5290 |
11 Feb 19 |
nicklas |
2317 |
int numTotal; |
5292 |
12 Feb 19 |
nicklas |
2318 |
int numAccruedSpecimen; |
5290 |
11 Feb 19 |
nicklas |
2319 |
int numAccruedNoSpecimen; |
5290 |
11 Feb 19 |
nicklas |
2320 |
int numNotAccrued; |
5290 |
11 Feb 19 |
nicklas |
2321 |
|
5290 |
11 Feb 19 |
nicklas |
2322 |
/** |
5290 |
11 Feb 19 |
nicklas |
Creates a subgroup with the given name and filter. |
5290 |
11 Feb 19 |
nicklas |
2324 |
*/ |
5302 |
15 Feb 19 |
nicklas |
2325 |
StatisticalGroup(String name, boolean isNa, Filter<IncaLine> filter) |
5290 |
11 Feb 19 |
nicklas |
2326 |
{ |
5290 |
11 Feb 19 |
nicklas |
2327 |
this.name = name; |
5302 |
15 Feb 19 |
nicklas |
2328 |
this.isNa = isNa; |
5290 |
11 Feb 19 |
nicklas |
2329 |
this.filter = filter; |
5290 |
11 Feb 19 |
nicklas |
2330 |
} |
5290 |
11 Feb 19 |
nicklas |
2331 |
|
5290 |
11 Feb 19 |
nicklas |
2332 |
/** |
5290 |
11 Feb 19 |
nicklas |
Check if the group filter matches the filter and |
5290 |
11 Feb 19 |
nicklas |
update the statistics if it does. |
5290 |
11 Feb 19 |
nicklas |
@return TRUE if the line matched the group, FALSE if not |
5290 |
11 Feb 19 |
nicklas |
2336 |
*/ |
5290 |
11 Feb 19 |
nicklas |
2337 |
boolean update(IncaLine line) |
5290 |
11 Feb 19 |
nicklas |
2338 |
{ |
5290 |
11 Feb 19 |
nicklas |
2339 |
if (!filter.evaluate(line)) return false; |
5290 |
11 Feb 19 |
nicklas |
2340 |
|
5290 |
11 Feb 19 |
nicklas |
2341 |
numTotal++; |
5290 |
11 Feb 19 |
nicklas |
2342 |
if (line.personalNo != null) |
5290 |
11 Feb 19 |
nicklas |
2343 |
{ |
5292 |
12 Feb 19 |
nicklas |
2344 |
if (line.hasSpecimen) |
5292 |
12 Feb 19 |
nicklas |
2345 |
{ |
5292 |
12 Feb 19 |
nicklas |
2346 |
numAccruedSpecimen++; |
5292 |
12 Feb 19 |
nicklas |
2347 |
} |
5292 |
12 Feb 19 |
nicklas |
2348 |
else |
5292 |
12 Feb 19 |
nicklas |
2349 |
{ |
5292 |
12 Feb 19 |
nicklas |
2350 |
numAccruedNoSpecimen++; |
5292 |
12 Feb 19 |
nicklas |
2351 |
} |
5290 |
11 Feb 19 |
nicklas |
2352 |
} |
5290 |
11 Feb 19 |
nicklas |
2353 |
else |
5290 |
11 Feb 19 |
nicklas |
2354 |
{ |
5290 |
11 Feb 19 |
nicklas |
2355 |
numNotAccrued++; |
5290 |
11 Feb 19 |
nicklas |
2356 |
} |
5290 |
11 Feb 19 |
nicklas |
2357 |
return true; |
5290 |
11 Feb 19 |
nicklas |
2358 |
} |
5290 |
11 Feb 19 |
nicklas |
2359 |
} |
5288 |
08 Feb 19 |
nicklas |
2360 |
|
3786 |
17 Mar 16 |
olle |
2361 |
} |