From ffeba1717b15c16f06e2858b176b3ab05d13f76f Mon Sep 17 00:00:00 2001 From: Piotr Gawron <piotr.gawron@uni.lu> Date: Thu, 23 Feb 2017 18:33:56 +0100 Subject: [PATCH] simple parser for redcap added --- .../appointment/parse/AppointmentEntry.java | 21 ++ .../smash/appointment/parse/RedcapParser.java | 204 ++++++++++++++++++ .../java/smash/appointment/parse/Subject.java | 67 ++++++ .../smash/appointment/parse/SubjectDao.java | 9 + .../appointment/parse/CellParserTest.java | 11 +- .../appointment/parse/RedcapParserTest.java | 75 +++++++ .../smash/appointment/parse/TestBase.java | 14 +- .../parse/XlsxCalendarProcessorTest.java | 14 +- appointment-import/testFiles/redcap_imp.txt | 7 + 9 files changed, 402 insertions(+), 20 deletions(-) create mode 100644 appointment-import/src/main/java/smash/appointment/parse/RedcapParser.java create mode 100644 appointment-import/src/test/java/smash/appointment/parse/RedcapParserTest.java create mode 100644 appointment-import/testFiles/redcap_imp.txt diff --git a/appointment-import/src/main/java/smash/appointment/parse/AppointmentEntry.java b/appointment-import/src/main/java/smash/appointment/parse/AppointmentEntry.java index 8db01548..2268c902 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/AppointmentEntry.java +++ b/appointment-import/src/main/java/smash/appointment/parse/AppointmentEntry.java @@ -1,8 +1,12 @@ package smash.appointment.parse; +import java.util.ArrayList; +import java.util.List; + public class AppointmentEntry { private String day; private String time; + private String duration; private Subject subject; private AppointmentType type; private String source; @@ -96,4 +100,21 @@ public class AppointmentEntry { public String toString() { return day + " " + time + " " + subject + " " + type + "\t\t[source: " + source + "]"; } + + /** + * @return the duration + * @see #duration + */ + public String getDuration() { + return duration; + } + + /** + * @param duration + * the duration to set + * @see #duration + */ + public void setDuration(String duration) { + this.duration = duration; + } } diff --git a/appointment-import/src/main/java/smash/appointment/parse/RedcapParser.java b/appointment-import/src/main/java/smash/appointment/parse/RedcapParser.java new file mode 100644 index 00000000..2e8cbc2f --- /dev/null +++ b/appointment-import/src/main/java/smash/appointment/parse/RedcapParser.java @@ -0,0 +1,204 @@ +package smash.appointment.parse; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +public class RedcapParser { + Logger logger = Logger.getLogger(RedcapParser.class); + + private SubjectDao subjectDao; + + public List<AppointmentEntry> parse(String filename) throws FileNotFoundException, IOException { + List<AppointmentEntry> result = new ArrayList<>(); + try (BufferedReader br = new BufferedReader(new FileReader(filename))) { + int lineNumber = 0; + String line; + while ((line = br.readLine()) != null) { + lineNumber++; + if (lineNumber == 1) { + continue; + } + String tmp[] = line.split("\t", -1); + + String ndNumber = tmp[0]; + Subject subject = subjectDao.getByNdNumber(ndNumber); + if (subject == null) { + logger.warn("Cannot find subject with id: " + ndNumber); + } else { + subject.addLanguage(tmp[4]); + subject.addLanguage(tmp[5]); + subject.addLanguage(tmp[6]); + subject.addLanguage(tmp[7]); + subject.setSex(tmp[3]); + subject.setBirthDate(tmp[2]); + + List<AppointmentEntry> subjectAppointments = new ArrayList<>(); + if (!tmp[9].isEmpty()) { + subjectAppointments.add(createEntryLevelA(tmp[9], tmp[10], subject)); + } + + if (!tmp[13].isEmpty()) { + subjectAppointments.add(createEntryLevelB(tmp[13], tmp[14], subject)); + } + + if (!tmp[17].isEmpty()) { + subjectAppointments.add(createEntryLevelBG(tmp[17], tmp[18], subject)); + } + + if (!tmp[21].isEmpty()) { + subjectAppointments.add(createEntryLevelBV(tmp[21], tmp[22], subject)); + } + + if (!tmp[41].isEmpty()) { + subjectAppointments.add(createEntryLevelSB(tmp[41], subject)); + } + + if (!tmp[45].isEmpty()) { + subjectAppointments.add(createEntryLevelMPower(tmp[45], subject)); + } + + mergeAppointments(subjectAppointments); + + result.addAll(subjectAppointments); + } + + } + } + return result; + + } + + private void mergeAppointments(List<AppointmentEntry> subjectAppointments) { + logger.warn("Not implemented"); + } + + private AppointmentEntry createEntryLevelMPower(String from, Subject subject) { + AppointmentEntry entry = new AppointmentEntry(); + String date = getDate(from); + entry.setDay(date); + entry.setSubject(subject); + entry.setSource("Imported from RedCap"); + entry.setType(AppointmentType.LEVEL_B_M_POWER); + return entry; + } + + private AppointmentEntry createEntryLevelSB(String from, Subject subject) { + AppointmentEntry entry = new AppointmentEntry(); + String date = getDate(from); + entry.setDay(date); + entry.setSubject(subject); + entry.setSource("Imported from RedCap"); + entry.setType(AppointmentType.LEVEL_SB); + return entry; + } + + private AppointmentEntry createEntryLevelBV(String from, String to, Subject subject) { + AppointmentEntry entry = new AppointmentEntry(); + String date = getDate(from); + String date2 = getDate(to); + if (!date.equals(date2)){ + logger.warn("Different start and end date for level BV appointment. Subject: "+subject.getNdNumber()); + } + entry.setDay(date); + entry.setSubject(subject); + entry.setSource("Imported from RedCap"); + entry.setType(AppointmentType.LEVEL_BV); + return entry; + } + + private AppointmentEntry createEntryLevelBG(String from, String to, Subject subject) { + AppointmentEntry entry = new AppointmentEntry(); + String date = getDate(from); + String date2 = getDate(to); + if (!date.equals(date2)){ + logger.warn("Different start and end date for level BG appointment. Subject: "+subject.getNdNumber()); + } + entry.setDay(date); + entry.setSubject(subject); + entry.setSource("Imported from RedCap"); + entry.setType(AppointmentType.LEVEL_BG); + return entry; + } + + private AppointmentEntry createEntryLevelB(String from, String to, Subject subject) { + AppointmentEntry entry = new AppointmentEntry(); + String date = getDate(from); + String date2 = getDate(to); + if (!date.equals(date2)){ + logger.warn("Different start and end date for level B appointment. Subject: "+subject.getNdNumber()); + } + entry.setDay(date); + entry.setSubject(subject); + entry.setSource("Imported from RedCap"); + entry.setType(AppointmentType.LEVEL_B); + return entry; + } + + private AppointmentEntry createEntryLevelA(String from, String to, Subject subject) { + AppointmentEntry entry = new AppointmentEntry(); + String date = getDate(from); + String date2 = getDate(to); + if (!date.equals(date2)){ + logger.warn("Different start and end date for level A appointment. Subject: "+subject.getNdNumber()+"; "+date+", "+date2); + } + String time = getTime(from); + String duration = getDuration(from, to); + entry.setDay(date); + entry.setDuration(duration); + entry.setSubject(subject); + entry.setSource("Imported from RedCap"); + entry.setTime(time); + entry.setType(AppointmentType.LEVEL_A); + return entry; + } + + private String getDuration(String string, String string2) { + if (string2.isEmpty()) { + return null; + } + String time1 = getTime(string); + String time2 = getTime(string2); + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + LocalDateTime dateTime1 = LocalDateTime.parse("2014-11-25 " + time1 + ":00", formatter); + LocalDateTime dateTime2 = LocalDateTime.parse("2014-11-25 " + time2 + ":00", formatter); + + long diffInMinutes = java.time.Duration.between(dateTime1, dateTime2).toMinutes(); + + return diffInMinutes + ""; + } + + private String getTime(String string) { + return string.split(" ")[1]; + } + + private String getDate(String string) { + return string.split(" ")[0]; + } + + /** + * @return the subjectDao + * @see #subjectDao + */ + public SubjectDao getSubjectDao() { + return subjectDao; + } + + /** + * @param subjectDao + * the subjectDao to set + * @see #subjectDao + */ + public void setSubjectDao(SubjectDao subjectDao) { + this.subjectDao = subjectDao; + } +} diff --git a/appointment-import/src/main/java/smash/appointment/parse/Subject.java b/appointment-import/src/main/java/smash/appointment/parse/Subject.java index ee5cc02d..92c2ab9f 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/Subject.java +++ b/appointment-import/src/main/java/smash/appointment/parse/Subject.java @@ -1,10 +1,21 @@ package smash.appointment.parse; +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + public class Subject { + Logger logger = Logger.getLogger(Subject .class); + private String name; private String surname; private String ndNumber; private String screeningNumber; + private String sex; + private String birthDate; + + private List<String> languages = new ArrayList<>(); public Subject(String name, String surname, String ndNumber, String screeningNumber) { this.name = name; @@ -86,4 +97,60 @@ public class Subject { return this.getName() + " " + this.getSurname() + " (" + this.getNdNumber() + "; " + this.getScreeningNumber() + ")"; } + public void addLanguage(String string) { + if (!string.isEmpty()) { + this.languages.add(string); + } + } + + /** + * @return the sex + * @see #sex + */ + public String getSex() { + return sex; + } + + /** + * @param sex + * the sex to set + * @see #sex + */ + public void setSex(String sex) { + this.sex = sex; + } + + /** + * @return the birthDate + * @see #birthDate + */ + public String getBirthDate() { + return birthDate; + } + + /** + * @param birthDate + * the birthDate to set + * @see #birthDate + */ + public void setBirthDate(String birthDate) { + this.birthDate = birthDate; + } + + /** + * @return the languages + * @see #languages + */ + public List<String> getLanguages() { + return languages; + } + + /** + * @param languages the languages to set + * @see #languages + */ + public void setLanguages(List<String> languages) { + this.languages = languages; + } + } diff --git a/appointment-import/src/main/java/smash/appointment/parse/SubjectDao.java b/appointment-import/src/main/java/smash/appointment/parse/SubjectDao.java index c852c84c..682133ca 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/SubjectDao.java +++ b/appointment-import/src/main/java/smash/appointment/parse/SubjectDao.java @@ -40,4 +40,13 @@ public class SubjectDao { this.subjects = subjects; } + public Subject getByNdNumber(String ndNumber) { + for (Subject s : subjects) { + if (ndNumber.equals(s.getNdNumber())) { + return s; + } + } + return null; + } + } diff --git a/appointment-import/src/test/java/smash/appointment/parse/CellParserTest.java b/appointment-import/src/test/java/smash/appointment/parse/CellParserTest.java index 5c96c720..9178b38a 100644 --- a/appointment-import/src/test/java/smash/appointment/parse/CellParserTest.java +++ b/appointment-import/src/test/java/smash/appointment/parse/CellParserTest.java @@ -6,20 +6,23 @@ import static org.junit.Assert.assertFalse; import java.util.ArrayList; import java.util.List; +import org.apache.log4j.Logger; import org.junit.Before; import org.junit.Test; public class CellParserTest extends TestBase { + Logger logger = Logger.getLogger(CellParserTest.class); + List<CellParseTestCase> testCases; - CellParser parser; + CellParser parser; @Before public void setUp() { super.setUp(); - + parser = new CellParser(); parser.setSubjectDao(subjectDao); - + testCases = new ArrayList<CellParseTestCase>(); testCases.add(new CellParseTestCase("Piotr Gawron level A FU V3", piotrGawron, null, AppointmentType.LEVEL_A)); @@ -32,9 +35,9 @@ public class CellParserTest extends TestBase { testCases.add(new CellParseTestCase("ND0004 level BV (c) + SB ©", cateKowalsky, null, AppointmentType.LEVEL_BV_SB)); testCases.add(new CellParseTestCase("Cate Kowalsky level BV + BG + SB + m-Power", cateKowalsky, null, AppointmentType.LEVEL_BV_BG_SB_MPOWER)); testCases.add(new CellParseTestCase("sb name level A", null, null, AppointmentType.LEVEL_A)); + testCases.add(new CellParseTestCase("Andrew Dude level A FU V3", andrewDude, null, AppointmentType.LEVEL_A)); } - @Test public void testExtractTime() { for (CellParseTestCase testCase : testCases) { diff --git a/appointment-import/src/test/java/smash/appointment/parse/RedcapParserTest.java b/appointment-import/src/test/java/smash/appointment/parse/RedcapParserTest.java new file mode 100644 index 00000000..33a72db1 --- /dev/null +++ b/appointment-import/src/test/java/smash/appointment/parse/RedcapParserTest.java @@ -0,0 +1,75 @@ +package smash.appointment.parse; + +import static org.junit.Assert.*; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; + +import org.apache.log4j.Logger; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; + +public class RedcapParserTest extends TestBase{ + Logger logger = Logger.getLogger(RedcapParserTest.class); + RedcapParser parser = new RedcapParser(); + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + @Before + public void setUp() { + super.setUp(); + parser.setSubjectDao(subjectDao); + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testParseFile() throws FileNotFoundException, IOException { + List<AppointmentEntry> entries = parser.parse("testFiles/redcap_imp.txt"); + assertTrue(entries.size() > 0); + assertEquals(3, subjectDao.getByNdNumber("ND0001").getLanguages().size()); + + int levelACount = 0; + int levelBCount = 0; + int levelBVCount = 0; + int levelBGCount = 0; + int levelSBCount = 0; + int levelMPowerCount = 0; + for (AppointmentEntry appointmentEntry : entries) { + logger.debug(appointmentEntry); + if (appointmentEntry.getType().equals(AppointmentType.LEVEL_A)) { + levelACount++; + } + if (appointmentEntry.getType().equals(AppointmentType.LEVEL_B)) { + levelBCount++; + } + if (appointmentEntry.getType().equals(AppointmentType.LEVEL_BG)) { + levelBGCount++; + } + if (appointmentEntry.getType().equals(AppointmentType.LEVEL_BV)) { + levelBVCount++; + } + if (appointmentEntry.getType().equals(AppointmentType.LEVEL_SB)) { + levelSBCount++; + } + if (appointmentEntry.getType().equals(AppointmentType.LEVEL_B_M_POWER)) { + levelMPowerCount++; + } + } + assertTrue(levelACount > 0); + assertTrue(levelBCount > 0); + assertTrue(levelBVCount > 0); + assertTrue(levelBGCount > 0); + assertTrue(levelSBCount > 0); + assertTrue(levelMPowerCount > 0); + + } + +} diff --git a/appointment-import/src/test/java/smash/appointment/parse/TestBase.java b/appointment-import/src/test/java/smash/appointment/parse/TestBase.java index e38cc5c3..72704a8a 100644 --- a/appointment-import/src/test/java/smash/appointment/parse/TestBase.java +++ b/appointment-import/src/test/java/smash/appointment/parse/TestBase.java @@ -1,27 +1,25 @@ package smash.appointment.parse; -import java.util.ArrayList; -import java.util.List; - -import org.junit.Before; - public class TestBase { SubjectDao subjectDao; Subject piotrGawron = new Subject("Piotr", "Gawron", "ND0001", "1"); Subject janKowalskiNowak = new Subject("Jan", "Kowalski-Nowak", "ND0002", "2"); Subject johnDoe = new Subject("John", "Doe", "ND0003", "3"); - Subject cateKowalsky = new Subject("Cate", "Kowalsky", "ND0004", "4"); + Subject cateKowalsky = new Subject("Cate", "Kowalsky", "ND0004", "4"); + Subject andrewDude = new Subject(" Andrew ", " Dude ", "ND0005", "5"); + Subject unknownDude = new Subject(" Unknownnnnnnnn ", " Dude ", "ND0006", "6"); public void setUp() { subjectDao = new SubjectDao(); - subjectDao.addSubject(new Subject("Piotr Marcin", "Gawron", "ND0005", "5")); + subjectDao.addSubject(new Subject("Piotr Marcin", "Gawron", "ND1005", "1005")); subjectDao.addSubject(piotrGawron); subjectDao.addSubject(janKowalskiNowak); subjectDao.addSubject(johnDoe); subjectDao.addSubject(cateKowalsky); + subjectDao.addSubject(andrewDude); + subjectDao.addSubject(unknownDude); } - } diff --git a/appointment-import/src/test/java/smash/appointment/parse/XlsxCalendarProcessorTest.java b/appointment-import/src/test/java/smash/appointment/parse/XlsxCalendarProcessorTest.java index 8c07b601..7625586d 100644 --- a/appointment-import/src/test/java/smash/appointment/parse/XlsxCalendarProcessorTest.java +++ b/appointment-import/src/test/java/smash/appointment/parse/XlsxCalendarProcessorTest.java @@ -1,5 +1,6 @@ package smash.appointment.parse; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.FileNotFoundException; @@ -11,17 +12,17 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; -public class XlsxCalendarProcessorTest extends TestBase{ - Logger logger = Logger.getLogger(XlsxCalendarProcessorTest .class); - - XlsxCalendarProcessor processor = new XlsxCalendarProcessor(); +public class XlsxCalendarProcessorTest extends TestBase { + Logger logger = Logger.getLogger(XlsxCalendarProcessorTest.class); + + XlsxCalendarProcessor processor = new XlsxCalendarProcessor(); @AfterClass public static void tearDownAfterClass() throws Exception { } @Before - public void setUp() { + public void setUp() { super.setUp(); processor.setSubjectDao(subjectDao); } @@ -34,9 +35,6 @@ public class XlsxCalendarProcessorTest extends TestBase{ public void testReadExcel() throws Exception { List<AppointmentEntry> entries = processor.processExcel("testFiles/calendarExample.xlsx"); assertTrue(entries.size() > 0); -// for (AppointmentEntry appointmentEntry : entries) { -// logger.debug(appointmentEntry); -// } } } diff --git a/appointment-import/testFiles/redcap_imp.txt b/appointment-import/testFiles/redcap_imp.txt new file mode 100644 index 00000000..34ea9b17 --- /dev/null +++ b/appointment-import/testFiles/redcap_imp.txt @@ -0,0 +1,7 @@ +Subject ID Event Name Date/Time of Birth Sex Language: most fluent Language: second most fluent Language: third most fluent Language: fourth most fluent Basic assessment (Level A) Basic assessment: start date Basic assessment: end date Basic assessment: number of visits Detailed neuropsychological assessment (Level B) Detailed neuropsychological assessment: start date Detailed neuropsychological assessment: end date Detailed neuropsychological assessment: number of visits Detailed motor assessment (Level B) Detailed motor assessment: start date Detailed motor assessment: end date Detailed motor assessment: number of visits Detailed sensory assessment (Level B) Detailed sensory assessment: start date Detailed sensory assessment: end date Detailed sensory assessment: number of visits PSP (Level B) PSP: start date PSP: end date PSP: number of visits Blood Blood: date Urine Urine: date CSF CSF: date Imaging Imaging: date Stool Stool: collection acceptance by patient Stool: collection date proposed Stool: date collected Skin biopsy Skin biopsy: date Saliva Saliva: date mPower mPower: date +ND0001 Visit 1 1955-10-24 10:44 M Polish German French Yes 2015-01-04 10:14 2015-03-04 14:18 1 Yes 2015-03-04 Yes 2015-03-04 Yes 2016-10-20 Yes 2015-03-04 2015-03-11 +ND0001 Visit 2 Yes 2016-12-12 09:34 2017-01-24 10:46 2 +ND0002 Visit 1 1956-05-22 14:53 M Polish Yes 2015-03-04 14:56 2015-03-04 17:43 1 Yes 2015-07-29 2015-07-29 1 No No No Yes 2015-05-13 Yes 2015-05-13 No No No No Yes 2015-05-13 +ND0002 Visit 2 Yes 2016-01-18 10:00 2016-01-18 13:10 1 Yes 2016-05-26 2016-05-26 1 Yes 2016-01-18 Yes 2016-01-18 Yes Yes 2016-05-26 2016-05-26 Yes 2016-05-26 Yes 2016-01-18 +ND0003 Visit 1 1957-07-15 09:21 F Polish German Rusian English Yes 2015-03-11 09:25 2015-03-11 14:58 1 Yes 2015-11-12 2015-11-12 1 Yes 2015-03-11 Yes 2015-03-11 Yes 2015-03-11 +ND0003 Visit 2 Yes 2016-04-27 11:31 2016-05-11 13:30 2 Yes 2016-06-16 2016-06-16 1 Yes 2016-08-18 2016-08-18 1 Yes 2016-04-27 Yes 2016-04-27 Yes Yes 2016-04-27 2016-04-28 Yes 2016-07-28 Yes 2016-04-27 -- GitLab