diff --git a/appointment-import/src/main/java/smash/appointment/parse/AppointmentDao.java b/appointment-import/src/main/java/smash/appointment/parse/AppointmentDao.java index cb965abff90d22aecbb62626c0d27731314c7d3f..ab5871502c8445b74fb03c13d8f988b1a8acb664 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/AppointmentDao.java +++ b/appointment-import/src/main/java/smash/appointment/parse/AppointmentDao.java @@ -62,9 +62,9 @@ public class AppointmentDao { String date1 = o1.getDay().substring(0, 10); String date2 = o2.getDay().substring(0, 10); if (date1.compareTo(date2) == 0) { - if (o1.getTypes().contains(AppointmentType.LEVEL_A) || o1.getTypes().contains(AppointmentType.LEVEL_A_TQ)) { + if (o1.getTypes().contains(AppointmentType.LEVEL_AQUEST) || o1.getTypes().contains(AppointmentType.LEVEL_A_TQ)) { return -1; - } else if (o2.getTypes().contains(AppointmentType.LEVEL_A) || o2.getTypes().contains(AppointmentType.LEVEL_A_TQ)) { + } else if (o2.getTypes().contains(AppointmentType.LEVEL_AQUEST) || o2.getTypes().contains(AppointmentType.LEVEL_A_TQ)) { return 1; } else { return 0; @@ -79,8 +79,11 @@ public class AppointmentDao { List<Visit> result = new ArrayList<>(); Visit currentVisit = new Visit(subject); for (AppointmentEntry appointmentEntry : list) { - if (appointmentEntry.getTypes().contains(AppointmentType.LEVEL_A) || appointmentEntry.getTypes().contains(AppointmentType.LEVEL_A_TQ)) { + if (appointmentEntry.getTypes().contains(AppointmentType.LEVEL_AQUEST) || appointmentEntry.getTypes().contains(AppointmentType.LEVEL_A_TQ)) { if (currentVisit.getAppointments().size() > 0) { + if (shouldBeFinished(currentVisit.getEndDate())) { + currentVisit.setFinished(true); + } result.add(currentVisit); } @@ -101,7 +104,10 @@ public class AppointmentDao { result.add(currentVisit); } if (shouldBeFinished(currentVisit.getEndDate())) { - result.add(createNextVisit(currentVisit)); + currentVisit.setFinished(true); + if (!subject.isDead() && !subject.isResigned()) { + result.add(createNextVisit(currentVisit)); + } } return result; } @@ -109,7 +115,7 @@ public class AppointmentDao { protected Visit createNextVisit(Visit currentVisit) throws ParseException { Visit visit = new Visit(currentVisit.getSubject()); Calendar date = Calendar.getInstance(); - String dateStr =currentVisit.getStartDate(); + String dateStr = currentVisit.getStartDate(); date.setTime(DATE_FORMATTER.parse(dateStr)); if (currentVisit.getSubject().getType().equals(SubjectType.CONTROL)) { date.add(Calendar.YEAR, 4); diff --git a/appointment-import/src/main/java/smash/appointment/parse/AppointmentType.java b/appointment-import/src/main/java/smash/appointment/parse/AppointmentType.java index bceb087694fe573ae698e4251a7364557624f402..a4e9aec8774b7cec2379e17d3e78ae89c7f42168 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/AppointmentType.java +++ b/appointment-import/src/main/java/smash/appointment/parse/AppointmentType.java @@ -6,7 +6,11 @@ public enum AppointmentType { LEVEL_SB(30,"SB"), // LEVEL_A_TQ(120,"A_TQ"), // - LEVEL_A(120,"A"), // + LEVEL_AN(60,"AN"), // + LEVEL_ANP(60,"ANP"), // + LEVEL_ASAMP(60,"ASAMP"), // + LEVEL_AWELC(15,"AWELC"), // + LEVEL_AQUEST(0,"AQUEST"), // LEVEL_B(90,"B"), // LEVEL_B_M_POWER(70,"mPower"), // OTHER(60,"OTHER"), // diff --git a/appointment-import/src/main/java/smash/appointment/parse/AppointmentTypeCollection.java b/appointment-import/src/main/java/smash/appointment/parse/AppointmentTypeCollection.java index 63797a4d2e20e4276b351e7a724e4fd992e64d7a..0799c0e5124e2c6eefb519059962135947c39885 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/AppointmentTypeCollection.java +++ b/appointment-import/src/main/java/smash/appointment/parse/AppointmentTypeCollection.java @@ -1,9 +1,9 @@ package smash.appointment.parse; public enum AppointmentTypeCollection { - LEVEL_A_BV_BG(new AppointmentType[] { AppointmentType.LEVEL_A, AppointmentType.LEVEL_BV, AppointmentType.LEVEL_BG }, // + LEVEL_A_BV_BG(new AppointmentType[] { AppointmentType.LEVEL_ANP, AppointmentType.LEVEL_BV, AppointmentType.LEVEL_BG }, // new String[] { "BV + BG + neuro level A" }), // - LEVEL_A_B(new AppointmentType[] { AppointmentType.LEVEL_A, AppointmentType.LEVEL_B }, // + LEVEL_A_B(new AppointmentType[] { AppointmentType.LEVEL_ANP, AppointmentType.LEVEL_B }, // new String[] { "level B + level A neuro" }), // LEVEL_B_BV_SB(new AppointmentType[] { AppointmentType.LEVEL_B, AppointmentType.LEVEL_BV, AppointmentType.LEVEL_SB }, // @@ -27,6 +27,10 @@ public enum AppointmentTypeCollection { new String[] { "level BG + SB + mPower" }), // LEVEL_BV_BG_SB(new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_BG, AppointmentType.LEVEL_SB }, // new String[] { "evel BV + BG + SB", "BV + BG + SB" }), // + LEVEL_BV_SB_NEURO(new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_SB, AppointmentType.LEVEL_AN }, // + new String[] { "BV + SB + neuro" }), // + LEVEL_BV_SB_M_POWER(new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_SB, AppointmentType.LEVEL_B_M_POWER}, // + new String[] { "BV + SB + mPower" }), // LEVEL_BV_SB(new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_SB }, // new String[] { "evel BV + SB", "BV + SB" }), // LEVEL_BV_BG(new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_BG }, // @@ -42,7 +46,7 @@ public enum AppointmentTypeCollection { LEVEL_A_TQ(new AppointmentType[] { AppointmentType.LEVEL_A_TQ }, // new String[] { "TQ" }), // - LEVEL_A(new AppointmentType[] { AppointmentType.LEVEL_A }, // + LEVEL_A(new AppointmentType[] { AppointmentType.LEVEL_AWELC, AppointmentType.LEVEL_AN, AppointmentType.LEVEL_ANP, AppointmentType.LEVEL_AQUEST, AppointmentType.LEVEL_ASAMP, }, // new String[] { "level A" }), // LEVEL_B(new AppointmentType[] { AppointmentType.LEVEL_B }, // new String[] { "evel B" }), // diff --git a/appointment-import/src/main/java/smash/appointment/parse/LihControlParser.java b/appointment-import/src/main/java/smash/appointment/parse/LihControlParser.java index fe44bb6fc33c679718a54aa51af1443565341be6..c5b587f466ee3e91a4eceabf1236072d493512bf 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/LihControlParser.java +++ b/appointment-import/src/main/java/smash/appointment/parse/LihControlParser.java @@ -171,6 +171,12 @@ public class LihControlParser extends SubjectParser { return "French"; case ("D"): return "German"; + case ("A"): + return "German"; + case ("E"): + return "Spanish"; + case ("ALL"): + return "German"; case ("GB"): return "English"; case ("P"): diff --git a/appointment-import/src/main/java/smash/appointment/parse/Main.java b/appointment-import/src/main/java/smash/appointment/parse/Main.java index 59a12ffb85d8be2c62b77c3c7db03416eae1dea6..0d7c34238f606a77641a54033fe56d92588d41a9 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/Main.java +++ b/appointment-import/src/main/java/smash/appointment/parse/Main.java @@ -80,11 +80,13 @@ public class Main { String redCapFile = line.getOptionValue("red-cap"); appointmentDao.addAppointments(processRedCapAppointments(redCapFile)); + System.out.println("delete from web_visit_appointment_types;"); System.out.println("delete from web_appointment_appointment_types;"); System.out.println("delete from web_subject_languages;"); System.out.println("delete from web_appointment;"); System.out.println("delete from web_visit;"); System.out.println("delete from web_subject;"); + SubjectSqlExporter subjectSqlExporter = new SubjectSqlExporter(); // logger.debug("SUBJECTS: "); for (Subject subject : subjectDao.getSubjects()) { @@ -95,13 +97,7 @@ public class Main { List<Visit> visits = appointmentDao.getVisits(); for (int i = 0; i < visits.size(); i++) { Visit visit = visits.get(i); - boolean finished = false; - if (i < visits.size() - 1) { - if (visit.getSubject().equals(visits.get(i + 1).getSubject())) { - finished = true; - } - } - System.out.println(visitSqlExporter.toSql(visit, finished)); + System.out.println(visitSqlExporter.toSql(visit)); } } catch (ParseException exp) { diff --git a/appointment-import/src/main/java/smash/appointment/parse/PrcFlyingParser.java b/appointment-import/src/main/java/smash/appointment/parse/PrcFlyingParser.java index 6d17297e7e541c23d252bd544dbca7a0e01e74af..168a3cfdd2d3443641e12d449d819fb6bcd439be 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/PrcFlyingParser.java +++ b/appointment-import/src/main/java/smash/appointment/parse/PrcFlyingParser.java @@ -11,7 +11,7 @@ public class PrcFlyingParser extends SubjectParser { @Override protected String parseScreeningNumber(Row row) { - String number = getString(row.getCell(21)); + String number = getString(row.getCell(22)); if (number.trim().isEmpty()) { return ""; } else { @@ -36,7 +36,7 @@ public class PrcFlyingParser extends SubjectParser { @Override protected String parseNdNumber(Row row) { - return getString(row.getCell(20)); + return getString(row.getCell(21)); } @Override @@ -51,7 +51,7 @@ public class PrcFlyingParser extends SubjectParser { @Override protected String parseBirthDate(Row row) { - return parseDateOfBirth(row.getCell(22)); + return parseDateOfBirth(row.getCell(23)); } @Override @@ -61,12 +61,12 @@ public class PrcFlyingParser extends SubjectParser { @Override protected String parseAddDate(Row row) { - return getDate(row.getCell(12),Calendar.getInstance()); + return getDate(row.getCell(13),Calendar.getInstance()); } @Override protected String parseReferal(Row row) { - return getString(row.getCell(11)); + return getString(row.getCell(12)); } @Override @@ -76,7 +76,7 @@ public class PrcFlyingParser extends SubjectParser { @Override protected String parseMail(Row row) { - return getString(row.getCell(8)); + return getString(row.getCell(9)); } @Override @@ -86,38 +86,38 @@ public class PrcFlyingParser extends SubjectParser { @Override protected String parsePhone2(Row row) { - return getString(row.getCell(7)); + return getString(row.getCell(8)); } @Override protected String parsePhone1(Row row) { - return getString(row.getCell(6)); + return getString(row.getCell(7)); } @Override protected String parseCity(Row row) { - return getString(row.getCell(4)); + return getString(row.getCell(5)); } @Override protected String parseCountry(Row row) { - return getString(row.getCell(5)); + return getString(row.getCell(6)); } @Override protected String parseZipCode(Row row) { - return getString(row.getCell(3)); + return getString(row.getCell(4)); } @Override protected String parseAddress(Row row) { - return getString(row.getCell(2)); + return getString(row.getCell(3)); } @Override protected String parseRemarks(Row row) { - String remark1 = getString(row.getCell(9)); - String remark2 = getString(row.getCell(18)); + String remark1 = getString(row.getCell(10)); + String remark2 = getString(row.getCell(19)); String result = ""; if (!remark1.trim().isEmpty()) { @@ -131,17 +131,18 @@ public class PrcFlyingParser extends SubjectParser { @Override protected String parseDiagnosis(Row row) { - return getString(row.getCell(10)); + return getString(row.getCell(11)); } @Override protected SubjectType parseType(Row row) { - String name = (parseName(row) + " " + parseSurname(row)).toLowerCase(); - if (name.indexOf("(c)") >= 0 || name.indexOf("©") >= 0) { + String str = getString(row.getCell(2)).toLowerCase().trim(); + if (str.startsWith("c")) { return SubjectType.CONTROL; - } else { + } else if (str.startsWith("p")) { return SubjectType.PATIENT; } + return SubjectType.PATIENT; } @Override diff --git a/appointment-import/src/main/java/smash/appointment/parse/RedcapParser.java b/appointment-import/src/main/java/smash/appointment/parse/RedcapParser.java index 2f4615a2f9bf9e82ebd980a1a31301119dd70e17..59e8eaf9cf7e9c07a289e19a01196c3c91e8a820 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/RedcapParser.java +++ b/appointment-import/src/main/java/smash/appointment/parse/RedcapParser.java @@ -166,7 +166,7 @@ public class RedcapParser { entry.setSubject(subject); entry.setSource("Imported from RedCap"); entry.setTime(time); - entry.addType(AppointmentType.LEVEL_A); + entry.addTypes(AppointmentTypeCollection.LEVEL_A.getTypes()); entry.setLocation(subject.getToBeSeenAt()); return entry; } diff --git a/appointment-import/src/main/java/smash/appointment/parse/SubjectSqlExporter.java b/appointment-import/src/main/java/smash/appointment/parse/SubjectSqlExporter.java index 0e3389198112f172885be125dc195b11e2767dac..ebbd5866b0fa122704b3a0ef8a160a234372147b 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/SubjectSqlExporter.java +++ b/appointment-import/src/main/java/smash/appointment/parse/SubjectSqlExporter.java @@ -28,6 +28,7 @@ public class SubjectSqlExporter extends SqlExporter { result.append("default_location_id,"); result.append("type,"); result.append("dead,"); + result.append("default_written_communication_language_id,"); result.append("resigned,"); result.append("date_born) "); @@ -67,6 +68,11 @@ public class SubjectSqlExporter extends SqlExporter { result.append(getStringVal(subject.getType().toString().substring(0, 1)) + ","); result.append(subject.isDead() + ","); + if (subject.getLanguages().size()>0) { + result.append("(select id from web_language where name=" + getStringVal(subject.getLanguages().get(0)) + "),"); + } else { + result.append("null,"); + } result.append(subject.isResigned() + ","); result.append(getDateVal(subject.getBirthDate())); result.append(");\n"); diff --git a/appointment-import/src/main/java/smash/appointment/parse/Visit.java b/appointment-import/src/main/java/smash/appointment/parse/Visit.java index b585c524ba3264bef582ee8c4657f1a9b65b4077..98b2a896b1b67821ce78a64ec5901a4f2344aeee 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/Visit.java +++ b/appointment-import/src/main/java/smash/appointment/parse/Visit.java @@ -11,7 +11,7 @@ import org.apache.log4j.Logger; public class Visit { static Subject UNKNOWN; static { - UNKNOWN = new Subject("NOBODY","","","---"); + UNKNOWN = new Subject("NOBODY", "", "", "---"); UNKNOWN.setType(SubjectType.CONTROL); UNKNOWN.setAddDate("1900-01-01"); UNKNOWN.setAddress(""); @@ -28,11 +28,12 @@ public class Visit { private Subject subject; private List<AppointmentEntry> appointments = new ArrayList<>(); - - private String startDate; + + private String startDate; + private boolean finished = false; public Visit(Subject subject) { - if (subject==null) { + if (subject == null) { subject = UNKNOWN; } this.subject = subject; @@ -77,7 +78,7 @@ public class Visit { public String getStartDate() { if (appointments.size() > 0) { return appointments.get(0).getDay().substring(0, 10); - } else if (startDate==null){ + } else if (startDate == null) { return "1900-01-01"; } else { return startDate; @@ -114,4 +115,20 @@ public class Visit { this.startDate = newDate; } + /** + * @return the finished + * @see #finished + */ + public boolean isFinished() { + return finished; + } + + /** + * @param finished the finished to set + * @see #finished + */ + public void setFinished(boolean finished) { + this.finished = finished; + } + } diff --git a/appointment-import/src/main/java/smash/appointment/parse/VisitSqlExporter.java b/appointment-import/src/main/java/smash/appointment/parse/VisitSqlExporter.java index 730b6c09cfc0d9e9d432ab5ed42f0322db30a5f6..6ffef703b3104abf444a31be9942ea409f59fa9b 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/VisitSqlExporter.java +++ b/appointment-import/src/main/java/smash/appointment/parse/VisitSqlExporter.java @@ -5,7 +5,7 @@ import java.util.Calendar; public class VisitSqlExporter extends SqlExporter { AppointmentSqlExporter appointmentSqlExporter = new AppointmentSqlExporter(); - public String toSql(Visit visit, boolean isFinished) throws ParseException { + public String toSql(Visit visit) throws ParseException { StringBuilder result = new StringBuilder(""); result.append("insert into web_visit ("); @@ -18,12 +18,9 @@ public class VisitSqlExporter extends SqlExporter { result.append("(SELECT id from web_subject where screening_number = "+getStringVal(visit.getSubject().getScreeningNumber()) + "),"); result.append(getStringVal(visit.getStartDate()) + ","); result.append(getStringVal(visit.getEndDate()) + ","); - result.append(isFinished); + result.append(visit.isFinished()); result.append(");\n"); for (AppointmentEntry entry: visit.getAppointments()) { - boolean entryFinished= isFinished; - if (isBefore(entry, Calendar.getInstance())) - entryFinished=true; result.append(appointmentSqlExporter.toSql(entry)+"\n"); } diff --git a/appointment-import/src/main/java/smash/appointment/parse/XlsxCalendarProcessor.java b/appointment-import/src/main/java/smash/appointment/parse/XlsxCalendarProcessor.java index 0c6e2a1419f1cbfc83e1eaafebedafed8553ca88..57040b275b38bed01495b615d2a2e94b66b5c6fc 100644 --- a/appointment-import/src/main/java/smash/appointment/parse/XlsxCalendarProcessor.java +++ b/appointment-import/src/main/java/smash/appointment/parse/XlsxCalendarProcessor.java @@ -102,7 +102,7 @@ public class XlsxCalendarProcessor { if (dayOfMonth.length() == 1) { dayOfMonth = "0" + dayOfMonth; } - if (!dayOfMonth.equals("00")) { + if (!dayOfMonth.equals("00") && !dayOfMonth.trim().isEmpty()) { String day = string + "-" + dayOfMonth; String hour = "08:00"; diff --git a/appointment-import/src/test/java/smash/appointment/parse/AppointmentDaoTest.java b/appointment-import/src/test/java/smash/appointment/parse/AppointmentDaoTest.java index 98c1a8ad0fb2f444c7506a4c44b24efcf62fab45..c0339f690a2c346a0aaf65a7865f8824996bd79b 100644 --- a/appointment-import/src/test/java/smash/appointment/parse/AppointmentDaoTest.java +++ b/appointment-import/src/test/java/smash/appointment/parse/AppointmentDaoTest.java @@ -34,7 +34,7 @@ public class AppointmentDaoTest { AppointmentEntry appointment = new AppointmentEntry(); appointment.setSubject(subject1); appointment.setDay("2026-02-02"); - appointment.addType(AppointmentType.LEVEL_A); + appointment.addType(AppointmentType.LEVEL_AQUEST); AppointmentEntry appointment2 = new AppointmentEntry(); appointment2.setSubject(subject1); @@ -44,7 +44,7 @@ public class AppointmentDaoTest { AppointmentEntry appointment3 = new AppointmentEntry(); appointment3.setSubject(subject1); appointment3.setDay("2021-02-02"); - appointment3.addType(AppointmentType.LEVEL_A); + appointment3.addType(AppointmentType.LEVEL_AQUEST); AppointmentDao appointmentDao = new AppointmentDao(); appointmentDao.addAppointment(appointment); @@ -60,7 +60,7 @@ public class AppointmentDaoTest { AppointmentEntry appointment = new AppointmentEntry(); appointment.setSubject(subject1); appointment.setDay("2026-02-02"); - appointment.addType(AppointmentType.LEVEL_A); + appointment.addType(AppointmentType.LEVEL_AQUEST); AppointmentEntry appointment3 = new AppointmentEntry(); appointment3.setSubject(subject2); 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 56ae493787677dad7bf08861061720b5302a161e..1c0290fe7f9426312100b8adf7654d94586fb4c9 100644 --- a/appointment-import/src/test/java/smash/appointment/parse/CellParserTest.java +++ b/appointment-import/src/test/java/smash/appointment/parse/CellParserTest.java @@ -28,8 +28,8 @@ public class CellParserTest extends TestBase { testCases = new ArrayList<CellParseTestCase>(); - testCases.add(new CellParseTestCase("Piotr Gawron level A FU V3", piotrGawron, null, new AppointmentType[] { AppointmentType.LEVEL_A })); - testCases.add(new CellParseTestCase("09:00 Jan Kowalski-Nowak level A", janKowalskiNowak, "09:00", new AppointmentType[] { AppointmentType.LEVEL_A })); + testCases.add(new CellParseTestCase("Piotr Gawron level A FU V3", piotrGawron, null, AppointmentTypeCollection.LEVEL_A.getTypes())); + testCases.add(new CellParseTestCase("09:00 Jan Kowalski-Nowak level A", janKowalskiNowak, "09:00", AppointmentTypeCollection.LEVEL_A.getTypes())); testCases.add( new CellParseTestCase( "ND0002 l664574645 (sms)evel BV © + SB ©", janKowalskiNowak, null, new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_SB })); @@ -45,16 +45,16 @@ public class CellParserTest extends TestBase { new CellParseTestCase( "Cate Kowalsky level BV + BG + SB + m-Power", cateKowalsky, null, new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_BG, AppointmentType.LEVEL_SB, AppointmentType.LEVEL_B_M_POWER })); - testCases.add(new CellParseTestCase("sb name level A", null, null, new AppointmentType[] { AppointmentType.LEVEL_A })); - testCases.add(new CellParseTestCase("Andrew Dude level A FU V3", andrewDude, null, new AppointmentType[] { AppointmentType.LEVEL_A })); + testCases.add(new CellParseTestCase("sb name level A", null, null, AppointmentTypeCollection.LEVEL_A.getTypes())); + testCases.add(new CellParseTestCase("Andrew Dude level A FU V3", andrewDude, null, AppointmentTypeCollection.LEVEL_A.getTypes())); testCases.add( new CellParseTestCase( "Gawron Piotr BV + BG + neuro level A (FU)", piotrGawron, null, - new AppointmentType[] { AppointmentType.LEVEL_A, AppointmentType.LEVEL_BV, AppointmentType.LEVEL_BG })); + new AppointmentType[] { AppointmentType.LEVEL_ANP, AppointmentType.LEVEL_BV, AppointmentType.LEVEL_BG })); testCases.add( new CellParseTestCase( - "Gawron Piotr level B © + level A neuro ©", piotrGawron, null, new AppointmentType[] { AppointmentType.LEVEL_A, AppointmentType.LEVEL_B })); + "Gawron Piotr level B © + level A neuro ©", piotrGawron, null, new AppointmentType[] { AppointmentType.LEVEL_ANP, AppointmentType.LEVEL_B })); testCases.add( new CellParseTestCase( @@ -83,7 +83,17 @@ public class CellParserTest extends TestBase { new CellParseTestCase( "Gawron Piotr level BG + SB + M-Power", piotrGawron, null, new AppointmentType[] { AppointmentType.LEVEL_BG, AppointmentType.LEVEL_SB, AppointmentType.LEVEL_B_M_POWER })); + testCases.add( + new CellParseTestCase( + "Gawron Piotr BV © + SB © + neuro", piotrGawron, null, + new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_SB, AppointmentType.LEVEL_AN })); + testCases.add( + new CellParseTestCase( + "Gawron Piotr BV © + SB © + M-Power", piotrGawron, null, + new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_SB, AppointmentType.LEVEL_B_M_POWER })); + + } @Test diff --git a/appointment-import/src/test/java/smash/appointment/parse/PrcFlyingParserTest.java b/appointment-import/src/test/java/smash/appointment/parse/PrcFlyingParserTest.java index 222e092c6a1544fb79baebc876032ed8c00bc78d..6c34a90a4af6520dc27a332f18662fc711e06d90 100644 --- a/appointment-import/src/test/java/smash/appointment/parse/PrcFlyingParserTest.java +++ b/appointment-import/src/test/java/smash/appointment/parse/PrcFlyingParserTest.java @@ -36,6 +36,7 @@ public class PrcFlyingParserTest extends TestBase { Subject subject = entries.get(0); assertEquals("F-222", subject.getScreeningNumber()); + assertEquals(SubjectType.CONTROL, subject.getType()); assertEquals("DOE", subject.getSurname()); assertEquals("John", subject.getName()); assertTrue(subject.getRemarks().contains("notes")); diff --git a/appointment-import/src/test/java/smash/appointment/parse/RedcapParserTest.java b/appointment-import/src/test/java/smash/appointment/parse/RedcapParserTest.java index e912829f7d1345aeedb3b5aa079e1d35c0eff59b..ba1be9f13d5c784e651537a48585808616e1cba9 100644 --- a/appointment-import/src/test/java/smash/appointment/parse/RedcapParserTest.java +++ b/appointment-import/src/test/java/smash/appointment/parse/RedcapParserTest.java @@ -43,7 +43,7 @@ public class RedcapParserTest extends TestBase{ int levelSBCount = 0; int levelMPowerCount = 0; for (AppointmentEntry appointmentEntry : entries) { - if (appointmentEntry.getTypes().contains(AppointmentType.LEVEL_A)) { + if (appointmentEntry.getTypes().contains(AppointmentType.LEVEL_AQUEST)) { levelACount++; } if (appointmentEntry.getTypes().contains(AppointmentType.LEVEL_B)) { diff --git a/appointment-import/testFiles/prcFlyingTeam.xlsx b/appointment-import/testFiles/prcFlyingTeam.xlsx index a3296b81a92b108d4d31115c134211c19223c306..891faa078123cf6c7d6d0b676afc1ce763df549a 100644 Binary files a/appointment-import/testFiles/prcFlyingTeam.xlsx and b/appointment-import/testFiles/prcFlyingTeam.xlsx differ diff --git a/smash/web/forms.py b/smash/web/forms.py index b8866f178c2da1094f2633e42a432c1740ea2dec..5df96b139a5ce82909d37a17021ccff7dca13724 100644 --- a/smash/web/forms.py +++ b/smash/web/forms.py @@ -25,7 +25,7 @@ def validate_subject_nd_number(self): if subject['nd_number']!="": subjects_from_db = Subject.objects.filter(nd_number=subject['nd_number']) if (len(subjects_from_db)>0): - if (subjects_from_db[0].screening_number!= subject['screening_number']): + if subjects_from_db[0].screening_number!= subject.get('screening_number',''): self.add_error('nd_number', "ND number already in use") @@ -42,31 +42,13 @@ class SubjectAddForm(ModelForm): def clean(self): subject = self.cleaned_data - subjects_from_db = Subject.objects.filter(screening_number=subject['screening_number']) + subjects_from_db = Subject.objects.filter(screening_number=subject.get('screening_number','')) if len(subjects_from_db)>0: self.add_error('screening_number', "Screening number already in use") validate_subject_nd_number(self) - def get_new_screening_number(self): - result = 1; - numbers = Subject.objects.values_list('screening_number') - for number_row in numbers: - try: - id = int(number_row[0]) - result = max(result, id+1) - except: - pass - return result - - - def clean_screening_number(self): - if self.cleaned_data['screening_number']=="": - self.cleaned_data['screening_number'] = self.get_new_screening_number() - - return self.cleaned_data['screening_number']; - class SubjectDetailForm(ModelForm): class Meta: model = Subject @@ -170,6 +152,7 @@ class VisitAddForm(ModelForm): exclude = ['is_finished'] def clean(self): + print self.cleaned_data['appointment_types'] if (self.cleaned_data['datetime_begin']>=self.cleaned_data['datetime_end']): self.add_error('datetime_begin', "Start date must be before end date") self.add_error('datetime_end', "Start date must be before end date") diff --git a/smash/web/models.py b/smash/web/models.py index 663a5a3530df7320a198fe16c43892002cc48977..170690befbed6227eebfec5f2b8a61208187b113 100644 --- a/smash/web/models.py +++ b/smash/web/models.py @@ -50,14 +50,33 @@ class Subject(models.Model): ('P','PATIENT'), ) + + def finish_all_visits(self): + visits = Visit.objects.filter(subject = self, is_finished = False) + for visit in visits: + visit.is_finished = True + visit.save() + + def finish_all_appointments(self): + appointments = Appointment.objects.filter(visit__subject = self, status = Appointment.APPOINTMENT_STATUS_SCHEDULED) + for appointment in appointments: + appointment.status = Appointment.APPOINTMENT_STATUS_CANCELLED + appointment.save() + def mark_as_dead(self): self.dead = True self.save() + self.finish_all_visits() + self.finish_all_appointments() + def mark_as_rejected(self): self.resigned = True self.save() + self.finish_all_visits() + self.finish_all_appointments() + sex = models.CharField(max_length=1, choices=SEX_CHOICES, verbose_name='Sex' @@ -90,6 +109,12 @@ class Subject(models.Model): blank=True, verbose_name='Known languages' ) + default_written_communication_language = models.ForeignKey(Language, + null=True, + blank=True, + related_name="%(class)s_written_comunication", + verbose_name='Default language for document generation' + ) phone_number = models.CharField(max_length=20, null=True, blank=True, @@ -131,7 +156,6 @@ class Subject(models.Model): verbose_name='Country' ) screening_number = models.CharField(max_length=50, - blank=True, unique=True, verbose_name='Screening number' ) @@ -184,51 +208,6 @@ class Subject(models.Model): return "%s %s" % (self.first_name, self.last_name) -class Visit(models.Model): - subject = models.ForeignKey(Subject, on_delete=models.CASCADE, - verbose_name='Subject' - ) - datetime_begin = models.DateTimeField( - verbose_name='Visit starts at' - ) - datetime_end = models.DateTimeField( - verbose_name='Visit ends at' - ) # Deadline before which all appointments need to be scheduled - - is_finished = models.BooleanField( - verbose_name='Has ended', - default=False - ) - def __unicode__(self): - return "%s %s" % (self.subject.first_name, self.subject.last_name) - - def __str__(self): - return "%s %s" % (self.subject.first_name, self.subject.last_name) - - def follow_up_title(self): - count = Visit.objects.filter(subject=self.subject, datetime_begin__lt =self.datetime_begin).count() - return "Visit " + str(count + 1) - - def mark_as_finished(self): - self.is_finished = True - self.save() - - visit_started = self.datetime_end - - appointments = Appointment.objects.filter(visit=self) - - for appointment in appointments: - visit_started = min(visit_started,appointment.datetime_when) - - time_to_next_visit = datetime.timedelta(days=365) - if self.subject.type== Subject.SUBJECT_TYPE_CHOICES_CONTROL: - time_to_next_visit = datetime.timedelta(days=365*3+366) - - Visit.objects.create( - subject = self.subject, - datetime_begin = visit_started+time_to_next_visit, - datetime_end = visit_started+time_to_next_visit+datetime.timedelta(days=93) - ) class Item (models.Model): is_fixed = models.BooleanField( @@ -309,7 +288,8 @@ class AppointmentType (models.Model): default=DEFAULT_FONT_COLOR ) rest_time = models.IntegerField( - verbose_name='Suggested rest time' + verbose_name='Suggested rest time', + default=0 ) REQ_ROLE_CHOICES = ( ('DOCTOR', 'Doctor'), @@ -401,27 +381,39 @@ class Worker (models.Model): class FlyingTeam(models.Model): - doctor = models.ForeignKey(Worker, related_name='FlyingTeamDoctor', - verbose_name='Doctor' - ) - nurse = models.ForeignKey(Worker, related_name='FlyingTeamNurse', - verbose_name='Nurse' - ) - psychologist = models.ForeignKey(Worker, related_name='FlyingTeamPsychologist', - verbose_name='Psychologist' - ) - datetime_called = models.DateTimeField( - verbose_name='Created on' - ) - datetime_until = models.DateTimeField( - verbose_name='Disbanded on' + # doctor = models.ForeignKey(Worker, related_name='FlyingTeamDoctor', + # verbose_name='Doctor' + # ) + # nurse = models.ForeignKey(Worker, related_name='FlyingTeamNurse', + # verbose_name='Nurse' + # ) + # psychologist = models.ForeignKey(Worker, related_name='FlyingTeamPsychologist', + # verbose_name='Psychologist' + # ) + # datetime_called = models.DateTimeField( + # verbose_name='Created on' + # ) + # datetime_until = models.DateTimeField( + # verbose_name='Disbanded on' + # ) + # + # def __str__(self): + # return "%s %s %s" % (self.doctor.last_name, self.nurse.last_name, self.psychologist.last_name) + # + # def __unicode__(self): + # return "%s %s %s" % (self.doctor.last_name, self.nurse.last_name, self.psychologist.last_name) + + + place = models.CharField(max_length=255, + verbose_name='Place', ) def __str__(self): - return "%s %s %s" % (self.doctor.last_name, self.nurse.last_name, self.psychologist.last_name) + return "%s" % (self.place) def __unicode__(self): - return "%s %s %s" % (self.doctor.last_name, self.nurse.last_name, self.psychologist.last_name) + return "%s" % (self.place) + class Avaibility(models.Model): @@ -466,6 +458,53 @@ class Holiday(models.Model): def __unicode__(self): return "%s %s" % (self.person.first_name, self.person.last_name) +class Visit(models.Model): + subject = models.ForeignKey(Subject, on_delete=models.CASCADE, + verbose_name='Subject' + ) + datetime_begin = models.DateTimeField( + verbose_name='Visit starts at' + ) + datetime_end = models.DateTimeField( + verbose_name='Visit ends at' + ) # Deadline before which all appointments need to be scheduled + + is_finished = models.BooleanField( + verbose_name='Has ended', + default=False + ) + + appointment_types = models.ManyToManyField(AppointmentType, + verbose_name='Requested appointments', + blank=True, + ) + + def __unicode__(self): + return "%s %s" % (self.subject.first_name, self.subject.last_name) + + def __str__(self): + return "%s %s" % (self.subject.first_name, self.subject.last_name) + + def follow_up_title(self): + count = Visit.objects.filter(subject=self.subject, datetime_begin__lt =self.datetime_begin).count() + return "Visit " + str(count + 1) + + def mark_as_finished(self): + self.is_finished = True + self.save() + + if (not self.subject.dead) and (not self.subject.resigned): + visit_started = self.datetime_begin + + time_to_next_visit = datetime.timedelta(days=365) + if self.subject.type== Subject.SUBJECT_TYPE_CHOICES_CONTROL: + time_to_next_visit = datetime.timedelta(days=365*3+366) + + Visit.objects.create( + subject = self.subject, + datetime_begin = visit_started+time_to_next_visit, + datetime_end = visit_started+time_to_next_visit+datetime.timedelta(days=93) + ) class Appointment(models.Model): APPOINTMENT_STATUS_SCHEDULED = 'SCHEDULED'; @@ -489,6 +528,7 @@ class Appointment(models.Model): ) appointment_types = models.ManyToManyField(AppointmentType, verbose_name='Appointment types', + blank=True ) room = models.ForeignKey(Room, verbose_name='Room ID', @@ -558,7 +598,7 @@ class Appointment(models.Model): def title(self): if self.visit.subject.screening_number=="---": - return self.comment.replace("\n", ";") + return self.comment.replace("\n", ";").replace("\r", ";") else: title = self.visit.subject.first_name + " " + self.visit.subject.last_name + " type: " for type in self.appointment_types.all(): diff --git a/smash/web/templates/_base.html b/smash/web/templates/_base.html index 06e5dccf31f19942b5c389dbb8e5b443fac7fc9d..ffe2c8965ca37b4fdf14659f70e365713e6d0a1c 100644 --- a/smash/web/templates/_base.html +++ b/smash/web/templates/_base.html @@ -351,7 +351,7 @@ desired effect {% block footer %} <!-- To the right --> <div class="pull-right hidden-xs"> - Version: <strong>preview 0.2.0</strong> (7 Mar 2017) + Version: <strong>preview 0.2.1</strong> (9 Mar 2017) </div> <!-- Default to the left --> diff --git a/smash/web/templates/appointments/edit.html b/smash/web/templates/appointments/edit.html index 76515ca22b49cbc2a832d93e694be4c60a3fe0e9..50d598d8065e3e7f6bf13f1889d0285fd5b762c0 100644 --- a/smash/web/templates/appointments/edit.html +++ b/smash/web/templates/appointments/edit.html @@ -58,7 +58,7 @@ Status: </label> <div class="btn-group-vertical col-sm-8"> - <label class="btn btn-primary">{{ status }}</label> + <label class="btn btn-primary">{{ appointment.status }}</label> <a href="{% url 'web.views.appointment_mark' id 'finished' %}" class="btn btn-warning btn-block">Mark as finished</a> <a href="{% url 'web.views.appointment_mark' id 'cancelled' %}" class="btn btn-warning btn-block">Mark as cancelled</a> <a href="{% url 'web.views.appointment_mark' id 'no_show' %}" class="btn btn-warning btn-block">Mark as no show</a> @@ -74,6 +74,38 @@ <a href="{% url 'web.views.appointments' %}" class="btn btn-block btn-default" onclick="history.back()">Cancel</a> </div> </div><!-- /.box-footer --> + + <div class="box-header with-border"> + <h3 class="box-title">Subject's details</h3> + </div> + + <form class="form-horizontal"> + <div class="box-body"> + {% for field in subject_form %} + <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> + <label for="{# TODO #}" class="col-sm-4 control-label"> + {{ field.label }} + </label> + + <div class="col-sm-8"> + {{ field|disable|add_class:'form-control' }} + </div> + + {% if field.errors %} + <span class="help-block"> + {{ field.errors }} + </span> + {% endif %} + </div> + {% endfor %} + </div><!-- /.box-body --> + + <div class="box-footer"> + <a href="{% url 'web.views.subject_edit' appointment.visit.subject.id %}" type="button" class="btn btn-block btn-default">Edit subject</a> + <a href="{% url 'web.views.subjects' %}" class="btn btn-block btn-default" onclick="history.back()">Back</a> + </div><!-- /.box-footer --> + </form> + </form> </div> {% endblock %} diff --git a/smash/web/templates/appointments/index.html b/smash/web/templates/appointments/index.html index 0a44243a9216474997aa1b3d404d77cd4375f3b6..3e35a34f8672051eaef93d2a2c22d3b68566d9a4 100644 --- a/smash/web/templates/appointments/index.html +++ b/smash/web/templates/appointments/index.html @@ -41,7 +41,7 @@ <tr> <td>{{ planned.visit.subject.first_name }} {{ planned.visit.subject.last_name }}</td> <td> - <a href="{% url 'web.views.subject_details' planned.visit.subject.id %}" type="button" class="btn btn-block btn-default">Subject's details</a> + <a href="{% url 'web.views.subject_edit' planned.visit.subject.id %}" type="button" class="btn btn-block btn-default">Subject's details</a> </td> <td> {{ planned.datetime_when }} @@ -156,6 +156,7 @@ color: '{{ appointment.color }}', subject_id: '{{ appointment.visit.subject.id }}', id: '{{ appointment.id }}', + url: '{% url 'web.views.appointment_edit' appointment.id %}', }, {% endfor %} ], diff --git a/smash/web/templates/subjects/edit.html b/smash/web/templates/subjects/edit.html index 3f227a3196a3f2dfed056621a4e1a292fe3b54bd..124a4d539a080cf7f95e06660da5c89299d438a8 100644 --- a/smash/web/templates/subjects/edit.html +++ b/smash/web/templates/subjects/edit.html @@ -26,6 +26,7 @@ <div class="box box-info"> <div class="box-header with-border"> <a href="{% url 'web.views.subjects' %}" class="btn btn-block btn-default" onclick="history.back()">Go back (without changes)</a> + <a href="{% url 'web.views.subject_visit_details' subject.id %}" type = "button" class="btn btn-block btn-default">Subject's visits</a> </div> {% comment %} <div class="box-header with-border"> @@ -56,8 +57,20 @@ {% endfor %} <div class="col-md-6"> - <a href="{% url 'web.views.subject_mark' id 'rejected' %}" class="btn btn-warning btn-block">Mark as rejected</a> - <a href="{% url 'web.views.subject_mark' id 'dead' %}" class="btn btn-danger btn-block">Mark as dead</a><br /> + {% if not subject.resigned %} + <a href="{% url 'web.views.subject_mark' subject.id 'rejected' %}" class="btn btn-warning btn-block">Mark as rejected</a> + {% else %} + <label for="{# TODO #}" class="col-sm-4 control-label">REJECTED</label> + <div class="col-sm-8">{{subject.resigned}}</div> + {% endif %} + </div> + <div class="col-md-6"> + {% if not subject.dead %} + <a href="{% url 'web.views.subject_mark' subject.id 'dead' %}" class="btn btn-danger btn-block">Mark as dead</a><br /> + {% else %} + <label for="{# TODO #}" class="col-sm-4 control-label">DEAD</label> + <div class="col-sm-8">{{subject.dead}}</div> + {% endif %} </div> </div> diff --git a/smash/web/templates/subjects/index.html b/smash/web/templates/subjects/index.html index 9212f52b7b24dcbabd7f0d3b32fca919732bf600..991e049ceeaeba1c2294375208eaa7440bfee197 100644 --- a/smash/web/templates/subjects/index.html +++ b/smash/web/templates/subjects/index.html @@ -39,7 +39,6 @@ <th class="filter-select filter-exact" data-placeholder="Select location">Default location</th> <th>Dead</th> <th>Resigned</th> - <th>Details</th> <th>Edit</th> </tr> </thead> @@ -64,7 +63,6 @@ <td>{{ subject.get_default_appointment_location_display }}</td> <td>{% if subject.dead %} YES {% else %} NO {% endif %} </td> <td>{% if subject.resigned %} YES {% else %} NO {% endif %} </td> - <td><a href="{% url 'web.views.subject_details' subject.id %}" type="button" class="btn btn-block btn-default">Details</a></td> <td><a href="{% url 'web.views.subject_edit' subject.id %}" type="button" class="btn btn-block btn-default">Edit</a></td> </tr> {% endfor %} diff --git a/smash/web/templates/visits/details.html b/smash/web/templates/visits/details.html index 75b43f024c878ee78d91c1ef74e3cca90cae5d6e..66b9f979b2fbc000f06d30fedb70b8f9ba281539 100644 --- a/smash/web/templates/visits/details.html +++ b/smash/web/templates/visits/details.html @@ -35,7 +35,8 @@ </h3> </div> - <form class="form-horizontal"> + <form method="post" action="" class="form-horizontal"> + {% csrf_token %} <div class="box-body"> {% for field in vform %} <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> @@ -44,7 +45,7 @@ </label> <div class="col-sm-8"> - {{ field|disable|add_class:'form-control' }} + {{ field|add_class:'form-control' }} </div> {% if field.errors %} @@ -72,6 +73,11 @@ </div> </div> </div><!-- /.box-body --> + <div class="box-footer"> + <div class="col-sm-12"> + <button type="submit" class="btn btn-block btn-success">Save</button> + </div> + </div><!-- /.box-footer --> </form> @@ -162,6 +168,7 @@ </div><!-- /.box-body --> <div class="box-footer"> + <td><a href="{% url 'web.views.subject_edit' visit.subject.id %}" type="button" class="btn btn-block btn-default">Edit subject</a></td> <a href="{% url 'web.views.subjects' %}" class="btn btn-block btn-default" onclick="history.back()">Back</a> </div><!-- /.box-footer --> </form> diff --git a/smash/web/tests/functions.py b/smash/web/tests/functions.py index e343416887c17204c9ab04ee4ed91274b3c9a99e..c8c748908ff4ba8ee947fa06dfcc513efdf5457e 100644 --- a/smash/web/tests/functions.py +++ b/smash/web/tests/functions.py @@ -13,6 +13,13 @@ def get_test_location(): else: return create_location() +def create_appointment_type(): + return AppointmentType.objects.create( + code= "C", + default_duration="10", + description="test", + ) + def create_subject(): return Subject.objects.create( first_name="Piotr", diff --git a/smash/web/tests/test_SubjectAddForm.py b/smash/web/tests/test_SubjectAddForm.py index bbe6a553652b01e679857d3bc38b288bb68cfd3c..4e78578c3a93d9f901802242cfaae538c2fee014 100644 --- a/smash/web/tests/test_SubjectAddForm.py +++ b/smash/web/tests/test_SubjectAddForm.py @@ -12,6 +12,7 @@ class SubjectAddFormTests(TestCase): 'sex' : Subject.SEX_CHOICES_MALE, 'type' : Subject.SUBJECT_TYPE_CHOICES_CONTROL, 'default_location' : location.id, + 'screening_number' : "123", 'country' : 'Luxembourg' } def test_validation(self): @@ -42,7 +43,17 @@ class SubjectAddFormTests(TestCase): self.assertTrue(form.is_valid()) form.save() + form_data['screening_number'] = "2" form2 = SubjectAddForm(data=form_data) validation_status = form2.is_valid() self.assertFalse(validation_status) self.assertTrue("nd_number" in form2.errors) + + def test_invalid_3(self): + form_data = self.sample_data + form_data['screening_number'] = "" + + form = SubjectAddForm(data=form_data) + validation_status = form.is_valid() + self.assertFalse(validation_status) + self.assertTrue("screening_number" in form.errors) diff --git a/smash/web/tests/test_VisitAddForm.py b/smash/web/tests/test_VisitAddForm.py index d292124c4fd6b89f8e83a01c0f8ac451655c7519..2916e650bf12f88886fbcf157afc94d493751264 100644 --- a/smash/web/tests/test_VisitAddForm.py +++ b/smash/web/tests/test_VisitAddForm.py @@ -9,23 +9,20 @@ from web.tests.functions import * class SubjectAddFormTests(TestCase): def setUp(self): location = get_test_location() - subject_data = {'first_name': 'name', - 'last_name': 'name', - 'sex' : Subject.SEX_CHOICES_MALE, - 'type' : Subject.SUBJECT_TYPE_CHOICES_CONTROL, - 'default_location' : location.id, - 'country' : 'Luxembourg', - } - self.subject = SubjectAddForm(data=subject_data).save() + self.subject = create_subject() self.sample_data = {'datetime_begin': "2017-01-01", 'datetime_end': "2017-02-02", - 'subject' : self.subject.id + 'subject' : self.subject.id, + 'appointment_types' : '' + } def test_validation(self): form = VisitAddForm(data=self.sample_data) - self.assertTrue(form.is_valid()) + is_valid = form.is_valid() + print form.errors + self.assertTrue(is_valid) def test_invalid_validation(self): self.sample_data['datetime_begin'] = "2017-02-02" diff --git a/smash/web/tests/test_model_subject.py b/smash/web/tests/test_model_subject.py new file mode 100644 index 0000000000000000000000000000000000000000..4d9f479794896160bc3596ab91180facace87337 --- /dev/null +++ b/smash/web/tests/test_model_subject.py @@ -0,0 +1,37 @@ +from django.contrib.auth.models import User +from django.test import TestCase, RequestFactory +from django.urls import reverse + +from web.views import * + +from web.models import * + +from web.tests.functions import * + +class SubjectModelTests(TestCase): + + def test_mark_as_dead(self): + subject = create_subject() + visit = create_visit(subject) + appointment = create_appointment(visit) + + subject.mark_as_dead() + appointment_status = Appointment.objects.filter(id=appointment.id)[0].status + visit_finsihed = Visit.objects.filter(id=visit.id)[0].is_finished + + self.assertTrue(subject.dead) + self.assertTrue(visit_finsihed) + self.assertEquals(Appointment.APPOINTMENT_STATUS_CANCELLED, appointment_status) + + def test_mark_as_rejected(self): + subject = create_subject() + visit = create_visit(subject) + appointment = create_appointment(visit) + + subject.mark_as_rejected() + appointment_status = Appointment.objects.filter(id=appointment.id)[0].status + visit_finsihed = Visit.objects.filter(id=visit.id)[0].is_finished + + self.assertTrue(subject.resigned) + self.assertTrue(visit_finsihed) + self.assertEquals(Appointment.APPOINTMENT_STATUS_CANCELLED, appointment_status) diff --git a/smash/web/tests/test_model_visit.py b/smash/web/tests/test_model_visit.py new file mode 100644 index 0000000000000000000000000000000000000000..670d27346c764aa7deb37ce407c77baa7fe03806 --- /dev/null +++ b/smash/web/tests/test_model_visit.py @@ -0,0 +1,42 @@ +from django.contrib.auth.models import User +from django.test import TestCase, RequestFactory +from django.urls import reverse + +from web.views import * + +from web.models import * + +from web.tests.functions import * + +class VisitModelTests(TestCase): + + def test_mark_as_finished(self): + subject = create_subject() + visit = create_visit(subject) + + visit.mark_as_finished() + + visit_count = Visit.objects.filter(subject=subject).count() + self.assertEquals(2, visit_count) + + def test_mark_as_finished_2(self): + subject = create_subject() + visit = create_visit(subject) + subject.dead=True + subject.save() + + visit.mark_as_finished() + + visit_count = Visit.objects.filter(subject=subject).count() + self.assertEquals(1, visit_count) + + def test_mark_as_finished_3(self): + subject = create_subject() + visit = create_visit(subject) + subject.resigned=True + subject.save() + + visit.mark_as_finished() + + visit_count = Visit.objects.filter(subject=subject).count() + self.assertEquals(1, visit_count) diff --git a/smash/web/tests/test_view_notifications.py b/smash/web/tests/test_view_notifications.py index 48a93568ccffcf17e457bce57cde71c904f36a56..82868c2765e70250566fe6bf0782a0aba62f05b6 100644 --- a/smash/web/tests/test_view_notifications.py +++ b/smash/web/tests/test_view_notifications.py @@ -51,6 +51,16 @@ class NotificationViewTests(TestCase): notification = get_visits_without_appointments_count(self.user) self.assertEquals(original_notification.count + 1, notification.count) + def test_get_visits_without_appointments_count_2(self): + appointment_type = create_appointment_type() + original_notification = get_visits_without_appointments_count(self.user) + subject = create_subject() + visit = create_visit(subject) + visit.appointment_types.add(appointment_type) + visit.save() + + notification = get_visits_without_appointments_count(self.user) + self.assertEquals(original_notification.count, notification.count) def test_get_visits_without_appointments_count_3(self): original_notification = get_visits_without_appointments_count(self.user) diff --git a/smash/web/urls.py b/smash/web/urls.py index fd7a2f8a936c658c3b356240c93993e1efe0a48f..2129f7b67af2873d70618d465c2a5329f09902e8 100644 --- a/smash/web/urls.py +++ b/smash/web/urls.py @@ -33,6 +33,7 @@ urlpatterns = [ url(r'^visits/exceeded$', views.exceeded_visits, name='web.views.exceeded_visits'), url(r'^visits/unfinished$', views.unfinished_visits, name='web.views.unfinished_visits'), url(r'^visits/approaching$', views.approaching_visits_without_appointments, name='web.views.approaching_visits_without_appointments'), + url(r'^visits/missing_appointments$', views.visits_with_missing_appointments, name='web.views.visits_with_missing_appointments'), url(r'^visits/details/(?P<id>\d+)$', views.visit_details, name='web.views.visit_details'), url(r'^visits/add$', views.visit_add, name='web.views.visit_add'), url(r'^visits/add/(?P<subject_id>\d+)$', views.visit_add, name='web.views.visit_add'), @@ -41,7 +42,6 @@ urlpatterns = [ url(r'^subjects$', views.subjects, name='web.views.subjects'), url(r'^subjects/no_visit$', views.subject_no_visits, name='web.views.subject_no_visits'), url(r'^subjects/add$', views.subject_add, name='web.views.subject_add'), - url(r'^subjects/details/(?P<id>\d+)$', views.subject_details, name='web.views.subject_details'), url(r'^subjects/subject_visit_details/(?P<id>\d+)$', views.subject_visit_details, name='web.views.subject_visit_details'), url(r'^subjects/edit/(?P<id>\d+)$', views.subject_edit, name='web.views.subject_edit'), url(r'^subjects/delete/(?P<id>\d+)$', views.subject_delete, name='web.views.subject_delete'), diff --git a/smash/web/views.py b/smash/web/views.py index a568f3437f8f09c349895ce1b19711b0aaea8f78..0466b85a2d91d14c6653619c31a6280eca8a4197 100644 --- a/smash/web/views.py +++ b/smash/web/views.py @@ -124,14 +124,21 @@ def get_subject_with_no_visit_notifications_count(user): def get_visits_without_appointments_count(user): notification = NotificationCount( title = "unfinished visits ", - count = get_unfinished_visits(user).count(), + count = len(get_unfinished_visits(user)), style = "fa fa-user-times text-yellow", type = 'web.views.unfinished_visits') return notification -def get_unfinished_visits(user): +def get_visits_with_missing_appointments_count(user): + notification = NotificationCount( + title = "visits with missing appointments", + count = len(get_active_visits_with_missing_appointments(user)), + style = "fa fa-user-times text-yellow", + type = 'web.views.visits_with_missing_appointments') + return notification + +def get_active_visits_without_appointments(user): today = get_today_midnight_date() - #list visits that have no scheduled appointments and taking place now return Visit.objects.annotate(my_count=Count(Case(When(appointment__status=Appointment.APPOINTMENT_STATUS_SCHEDULED, then=1)))).filter( datetime_begin__lt = today, datetime_end__gt = today, @@ -139,6 +146,33 @@ def get_unfinished_visits(user): subject__default_location__in = get_filter_locations(user), my_count=0) +def waiting_for_appointment(visit): + required_types = visit.appointment_types.all() + appointment_types = [] + for appointment in visit.appointment_set.all(): + for type in appointment.appointment_types.all(): + if (appointment.status in [Appointment.APPOINTMENT_STATUS_FINISHED,Appointment.APPOINTMENT_STATUS_SCHEDULED] ) and (not (type in appointment_types)): + appointment_types.append(type) + result = False + for type in required_types: + if not (type in appointment_types): + result = True + return result + +def get_unfinished_visits(user): + result = []; + for visit in get_active_visits_without_appointments(user): + if not waiting_for_appointment(visit): + result.append(visit) + return result + +def get_active_visits_with_missing_appointments(user): + result = []; + for visit in get_active_visits_without_appointments(user): + if waiting_for_appointment(visit): + result.append(visit) + return result + def get_approaching_visits_without_appointments_count(user): notification = NotificationCount( title = "approaching visits ", @@ -157,7 +191,6 @@ def get_approaching_visits_without_appointments(user): subject__default_location__in = get_filter_locations(user), my_count=0) - def get_unfinished_appointments_count(user): return NotificationCount( title = "unfinished appointments ", @@ -183,7 +216,9 @@ def get_notifications(the_user): notifications.append(get_visits_without_appointments_count(person)) notifications.append(get_approaching_visits_without_appointments_count(person)) notifications.append(get_unfinished_appointments_count(person)) + notifications.append(get_visits_with_missing_appointments_count(person)) notifications.append(get_subject_with_no_visit_notifications_count(person)) + for notification in notifications: count += notification.count return (count, notifications) @@ -234,6 +269,14 @@ def unfinished_visits(request): return wrap_response(request, 'visits/index.html', context) +def visits_with_missing_appointments(request): + context = { + 'visit_list': get_active_visits_with_missing_appointments(request.user) + } + + return wrap_response(request, 'visits/index.html', context) + + def approaching_visits_without_appointments(request): context = { 'visit_list': get_approaching_visits_without_appointments(request.user) @@ -243,15 +286,24 @@ def approaching_visits_without_appointments(request): def visit_details(request, id): displayedVisit = get_object_or_404(Visit, id=id) + if request.method == 'POST': + vform = VisitDetailForm(request.POST, request.FILES, instance=displayedVisit) + if vform.is_valid(): + vform.save() + else: + vform = VisitDetailForm(instance=displayedVisit) + visFinished = displayedVisit.is_finished vid = displayedVisit.id displayedSubject = displayedVisit.subject listOfAppointments = displayedVisit.appointment_set.all() - canFinish=True + + canFinish=not waiting_for_appointment(displayedVisit) + for appointment in listOfAppointments: if appointment.status == Appointment.APPOINTMENT_STATUS_SCHEDULED: canFinish=False; - vform = VisitDetailForm(instance=displayedVisit) + sform = SubjectDetailForm(instance=displayedSubject) return wrap_response(request, 'visits/details.html', { @@ -276,8 +328,8 @@ def visit_add(request, subject_id = -1): if request.method == 'POST': form = VisitAddForm(request.POST, request.FILES) if form.is_valid(): - form.save() - return redirect(visits) + visit = form.save() + return redirect(visit_details, visit.id) else: subjects = Subject.objects.filter(id=subject_id) subject = None @@ -319,14 +371,6 @@ def subject_no_visits(request): return wrap_response(request, 'subjects/index.html', context) -def subject_details(request, id): - the_subject = get_object_or_404(Subject, id=id) - form = SubjectDetailForm(instance=the_subject) - sid = id - - return wrap_response(request, 'subjects/details.html', {'form': form, 'sid': sid}) - - def subject_edit(request, id): the_subject = get_object_or_404(Subject, id=id) if request.method == 'POST': @@ -336,7 +380,10 @@ def subject_edit(request, id): return redirect(subjects) else: form = SubjectEditForm(instance=the_subject) - return wrap_response(request, 'subjects/edit.html', {'form': form, 'id':id}) + return wrap_response(request, 'subjects/edit.html', { + 'form': form, + 'subject': the_subject + }) def subject_delete(request, id): @@ -562,7 +609,15 @@ def appointment_edit(request, id): return redirect(appointments) else: form = AppointmentEditForm(instance=the_appointment) - return wrap_response(request, 'appointments/edit.html', {'form': form, 'id':id, 'status': the_appointment.status}) + + subject_form = SubjectDetailForm(instance=the_appointment.visit.subject) + + return wrap_response(request, 'appointments/edit.html', { + 'form': form, + 'subject_form': subject_form, + 'id':id, + 'appointment': the_appointment + }) def appointment_edit_datetime(request, id): @@ -608,7 +663,7 @@ def write_subjects_to_csv(writer): row = [] for field in subject_fields: row.append(getattr(subject,field.name)) - writer.writerow([unicode(s).encode("utf-8") for s in row]) + writer.writerow([unicode(s).replace("\n", ";").replace("\r", ";").encode("utf-8") for s in row]) def write_appointments_to_csv(writer): appointments_fields = [] @@ -640,7 +695,7 @@ def write_appointments_to_csv(writer): for type in appointment.appointment_types.all(): type_string+=type.code+"," row.append(type_string) - writer.writerow([unicode(s).encode("utf-8") for s in row]) + writer.writerow([unicode(s).replace("\n", ";").replace("\r", ";").encode("utf-8") for s in row]) def export(request): return wrap_response(request, 'export/index.html',{})