From 2fd5b0f6eea38b6b9494d014e03647e7804ef051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Fri, 20 Apr 2018 20:30:39 +0200 Subject: [PATCH] [API] Improve login (#83) --- .../java/io/github/wulkanowy/api/Client.java | 53 ++++-- .../java/io/github/wulkanowy/api/Diary.java | 50 +++++ .../io/github/wulkanowy/api/ParamItem.java | 14 ++ .../io/github/wulkanowy/api/Semester.java | 38 ++-- .../java/io/github/wulkanowy/api/SnP.java | 14 +- .../java/io/github/wulkanowy/api/Student.java | 46 +++++ .../wulkanowy/api/StudentAndParent.java | 125 ++++++++++-- .../java/io/github/wulkanowy/api/Vulcan.java | 25 +-- .../github/wulkanowy/api/VulcanException.java | 6 +- .../wulkanowy/api/grades/GradesList.java | 78 +++++--- .../io/github/wulkanowy/api/login/Login.java | 49 ++--- .../io/github/wulkanowy/api/ClientTest.java | 17 -- .../wulkanowy/api/StudentAndParentTest.java | 79 +++++--- .../api/StudentAndParentTestCase.java | 2 +- .../io/github/wulkanowy/api/VulcanTest.java | 4 +- .../github/wulkanowy/api/login/LoginTest.java | 35 +++- .../api/OcenyWszystkie-semester.html | 3 +- .../wulkanowy/api/StudentAndParent.html | 37 ++++ app/build.gradle | 3 +- .../data/db/dao/entities/DiaryTest.java | 22 +++ .../wulkanowy/data/db/dao/DbHelper.java | 81 +++++--- .../data/db/dao/entities/Account.java | 179 +++++++++++++----- .../wulkanowy/data/db/dao/entities/Diary.java | 161 ++++++++++++++++ .../data/db/dao/migrations/Migration23.java | 114 +++++++++++ .../data/sync/account/AccountSync.java | 23 ++- .../wulkanowy/utils/DataObjectConverter.java | 21 +- 26 files changed, 1025 insertions(+), 254 deletions(-) create mode 100644 api/src/main/java/io/github/wulkanowy/api/Diary.java create mode 100644 api/src/main/java/io/github/wulkanowy/api/ParamItem.java create mode 100644 api/src/main/java/io/github/wulkanowy/api/Student.java create mode 100644 api/src/test/resources/io/github/wulkanowy/api/StudentAndParent.html create mode 100644 app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DiaryTest.java create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Diary.java create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration23.java diff --git a/api/src/main/java/io/github/wulkanowy/api/Client.java b/api/src/main/java/io/github/wulkanowy/api/Client.java index c3abb2ee..dde27c68 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Client.java +++ b/api/src/main/java/io/github/wulkanowy/api/Client.java @@ -21,11 +21,9 @@ public class Client { private String password; - private String symbol = "Default"; + private String symbol; - private Login login; - - private Date lastSuccessRequest = new Date(); + private Date lastSuccessRequest = null; private Cookies cookies = new Cookies(); @@ -56,25 +54,16 @@ public class Client { return; } - this.symbol = getLogin().login(email, password, symbol); + this.cookies = new Cookies(); + this.symbol = new Login(this).login(email, password, symbol); } private boolean isLoggedIn() { - return getCookies().size() > 0 && + return getCookies().size() > 0 && lastSuccessRequest != null && 29 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime()); } - Login getLogin() { - if (null != login) { - return login; - } - - login = new Login(this); - - return login; - } - public String getSymbol() { return symbol; } @@ -83,6 +72,10 @@ public class Client { this.symbol = symbol; } + public void addCookies(Map items) { + cookies.addItems(items); + } + private Map getCookies() { return cookies.getItems(); } @@ -98,8 +91,22 @@ public class Client { .replace("{symbol}", symbol); } - Document getPageByUrl(String url) throws IOException, VulcanException { - login(); + public Document getPageByUrl(String url) throws IOException, VulcanException { + return getPageByUrl(url, true, null); + } + + public Document getPageByUrl(String url, boolean loginBefore) throws IOException, VulcanException { + return getPageByUrl(url, loginBefore, null); + } + + public Document getPageByUrl(String url, boolean loginBefore, Map cookies) throws IOException, VulcanException { + if (loginBefore) { + login(); + } + + if (null != cookies) { + this.cookies.addItems(cookies); + } Connection.Response response = Jsoup.connect(getFilledUrl(url)) .followRedirects(true) @@ -110,7 +117,9 @@ public class Client { Document doc = checkForErrors(response.parse()); - lastSuccessRequest = new Date(); + if (loginBefore) { + lastSuccessRequest = new Date(); + } return doc; } @@ -169,6 +178,8 @@ public class Client { } Document checkForErrors(Document doc) throws VulcanException { + lastSuccessRequest = null; + String title = doc.select("title").text(); if ("Przerwa techniczna".equals(title)) { throw new VulcanOfflineException(title); @@ -179,6 +190,10 @@ public class Client { throw new NotLoggedInErrorException(singIn); } + if ("Błąd strony".equals(title)) { + throw new VulcanException("Nieznany błąd"); + } + return doc; } } diff --git a/api/src/main/java/io/github/wulkanowy/api/Diary.java b/api/src/main/java/io/github/wulkanowy/api/Diary.java new file mode 100644 index 00000000..3c20f9e1 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Diary.java @@ -0,0 +1,50 @@ +package io.github.wulkanowy.api; + +public class Diary implements ParamItem { + + private String id = ""; + + private String studentId = ""; + + private String name = ""; + + private boolean current = false; + + public String getId() { + return id; + } + + public Diary setId(String id) { + this.id = id; + return this; + } + + public String getStudentId() { + return studentId; + } + + @Override + public Diary setStudentId(String studentId) { + this.studentId = studentId; + return this; + } + + public String getName() { + return name; + } + + public Diary setName(String name) { + this.name = name; + return this; + } + + @Override + public boolean isCurrent() { + return current; + } + + public Diary setCurrent(boolean current) { + this.current = current; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/ParamItem.java b/api/src/main/java/io/github/wulkanowy/api/ParamItem.java new file mode 100644 index 00000000..8aaab7ca --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/ParamItem.java @@ -0,0 +1,14 @@ +package io.github.wulkanowy.api; + +interface ParamItem { + + ParamItem setId(String id); + + ParamItem setStudentId(String id); + + ParamItem setName(String name); + + ParamItem setCurrent(boolean isCurrent); + + boolean isCurrent(); +} diff --git a/api/src/main/java/io/github/wulkanowy/api/Semester.java b/api/src/main/java/io/github/wulkanowy/api/Semester.java index 64a07c97..6d57e667 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Semester.java +++ b/api/src/main/java/io/github/wulkanowy/api/Semester.java @@ -1,21 +1,14 @@ package io.github.wulkanowy.api; -public class Semester { - - private String number = ""; +public class Semester implements ParamItem { private String id = ""; - private boolean isCurrent = false; + private String studentId = ""; - public String getNumber() { - return number; - } + private String name = ""; - public Semester setNumber(String number) { - this.number = number; - return this; - } + private boolean current = false; public String getId() { return id; @@ -26,12 +19,31 @@ public class Semester { return this; } + public String getStudentId() { + return studentId; + } + + @Override + public Semester setStudentId(String studentId) { + this.studentId = studentId; + return this; + } + + public String getName() { + return name; + } + + public Semester setName(String number) { + this.name = number; + return this; + } + public boolean isCurrent() { - return isCurrent; + return current; } public Semester setCurrent(boolean current) { - isCurrent = current; + this.current = current; return this; } } diff --git a/api/src/main/java/io/github/wulkanowy/api/SnP.java b/api/src/main/java/io/github/wulkanowy/api/SnP.java index adbffd4b..7f074071 100644 --- a/api/src/main/java/io/github/wulkanowy/api/SnP.java +++ b/api/src/main/java/io/github/wulkanowy/api/SnP.java @@ -8,17 +8,25 @@ import java.util.List; public interface SnP { - String getId(); + String getSchoolID(); - StudentAndParent storeContextCookies() throws IOException, VulcanException; + void setDiaryID(String id); + + String getStudentID(); + + List getStudents() throws IOException, VulcanException; + + StudentAndParent setUp() throws IOException, VulcanException; String getRowDataChildValue(Element e, int index); Document getSnPPageDocument(String url) throws IOException, VulcanException; + List getDiaries() throws IOException, VulcanException; + List getSemesters() throws IOException, VulcanException; List getSemesters(Document gradesPage); - Semester getCurrentSemester(List semesterList); + T getCurrent(List list); } diff --git a/api/src/main/java/io/github/wulkanowy/api/Student.java b/api/src/main/java/io/github/wulkanowy/api/Student.java new file mode 100644 index 00000000..10ca4caf --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Student.java @@ -0,0 +1,46 @@ +package io.github.wulkanowy.api; + +public class Student implements ParamItem { + + private String id = ""; + + private String name = ""; + + private boolean current = false; + + public String getId() { + return id; + } + + public Student setId(String id) { + this.id = id; + return this; + } + + public String getStudentId() { + return getId(); + } + + @Override + public Student setStudentId(String studentId) { + return setId(studentId); + } + + public String getName() { + return name; + } + + public Student setName(String name) { + this.name = name; + return this; + } + + public boolean isCurrent() { + return current; + } + + public Student setCurrent(boolean current) { + this.current = current; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java index 8f67da39..5379053f 100644 --- a/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java +++ b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java @@ -5,8 +5,11 @@ import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.IOException; +import java.net.URL; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class StudentAndParent implements SnP { @@ -18,28 +21,47 @@ public class StudentAndParent implements SnP { private Client client; - private String id; + private String schoolID; - StudentAndParent(Client client, String id) { + private String studentID; + + private String diaryID; + + StudentAndParent(Client client, String schoolID, String studentID, String diaryID) { this.client = client; - this.id = id; + this.schoolID = schoolID; + this.studentID = studentID; + this.diaryID = diaryID; } - private String getBaseUrl() { - return BASE_URL.replace("{ID}", getId()); - } + public StudentAndParent setUp() throws IOException, VulcanException { + if (null == getStudentID() || "".equals(getStudentID())) { + Document doc = client.getPageByUrl(getSnpHomePageUrl()); - public String getId() { - return id; - } + Student student = getCurrent(getStudents(doc)); + studentID = student.getId(); + + Diary diary = getCurrent(getDiaries(doc)); + diaryID = diary.getId(); + } - public StudentAndParent storeContextCookies() throws IOException, VulcanException { - client.getPageByUrl(getSnpHomePageUrl()); return this; } + public String getSchoolID() { + return schoolID; + } + + public String getStudentID() { + return studentID; + } + + private String getBaseUrl() { + return BASE_URL.replace("{ID}", getSchoolID()); + } + String getSnpHomePageUrl() throws IOException, VulcanException { - if (null != getId()) { + if (null != getSchoolID()) { return getBaseUrl(); } @@ -53,7 +75,7 @@ public class StudentAndParent implements SnP { String snpPageUrl = studentTileLink.attr("href"); - this.id = getExtractedIdFromUrl(snpPageUrl); + this.schoolID = getExtractedIdFromUrl(snpPageUrl); return snpPageUrl; } @@ -72,8 +94,39 @@ public class StudentAndParent implements SnP { return e.select(".daneWiersz .wartosc").get(index - 1).text(); } + public void setDiaryID(String id) { + this.diaryID = id; + } + public Document getSnPPageDocument(String url) throws IOException, VulcanException { - return client.getPageByUrl(getBaseUrl() + url); + Map cookies = new HashMap<>(); + cookies.put("idBiezacyDziennik", diaryID); + cookies.put("idBiezacyUczen", studentID); + client.addCookies(cookies); + + Document doc = client.getPageByUrl(getBaseUrl() + url, true, cookies); + + if ("Witryna ucznia i rodzica – Strona główna".equals(doc.select("title").first().text())) { + throw new VulcanException("Sesja została nieprawidłowo zainicjowana"); + } + + return doc; + } + + public List getDiaries() throws IOException, VulcanException { + return getDiaries(client.getPageByUrl(getBaseUrl())); + } + + private List getDiaries(Document doc) throws IOException, VulcanException { + return getList(doc.select("#dziennikDropDownList option"), Diary.class); + } + + public List getStudents() throws IOException, VulcanException { + return getStudents(client.getPageByUrl(getBaseUrl())); + } + + private List getStudents(Document doc) throws IOException, VulcanException { + return getList(doc.select("#uczenDropDownList option"), Student.class); } public List getSemesters() throws IOException, VulcanException { @@ -88,9 +141,9 @@ public class StudentAndParent implements SnP { for (Element e : semesterOptions) { Semester semester = new Semester() .setId(e.text()) - .setNumber(e.attr("value")); + .setName(e.attr("value")); - if ("selected".equals(e.attr("selected"))) { + if (isCurrent(e)) { semester.setCurrent(true); } @@ -100,15 +153,47 @@ public class StudentAndParent implements SnP { return semesters; } - public Semester getCurrentSemester(List semesterList) { - Semester current = null; - for (Semester s : semesterList) { + @SuppressWarnings("unchecked") + private List getList(Elements options, Class type) throws IOException, VulcanException { + List list = new ArrayList<>(); + + for (Element e : options) { + URL url = new URL(e.val()); + try { + ParamItem item = type.newInstance() + .setId(url.getQuery().split("=")[1]) + .setName(e.text()); + + if (isCurrent(e)) { + item.setCurrent(true); + } + if (item instanceof Diary) { + item.setStudentId(getStudentID()); + } + + list.add((T) item); + } catch (Exception ex) { + throw new VulcanException("Error while trying to parse params list", ex); + } + } + + return list; + } + + @SuppressWarnings("unchecked") + public T getCurrent(List list) { + ParamItem current = null; + for (ParamItem s : list) { if (s.isCurrent()) { current = s; break; } } - return current; + return (T) current; + } + + private boolean isCurrent(Element e) { + return "selected".equals(e.attr("selected")); } } diff --git a/api/src/main/java/io/github/wulkanowy/api/Vulcan.java b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java index 75b939dd..d90d3874 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Vulcan.java +++ b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java @@ -18,16 +18,22 @@ import io.github.wulkanowy.api.user.FamilyInformation; public class Vulcan { - private String id; - private SnP snp; private Client client; - public void setCredentials(String email, String password, String symbol, String id) { - client = new Client(email, password, symbol); + private String schoolId; - this.id = id; + private String studentId; + + private String diaryId; + + public void setCredentials(String email, String password, String symbol, String schoolId, String studentId, String diaryId) { + this.schoolId = schoolId; + this.studentId = studentId; + this.diaryId = diaryId; + + client = new Client(email, password, symbol); } public Client getClient() throws NotLoggedInErrorException { @@ -43,20 +49,17 @@ public class Vulcan { } - public SnP getStudentAndParent() throws IOException, VulcanException { + public SnP getStudentAndParent() throws VulcanException, IOException { if (null != this.snp) { return this.snp; } - this.snp = new StudentAndParent(getClient(), id).storeContextCookies(); + this.snp = new StudentAndParent(getClient(), schoolId, studentId, diaryId) + .setUp(); return this.snp; } - public String getId() throws IOException, VulcanException { - return getStudentAndParent().getId(); - } - public AttendanceTable getAttendanceTable() throws IOException, VulcanException { return new AttendanceTable(getStudentAndParent()); } diff --git a/api/src/main/java/io/github/wulkanowy/api/VulcanException.java b/api/src/main/java/io/github/wulkanowy/api/VulcanException.java index 723cab91..aba02057 100644 --- a/api/src/main/java/io/github/wulkanowy/api/VulcanException.java +++ b/api/src/main/java/io/github/wulkanowy/api/VulcanException.java @@ -1,8 +1,12 @@ package io.github.wulkanowy.api; -public abstract class VulcanException extends Exception { +public class VulcanException extends Exception { protected VulcanException(String message) { super(message); } + + protected VulcanException(String message, Exception e) { + super(message, e); + } } diff --git a/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java b/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java index ce8c0f77..6e96c3a8 100644 --- a/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java +++ b/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java @@ -22,7 +22,7 @@ public class GradesList { private static final String GRADES_PAGE_URL = "Oceny/Wszystkie?details=2&okres="; - private SnP snp = null; + private SnP snp; private List grades = new ArrayList<>(); @@ -41,43 +41,61 @@ public class GradesList { public List getAll(String semester) throws IOException, ParseException, VulcanException { Document gradesPage = snp.getSnPPageDocument(getGradesPageUrl() + semester); Elements gradesRows = gradesPage.select(".ocenySzczegoly-table > tbody > tr"); - Semester currentSemester = snp.getCurrentSemester(snp.getSemesters(gradesPage)); + + if ("".equals(semester)) { + List semesterList = snp.getSemesters(gradesPage); + Semester currentSemester = snp.getCurrent(semesterList); + semester = currentSemester.getName(); + } for (Element row : gradesRows) { if ("Brak ocen".equals(row.select("td:nth-child(2)").text())) { continue; } - String descriptions = row.select("td:nth-child(3)").text(); - String symbol = descriptions.split(", ")[0]; - String description = descriptions.replaceFirst(symbol, "").replaceFirst(", ", ""); - - Pattern pattern = Pattern.compile("#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})"); - Matcher matcher = pattern.matcher(row.select("td:nth-child(2) span.ocenaCzastkowa") - .attr("style")); - - String color = ""; - while (matcher.find()) { - color = matcher.group(1); - } - - SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT); - Date d = sdf.parse(row.select("td:nth-child(5)").text()); - sdf.applyPattern("yyyy-MM-dd"); - - grades.add(new Grade() - .setSubject(row.select("td:nth-child(1)").text()) - .setValue(row.select("td:nth-child(2)").text()) - .setColor(color) - .setSymbol(symbol) - .setDescription(description) - .setWeight(row.select("td:nth-child(4)").text()) - .setDate(sdf.format(d)) - .setTeacher(row.select("td:nth-child(6)").text()) - .setSemester(currentSemester.getNumber()) - ); + grades.add(getGrade(row, semester)); } return grades; } + + private Grade getGrade(Element row, String semester) throws ParseException { + String descriptions = row.select("td:nth-child(3)").text(); + + String symbol = descriptions.split(", ")[0]; + String description = descriptions.replaceFirst(symbol, "").replaceFirst(", ", ""); + String color = getColor(row.select("td:nth-child(2) span.ocenaCzastkowa").attr("style")); + String date = formatDate(row.select("td:nth-child(5)").text()); + + return new Grade() + .setSubject(row.select("td:nth-child(1)").text()) + .setValue(row.select("td:nth-child(2)").text()) + .setColor(color) + .setSymbol(symbol) + .setDescription(description) + .setWeight(row.select("td:nth-child(4)").text()) + .setDate(date) + .setTeacher(row.select("td:nth-child(6)").text()) + .setSemester(semester); + } + + private String getColor(String styleAttr) { + Pattern pattern = Pattern.compile("#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})"); + Matcher matcher = pattern.matcher(styleAttr); + + String color = ""; + while (matcher.find()) { + color = matcher.group(1); + } + + return color; + } + + private String formatDate(String date) throws ParseException { + SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT); + Date d = sdf.parse(date); + sdf.applyPattern("yyyy-MM-dd"); + + return sdf.format(d); + } } diff --git a/api/src/main/java/io/github/wulkanowy/api/login/Login.java b/api/src/main/java/io/github/wulkanowy/api/login/Login.java index e7aecec0..2814950f 100644 --- a/api/src/main/java/io/github/wulkanowy/api/login/Login.java +++ b/api/src/main/java/io/github/wulkanowy/api/login/Login.java @@ -18,26 +18,19 @@ public class Login { "{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx%26wctx%3D" + "{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx"; - private static final String LOGIN_ENDPOINT_PAGE_URL = - "{schema}://uonetplus.{host}/{symbol}/LoginEndpoint.aspx"; - private Client client; - private String symbol; - public Login(Client client) { this.client = client; } public String login(String email, String password, String symbol) throws VulcanException, IOException { - String certificate = sendCredentials(email, password, symbol); + Document certDoc = sendCredentials(email, password); - return sendCertificate(certificate, symbol); + return sendCertificate(certDoc, symbol); } - String sendCredentials(String email, String password, String symbol) throws IOException, VulcanException { - this.symbol = symbol; - + Document sendCredentials(String email, String password) throws IOException, VulcanException { Document html = client.postPageByUrl(LOGIN_PAGE_URL, new String[][]{ {"LoginName", email}, {"Password", password} @@ -48,27 +41,37 @@ public class Login { throw new BadCredentialsException(errorMessage.text()); } - return html.select("input[name=wresult]").attr("value"); + return html; } - String sendCertificate(String certificate, String defaultSymbol) throws IOException, VulcanException { - this.symbol = findSymbol(defaultSymbol, certificate); - client.setSymbol(this.symbol); + String sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException { + String certificate = doc.select("input[name=wresult]").val(); - String title = client.postPageByUrl(LOGIN_ENDPOINT_PAGE_URL, new String[][]{ - {"wa", "wsignin1.0"}, - {"wresult", certificate} - }).select("title").text(); + String symbol = findSymbol(defaultSymbol, certificate); + client.setSymbol(symbol); + + Document targetDoc = sendCertData(doc); + String title = targetDoc.select("title").text(); if ("Logowanie".equals(title)) { throw new AccountPermissionException("No account access. Try another symbol"); } if (!"Uonet+".equals(title)) { - throw new LoginErrorException("Could not log in, unknown error"); + throw new LoginErrorException("Expected page title `UONET+`, got " + title); } - return this.symbol; + return symbol; + } + + private Document sendCertData(Document doc) throws IOException, VulcanException { + String url = doc.select("form[name=hiddenform]").attr("action"); + + return client.postPageByUrl(url.replaceFirst("Default", "{symbol}"), new String[][]{ + {"wa", "wsignin1.0"}, + {"wresult", doc.select("input[name=wresult]").val()}, + {"wctx", doc.select("input[name=wctx]").val()} + }); } private String findSymbol(String symbol, String certificate) { @@ -80,14 +83,14 @@ public class Login { } String findSymbolInCertificate(String certificate) { - Elements els = Jsoup + Elements instances = Jsoup .parse(certificate.replaceAll(":", ""), "", Parser.xmlParser()) .select("[AttributeName=\"UserInstance\"] samlAttributeValue"); - if (els.isEmpty()) { + if (instances.isEmpty()) { return ""; } - return els.get(1).text(); + return instances.get(1).text(); } } diff --git a/api/src/test/java/io/github/wulkanowy/api/ClientTest.java b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java index 4aa1be91..38f85617 100644 --- a/api/src/test/java/io/github/wulkanowy/api/ClientTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java @@ -1,13 +1,10 @@ package io.github.wulkanowy.api; -import org.hamcrest.CoreMatchers; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.junit.Assert; import org.junit.Test; -import io.github.wulkanowy.api.login.Login; - public class ClientTest { private String getFixtureAsString(String fixtureFileName) { @@ -49,20 +46,6 @@ public class ClientTest { client.checkForErrors(doc); } - @Test - public void getClientTest() throws Exception { - Client client = new Client("", "", ""); - - Assert.assertThat(client.getLogin(), CoreMatchers.instanceOf(Login.class)); - } - - @Test - public void getClientTwiceTest() throws Exception { - Client client = new Client("", "", ""); - - Assert.assertEquals(client.getLogin(), client.getLogin()); - } - @Test public void getFilledUrlTest() throws Exception { Client client = new Client("http://fakelog.cf\\\\admin", "", "symbol123"); diff --git a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java index 5cde9b64..ca6ca316 100644 --- a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java @@ -7,6 +7,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -22,18 +23,21 @@ public class StudentAndParentTest { client = Mockito.mock(Client.class); Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(gradesPageDocument); + Mockito.when(client.getPageByUrl( + Mockito.anyString(), + Mockito.anyBoolean(), Mockito.anyMap())).thenReturn(gradesPageDocument); } @Test - public void snpTest() throws Exception { - StudentAndParent snp = new StudentAndParent(client, "id123"); - Assert.assertEquals("id123", snp.getId()); + public void snpTest() { + StudentAndParent snp = new StudentAndParent(client, "id123", null, null); + Assert.assertEquals("id123", snp.getSchoolID()); } @Test public void getSnpPageUrlWithIdTest() throws Exception { Assert.assertEquals("{schema}://uonetplus-opiekun.{host}/{symbol}/123456/", - (new StudentAndParent(client, "123456")).getSnpHomePageUrl()); + (new StudentAndParent(client, "123456", null, null)).getSnpHomePageUrl()); } @Test @@ -43,7 +47,7 @@ public class StudentAndParentTest { Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(startPageDocument); - StudentAndParent snp = new StudentAndParent(client, null); + StudentAndParent snp = new StudentAndParent(client, null, null, null); Assert.assertEquals("https://uonetplus-opiekun.vulcan.net.pl/symbol/534213/Start/Index/", snp.getSnpHomePageUrl()); @@ -56,7 +60,7 @@ public class StudentAndParentTest { ); Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(wrongPageDocument); - StudentAndParent snp = new StudentAndParent(client, null); + StudentAndParent snp = new StudentAndParent(client, null, null, null); snp.getSnpHomePageUrl(); } @@ -64,7 +68,7 @@ public class StudentAndParentTest { @Test public void getExtractedIDStandardTest() throws Exception { Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); - StudentAndParent snp = new StudentAndParent(client, "symbol"); + StudentAndParent snp = new StudentAndParent(client, "symbol", null, null); Assert.assertEquals("123456", snp.getExtractedIdFromUrl("https://uonetplus-opiekun" + ".vulcan.net.pl/powiat/123456/Start/Index/")); } @@ -72,7 +76,7 @@ public class StudentAndParentTest { @Test public void getExtractedIDDemoTest() throws Exception { Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); - StudentAndParent snp = new StudentAndParent(client, "symbol"); + StudentAndParent snp = new StudentAndParent(client, "symbol", null, null); Assert.assertEquals("demo12345", snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/")); } @@ -80,44 +84,75 @@ public class StudentAndParentTest { @Test(expected = NotLoggedInErrorException.class) public void getExtractedIDNotLoggedTest() throws Exception { Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); - StudentAndParent snp = new StudentAndParent(client, "symbol"); + StudentAndParent snp = new StudentAndParent(client, "symbol", null, null); Assert.assertEquals("123", snp.getExtractedIdFromUrl("https://uonetplus.vulcan.net.pl/powiat/")); } @Test public void getSemestersTest() throws Exception { - SnP snp = new StudentAndParent(client, "123456"); + SnP snp = new StudentAndParent(client, "123456", null, null); List semesters = snp.getSemesters(); Assert.assertEquals(2, semesters.size()); Assert.assertEquals("1", semesters.get(0).getId()); - Assert.assertEquals("1234", semesters.get(0).getNumber()); + Assert.assertEquals("1234", semesters.get(0).getName()); Assert.assertFalse(semesters.get(0).isCurrent()); Assert.assertEquals("2", semesters.get(1).getId()); - Assert.assertEquals("1235", semesters.get(1).getNumber()); + Assert.assertEquals("1235", semesters.get(1).getName()); Assert.assertTrue(semesters.get(1).isCurrent()); } @Test - public void getCurrentSemesterTest() throws Exception { + public void getCurrentSemesterTest() { List semesters = new ArrayList<>(); - semesters.add(new Semester().setNumber("1500100900").setId("1").setCurrent(false)); - semesters.add(new Semester().setNumber("1500100901").setId("2").setCurrent(true)); + semesters.add(new Semester().setName("1500100900").setId("1").setCurrent(false)); + semesters.add(new Semester().setName("1500100901").setId("2").setCurrent(true)); - SnP snp = new StudentAndParent(client, ""); - Assert.assertTrue(snp.getCurrentSemester(semesters).isCurrent()); - Assert.assertEquals("2", snp.getCurrentSemester(semesters).getId()); - Assert.assertEquals("1500100901", snp.getCurrentSemester(semesters).getNumber()); + SnP snp = new StudentAndParent(client, "", null, null); + Semester semester = snp.getCurrent(semesters); + + Assert.assertTrue(semester.isCurrent()); + Assert.assertEquals("2", semester.getId()); + Assert.assertEquals("1500100901", semester.getName()); } @Test - public void getCurrentSemesterFromEmptyTest() throws Exception { - SnP snp = new StudentAndParent(client, ""); + public void getCurrentSemesterFromEmptyTest() { + SnP snp = new StudentAndParent(client, "", null, null); List semesters = new ArrayList<>(); - Assert.assertNull(snp.getCurrentSemester(semesters)); + Assert.assertNull(snp.getCurrent(semesters)); + } + + @Test + public void getDiariesAndStudentTest() throws IOException, VulcanException { + Document snpHome = Jsoup.parse(FixtureHelper.getAsString( + getClass().getResourceAsStream("StudentAndParent.html"))); + + client = Mockito.mock(Client.class); + Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(snpHome); + SnP snp = new StudentAndParent(client, "", null, null); + + snp.setUp(); + + Assert.assertEquals("3Ti 2017", snp.getDiaries().get(0).getName()); + Assert.assertEquals("2Ti 2016", snp.getDiaries().get(1).getName()); + Assert.assertEquals("1Ti 2015", snp.getDiaries().get(2).getName()); + + Assert.assertEquals("1300", snp.getDiaries().get(0).getId()); + Assert.assertEquals("1200", snp.getDiaries().get(1).getId()); + Assert.assertEquals("1100", snp.getDiaries().get(2).getId()); + + Assert.assertTrue(snp.getDiaries().get(0).isCurrent()); + Assert.assertFalse(snp.getDiaries().get(1).isCurrent()); + Assert.assertFalse(snp.getDiaries().get(2).isCurrent()); + + Assert.assertEquals("100", snp.getDiaries().get(0).getStudentId()); + + Assert.assertEquals("Jan Kowal", snp.getStudents().get(0).getName()); + Assert.assertEquals("100", snp.getStudents().get(0).getStudentId()); } } diff --git a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java index b18076cf..8d32291f 100644 --- a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java +++ b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java @@ -16,7 +16,7 @@ public abstract class StudentAndParentTestCase { Mockito.when(snp.getSnPPageDocument(Mockito.anyString())) .thenReturn(tablePageDocument); Mockito.when(snp.getSemesters(Mockito.any(Document.class))).thenCallRealMethod(); - Mockito.when(snp.getCurrentSemester(Mockito.anyList())) + Mockito.when(snp.getCurrent(Mockito.anyList())) .thenCallRealMethod(); Mockito.when(snp.getRowDataChildValue(Mockito.any(Element.class), Mockito.anyInt())).thenCallRealMethod(); diff --git a/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java b/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java index bda66e8d..287935c0 100644 --- a/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java @@ -16,7 +16,7 @@ public class VulcanTest { @Test public void getClientTest() throws Exception { Vulcan vulcan = new Vulcan(); - vulcan.setCredentials("email", "password", "symbol", null); + vulcan.setCredentials("email", "password", "symbol", null, null, null); Assert.assertThat(vulcan.getClient(), CoreMatchers.instanceOf(Client.class)); } @@ -24,7 +24,7 @@ public class VulcanTest { @Test public void getClientTwiceTest() throws Exception { Vulcan vulcan = new Vulcan(); - vulcan.setCredentials("email", "password", "symbol", null); + vulcan.setCredentials("email", "password", "symbol", null, null, null); Assert.assertEquals(vulcan.getClient(), vulcan.getClient()); } diff --git a/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java b/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java index 69118018..aac3b795 100644 --- a/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java @@ -11,12 +11,16 @@ import io.github.wulkanowy.api.FixtureHelper; public class LoginTest { + private Document getFixtureAsDocument(String fixtureFileName) { + return Jsoup.parse(getFixtureAsString(fixtureFileName)); + } + private String getFixtureAsString(String fixtureFileName) { return FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName)); } private Client getClient(String fixtureFileName) throws Exception { - Document doc = Jsoup.parse(getFixtureAsString(fixtureFileName)); + Document doc = getFixtureAsDocument(fixtureFileName); Client client = Mockito.mock(Client.class); Mockito.when(client.postPageByUrl(Mockito.anyString(), Mockito.any(String[][].class))).thenReturn(doc); @@ -26,25 +30,36 @@ public class LoginTest { @Test public void loginTest() throws Exception { - Login login = new Login(getClient("Logowanie-success.html")); + Client client = getClient("Logowanie-success.html"); + Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean())) + .thenReturn(getFixtureAsDocument("Logowanie-error.html")); + Login login = new Login(client); Assert.assertEquals("d123", login.login("a@a", "pswd", "d123")); } @Test(expected = BadCredentialsException.class) public void sendWrongCredentialsTest() throws Exception { - Login login = new Login(getClient("Logowanie-error.html")); + Client client = getClient("Logowanie-error.html"); + Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean())) + .thenReturn(getFixtureAsDocument("Logowanie-error.html")); // -error.html because it html with form used by + Login login = new Login(client); - login.sendCredentials("a@a", "pswd", "d123"); + login.sendCredentials("a@a", "pswd"); } @Test public void sendCredentialsCertificateTest() throws Exception { - Login login = new Login(getClient("Logowanie-certyfikat.html")); + Client client = getClient("Logowanie-certyfikat.html"); + Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean())) + .thenReturn(getFixtureAsDocument("Logowanie-error.html")); // -error.html because it html with form used by + Login login = new Login(client); Assert.assertEquals( getFixtureAsString("cert.xml").replaceAll("\\s+",""), - login.sendCredentials("a@a", "passwd", "d123").replaceAll("\\s+","") + login.sendCredentials("a@a", "passwd") + .select("input[name=wresult]").attr("value") + .replaceAll("\\s+","") ); } @@ -53,7 +68,7 @@ public class LoginTest { Login login = new Login(getClient("Logowanie-success.html")); Assert.assertEquals("wulkanowyschool321", - login.sendCertificate("", "wulkanowyschool321")); + login.sendCertificate(new Document(""), "wulkanowyschool321")); } @Test @@ -61,21 +76,21 @@ public class LoginTest { Login login = new Login(getClient("Logowanie-success.html")); Assert.assertEquals("demo12345", - login.sendCertificate(getFixtureAsString("cert.xml"), "Default")); + login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "Default")); } @Test(expected = AccountPermissionException.class) public void sendCertificateAccountPermissionTest() throws Exception { Login login = new Login(getClient("Logowanie-brak-dostepu.html")); - login.sendCertificate(getFixtureAsString("cert.xml"), "demo123"); + login.sendCertificate(getFixtureAsDocument("cert.xml"), "demo123"); } @Test(expected = LoginErrorException.class) public void sendCertificateLoginErrorTest() throws Exception { Login login = new Login(getClient("Logowanie-certyfikat.html")); // change to other document - login.sendCertificate(getFixtureAsString("cert.xml"), "demo123"); + login.sendCertificate(getFixtureAsDocument("cert.xml"), "demo123"); } @Test diff --git a/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html b/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html index f4b712c1..dc7c6c3b 100644 --- a/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html +++ b/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html @@ -15,7 +15,8 @@ - + +
wersja: 17.05.0000.24042
diff --git a/api/src/test/resources/io/github/wulkanowy/api/StudentAndParent.html b/api/src/test/resources/io/github/wulkanowy/api/StudentAndParent.html new file mode 100644 index 00000000..66e2942f --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/StudentAndParent.html @@ -0,0 +1,37 @@ + + + + + Witryna ucznia i rodzica – Strona główna + + +
    +
  • + + +
  • +
  • + + +
  • +
+ +
wersja: 17.09.0008.26553
+ + diff --git a/app/build.gradle b/app/build.gradle index fa32f58a..1ba88621 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -62,7 +62,7 @@ android { } greendao { - schemaVersion 22 + schemaVersion 23 generateTests = true } @@ -79,7 +79,6 @@ dependencies { implementation "eu.davidea:flexible-adapter:$flexibleAdapter" implementation "eu.davidea:flexible-adapter-ui:$flexibleUi" implementation "org.greenrobot:greendao:$greenDao" - implementation "com.github.yuweiguocn:GreenDaoUpgradeHelper:$greenDaoHelper" implementation "com.jakewharton:butterknife:$butterknife" implementation "com.google.dagger:dagger-android-support:$dagger2" implementation "com.aurelhubert:ahbottomnavigation:$ahbottom" diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DiaryTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DiaryTest.java new file mode 100644 index 00000000..835ee7c8 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DiaryTest.java @@ -0,0 +1,22 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +import io.github.wulkanowy.data.db.dao.entities.Diary; +import io.github.wulkanowy.data.db.dao.entities.DiaryDao; + +public class DiaryTest extends AbstractDaoTestLongPk { + + public DiaryTest() { + super(DiaryDao.class); + } + + @Override + protected Diary createEntity(Long key) { + Diary entity = new Diary(); + entity.setId(key); + entity.setIsCurrent(false); + return entity; + } + +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java index a67e9b7f..89bf636e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java @@ -3,19 +3,21 @@ package io.github.wulkanowy.data.db.dao; import android.content.Context; import android.database.sqlite.SQLiteDatabase; -import com.github.yuweiguocn.library.greendao.MigrationHelper; - import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.database.StandardDatabase; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + import javax.inject.Inject; import javax.inject.Singleton; -import io.github.wulkanowy.BuildConfig; -import io.github.wulkanowy.data.db.dao.entities.AccountDao; +import io.github.wulkanowy.api.Vulcan; import io.github.wulkanowy.data.db.dao.entities.DaoMaster; -import io.github.wulkanowy.data.db.dao.entities.GradeDao; -import io.github.wulkanowy.data.db.dao.entities.SubjectDao; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.dao.migrations.Migration23; import io.github.wulkanowy.data.db.shared.SharedPrefContract; import io.github.wulkanowy.di.annotations.ApplicationContext; import io.github.wulkanowy.di.annotations.DatabaseInfo; @@ -24,29 +26,16 @@ import io.github.wulkanowy.utils.LogUtils; @Singleton public class DbHelper extends DaoMaster.OpenHelper { - private SharedPrefContract sharedPref; + private final SharedPrefContract sharedPref; + + private final Vulcan vulcan; @Inject DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName, - SharedPrefContract sharedPref) { + SharedPrefContract sharedPref, Vulcan vulcan) { super(context, dbName); this.sharedPref = sharedPref; - } - - @Override - @SuppressWarnings("unchecked") - public void onUpgrade(Database db, int oldVersion, int newVersion) { - MigrationHelper.DEBUG = BuildConfig.DEBUG; - MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() { - @Override - public void onCreateAllTables(Database db, boolean ifNotExists) { - DaoMaster.createAllTables(db, ifNotExists); - } - @Override - public void onDropAllTables(Database db, boolean ifExists) { - DaoMaster.dropAllTables(db, ifExists); - } - }, AccountDao.class, SubjectDao.class, GradeDao.class); + this.vulcan = vulcan; } @Override @@ -55,7 +44,49 @@ public class DbHelper extends DaoMaster.OpenHelper { DaoMaster.dropAllTables(database, true); onCreate(database); sharedPref.setCurrentUserId(0); - LogUtils.info("Cleaning user data oldVersion=" + oldVersion + " newVersion=" + newVersion); } + + @Override + public void onUpgrade(Database db, int oldVersion, int newVersion) { + List migrations = getMigrations(); + + // Only run migrations past the old version + for (Migration migration : migrations) { + if (oldVersion < migration.getVersion()) { + try { + LogUtils.info("Applying migration to db schema v" + migration.getVersion() + "..."); + migration.runMigration(db, sharedPref, vulcan); + LogUtils.info("Migration " + migration.getVersion() + " complete"); + } catch (Exception e) { + e.printStackTrace(); + DaoMaster.dropAllTables(db, true); + sharedPref.setCurrentUserId(0); + break; + } + } + } + } + + private List getMigrations() { + List migrations = new ArrayList<>(); + migrations.add(new Migration23()); + + // Sorting just to be safe, in case other people add migrations in the wrong order. + Comparator migrationComparator = new Comparator() { + @Override + public int compare(Migration m1, Migration m2) { + return m1.getVersion().compareTo(m2.getVersion()); + } + }; + Collections.sort(migrations, migrationComparator); + + return migrations; + } + + public interface Migration { + Integer getVersion(); + + void runMigration(Database db, SharedPrefContract sharedPref, Vulcan vulcan) throws Exception; + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java index 20a47ac2..1ed7548f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java @@ -4,6 +4,8 @@ import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Generated; import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Index; +import org.greenrobot.greendao.annotation.JoinProperty; import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.ToMany; @@ -18,20 +20,29 @@ public class Account { @Id(autoincrement = true) private Long id; + @Index(unique = true) + @Property(nameInDb = "REAL_ID") + private String realId; + + @Property(nameInDb = "SYMBOL") + private String symbol; + + @Property(nameInDb = "SCHOOL_ID") + private String schoolId; + @Property(nameInDb = "NAME") private String name; - @Property(nameInDb = "E-MAIL") + @Property(nameInDb = "E_MAIL") private String email; @Property(nameInDb = "PASSWORD") private String password; - @Property(nameInDb = "SYMBOL") - private String symbol; - - @Property(nameInDb = "SNPID") - private String snpId; + @ToMany(joinProperties = { + @JoinProperty(name = "realId", referencedName = "studentId") + }) + private List diaryList; @ToMany(referencedJoinProperty = "userId") private List subjectList; @@ -54,15 +65,16 @@ public class Account { @Generated(hash = 335469827) private transient AccountDao myDao; - @Generated(hash = 735765217) - public Account(Long id, String name, String email, String password, String symbol, - String snpId) { + @Generated(hash = 727721142) + public Account(Long id, String realId, String symbol, String schoolId, String name, + String email, String password) { this.id = id; + this.realId = realId; + this.symbol = symbol; + this.schoolId = schoolId; this.name = name; this.email = email; this.password = password; - this.symbol = symbol; - this.snpId = snpId; } @Generated(hash = 882125521) @@ -78,6 +90,15 @@ public class Account { return this; } + public String getRealId() { + return realId; + } + + public Account setRealId(String realId) { + this.realId = realId; + return this; + } + public String getName() { return name; } @@ -114,15 +135,83 @@ public class Account { return this; } - public String getSnpId() { - return this.snpId; + public String getSchoolId() { + return schoolId; } - public Account setSnpId(String snpId) { - this.snpId = snpId; + public Account setSchoolId(String schoolId) { + this.schoolId = schoolId; return this; } + public Account setDiaryList(List diaryList) { + this.diaryList = diaryList; + return this; + } + + public Account setSubjectList(List subjectList) { + this.subjectList = subjectList; + return this; + } + + public Account setGradeList(List gradeList) { + this.gradeList = gradeList; + return this; + } + + public Account setDayList(List dayList) { + this.dayList = dayList; + return this; + } + + public DaoSession getDaoSession() { + return daoSession; + } + + public Account setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + return this; + } + + public AccountDao getMyDao() { + return myDao; + } + + public Account setMyDao(AccountDao myDao) { + this.myDao = myDao; + return this; + } + + /** + * To-many relationship, resolved on first access (and after reset). + * Changes to to-many relations are not persisted, make changes to the target entity. + */ + @Generated(hash = 1472214466) + public List getDiaryList() { + if (diaryList == null) { + final DaoSession daoSession = this.daoSession; + if (daoSession == null) { + throw new DaoException("Entity is detached from DAO context"); + } + DiaryDao targetDao = daoSession.getDiaryDao(); + List diaryListNew = targetDao._queryAccount_DiaryList(realId); + synchronized (this) { + if (diaryList == null) { + diaryList = diaryListNew; + } + } + } + return diaryList; + } + + /** + * Resets a to-many relationship, making the next get call to query for a fresh result. + */ + @Generated(hash = 1078514341) + public synchronized void resetDiaryList() { + diaryList = null; + } + /** * To-many relationship, resolved on first access (and after reset). * Changes to to-many relations are not persisted, make changes to the target entity. @@ -183,6 +272,36 @@ public class Account { gradeList = null; } + /** + * To-many relationship, resolved on first access (and after reset). + * Changes to to-many relations are not persisted, make changes to the target entity. + */ + @Generated(hash = 300459794) + public List getDayList() { + if (dayList == null) { + final DaoSession daoSession = this.daoSession; + if (daoSession == null) { + throw new DaoException("Entity is detached from DAO context"); + } + DayDao targetDao = daoSession.getDayDao(); + List dayListNew = targetDao._queryAccount_DayList(id); + synchronized (this) { + if (dayList == null) { + dayList = dayListNew; + } + } + } + return dayList; + } + + /** + * Resets a to-many relationship, making the next get call to query for a fresh result. + */ + @Generated(hash = 1010399236) + public synchronized void resetDayList() { + dayList = null; + } + /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. * Entity must attached to an entity context. @@ -219,36 +338,6 @@ public class Account { myDao.update(this); } - /** - * To-many relationship, resolved on first access (and after reset). - * Changes to to-many relations are not persisted, make changes to the target entity. - */ - @Generated(hash = 300459794) - public List getDayList() { - if (dayList == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - DayDao targetDao = daoSession.getDayDao(); - List dayListNew = targetDao._queryAccount_DayList(id); - synchronized (this) { - if (dayList == null) { - dayList = dayListNew; - } - } - } - return dayList; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 1010399236) - public synchronized void resetDayList() { - dayList = null; - } - /** called by internal mechanisms, do not call yourself. */ @Generated(hash = 1812283172) public void __setDaoSession(DaoSession daoSession) { diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Diary.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Diary.java new file mode 100644 index 00000000..03ff9b60 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Diary.java @@ -0,0 +1,161 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.DaoException; +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Generated; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Property; + +@Entity( + nameInDb = "Diaries", + active = true +) +public class Diary { + + @Id(autoincrement = true) + private Long id; + + @Property(nameInDb = "STUDENT_ID") + private String studentId; + + @Property(nameInDb = "NAME") + private String name; + + @Property(nameInDb = "VALUE") + private String value; + + @Property(nameInDb = "IS_CURRENT") + private boolean isCurrent; + + /** + * Used to resolve relations + */ + @Generated(hash = 2040040024) + private transient DaoSession daoSession; + + /** + * Used for active entity operations. + */ + @Generated(hash = 21166549) + private transient DiaryDao myDao; + + @Generated(hash = 459332202) + public Diary(Long id, String studentId, String name, String value, + boolean isCurrent) { + this.id = id; + this.studentId = studentId; + this.name = name; + this.value = value; + this.isCurrent = isCurrent; + } + + @Generated(hash = 112123061) + public Diary() { + } + + public Long getId() { + return id; + } + + public Diary setId(Long id) { + this.id = id; + return this; + } + + public String getStudentId() { + return studentId; + } + + public Diary setStudentId(String studentId) { + this.studentId = studentId; + return this; + } + + public String getName() { + return name; + } + + public Diary setName(String name) { + this.name = name; + return this; + } + + public String getValue() { + return value; + } + + public Diary setValue(String value) { + this.value = value; + return this; + } + + public boolean getIsCurrent() { + return isCurrent; + } + + public Diary setIsCurrent(boolean current) { + isCurrent = current; + return this; + } + + public DaoSession getDaoSession() { + return daoSession; + } + + public Diary setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + return this; + } + + public DiaryDao getMyDao() { + return myDao; + } + + public Diary setMyDao(DiaryDao myDao) { + this.myDao = myDao; + return this; + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 128553479) + public void delete() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.delete(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 1942392019) + public void refresh() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.refresh(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 713229351) + public void update() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.update(this); + } + + /** called by internal mechanisms, do not call yourself. */ + @Generated(hash = 629297785) + public void __setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + myDao = daoSession != null ? daoSession.getDiaryDao() : null; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration23.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration23.java new file mode 100644 index 00000000..9a497ceb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration23.java @@ -0,0 +1,114 @@ +package io.github.wulkanowy.data.db.dao.migrations; + +import android.database.Cursor; +import android.os.AsyncTask; + +import org.greenrobot.greendao.database.Database; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.github.wulkanowy.api.Diary; +import io.github.wulkanowy.api.Vulcan; +import io.github.wulkanowy.data.db.dao.DbHelper; +import io.github.wulkanowy.data.db.shared.SharedPrefContract; +import io.github.wulkanowy.utils.security.Scrambler; + +public class Migration23 implements DbHelper.Migration { + + @Override + public Integer getVersion() { + return 23; + } + + @Override + public void runMigration(final Database db, final SharedPrefContract sharedPref, final Vulcan vulcan) throws Exception { + createDiaryTable(db); + migrateAccountsTable(db); + + final Map user = getAccountData(db); + vulcan.setCredentials( + user.get("email"), + Scrambler.decrypt(user.get("email"), user.get("password")), + user.get("symbol"), + user.get("school_id"), + "", // inserted in code bellow + "" + ); + + AsyncTask.execute(new Runnable() { + @Override + public void run() { + try { + insertDiaries(db, vulcan.getStudentAndParent().getDiaries()); + updateAccount(db, vulcan.getStudentAndParent().getStudentID()); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + private void createDiaryTable(Database db) { + db.execSQL("DROP TABLE IF EXISTS Diaries"); + db.execSQL("CREATE TABLE IF NOT EXISTS \"Diaries\" (" + // + "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id + "\"STUDENT_ID\" TEXT," + // 1: studentId + "\"NAME\" TEXT," + // 2: name + "\"VALUE\" TEXT," + // 3: value + "\"IS_CURRENT\" INTEGER NOT NULL );"); // 4: isCurrent + } + + private void migrateAccountsTable(Database db) { + db.execSQL("DROP TABLE IF EXISTS tmp_account"); + db.execSQL("ALTER TABLE Accounts RENAME TO tmp_account"); + db.execSQL("CREATE TABLE IF NOT EXISTS \"Accounts\" (" + // + "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id + "\"REAL_ID\" TEXT," + // 1: realId + "\"SYMBOL\" TEXT," + // 2: symbol + "\"SCHOOL_ID\" TEXT," + // 3: schoolId + "\"NAME\" TEXT," + // 4: name + "\"E_MAIL\" TEXT," + // 5: email + "\"PASSWORD\" TEXT);"); // 6: password + // Add Indexes + db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS IDX_Accounts_REAL_ID ON \"Accounts\" (\"REAL_ID\" ASC);"); + db.execSQL("INSERT INTO Accounts(NAME, E_MAIL, PASSWORD, SYMBOL, SCHOOL_ID)" + + "SELECT `NAME`, `E-MAIL`, `PASSWORD`, `SYMBOL`, `SNPID` FROM tmp_account"); + db.execSQL("DROP TABLE tmp_account"); + } + + private Map getAccountData(Database db) { + Map values = new HashMap<>(); + Cursor cursor = db.rawQuery("SELECT SYMBOL, SCHOOL_ID, NAME, E_MAIL, PASSWORD FROM Accounts", null); + + if (cursor.moveToFirst()) { + do { + values.put("symbol", cursor.getString(cursor.getColumnIndex("SYMBOL"))); + values.put("school_id", cursor.getString(cursor.getColumnIndex("SCHOOL_ID"))); + values.put("name", cursor.getString(cursor.getColumnIndex("NAME"))); + values.put("email", cursor.getString(cursor.getColumnIndex("E_MAIL"))); + values.put("password", cursor.getString(cursor.getColumnIndex("PASSWORD"))); + } while (cursor.moveToNext()); + } + + cursor.close(); + + return values; + } + + private void insertDiaries(Database db, List list) { + for (Diary diary : list) { + db.execSQL("INSERT INTO Diaries(STUDENT_ID, NAME, VALUE, IS_CURRENT) VALUES(" + + "\"" + diary.getStudentId() + "\"," + + "\"" + diary.getName() + "\"," + + "\"" + diary.getId() + "\"," + + "\"" + (diary.isCurrent() ? "1" : "0") + "\"" + + ")"); + } + } + + private void updateAccount(Database db, String realId) { + db.execSQL("UPDATE Accounts SET REAL_ID = ?", new String[]{realId}); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSync.java index d56f9990..675a58df 100644 --- a/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSync.java +++ b/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSync.java @@ -3,6 +3,9 @@ package io.github.wulkanowy.data.sync.account; import android.content.Context; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; @@ -11,8 +14,11 @@ import io.github.wulkanowy.api.Vulcan; import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.data.db.dao.entities.Account; import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.dao.entities.Diary; +import io.github.wulkanowy.data.db.dao.entities.DiaryDao; import io.github.wulkanowy.data.db.shared.SharedPrefContract; import io.github.wulkanowy.di.annotations.ApplicationContext; +import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.LogUtils; import io.github.wulkanowy.utils.security.CryptoException; import io.github.wulkanowy.utils.security.Scrambler; @@ -43,22 +49,27 @@ public class AccountSync implements AccountSyncContract { LogUtils.debug("Register new user email=" + email); - vulcan.setCredentials(email, password, symbol, null); + vulcan.setCredentials(email, password, symbol, null, null, null); Account account = new Account() .setName(vulcan.getBasicInformation().getPersonalData().getFirstAndLastName()) .setEmail(email) .setPassword(Scrambler.encrypt(email, password, context)) .setSymbol(vulcan.getSymbol()) - .setSnpId(vulcan.getStudentAndParent().getId()); + .setSchoolId(vulcan.getStudentAndParent().getSchoolID()) + .setRealId(vulcan.getStudentAndParent().getStudentID()); + + List diaryList = DataObjectConverter.diariesToDiaryEntities( + vulcan.getStudentAndParent().getDiaries()); daoSession.getAccountDao().insert(account); + daoSession.getDiaryDao().insertInTx(diaryList); sharedPref.setCurrentUserId(account.getId()); } @Override - public void initLastUser() throws VulcanException, IOException, CryptoException { + public void initLastUser() throws IOException, CryptoException { long userId = sharedPref.getCurrentUserId(); @@ -73,6 +84,10 @@ public class AccountSync implements AccountSyncContract { vulcan.setCredentials(account.getEmail(), Scrambler.decrypt(account.getEmail(), account.getPassword()), account.getSymbol(), - account.getSnpId()); + account.getSchoolId(), + account.getRealId(), + daoSession.getDiaryDao().queryBuilder() + .where(DiaryDao.Properties.IsCurrent.eq(true)).unique().getValue() + ); } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java index a22e4d8b..bc17427e 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java +++ b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java @@ -6,6 +6,7 @@ import java.util.List; import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; import io.github.wulkanowy.data.db.dao.entities.Day; +import io.github.wulkanowy.data.db.dao.entities.Diary; import io.github.wulkanowy.data.db.dao.entities.Grade; import io.github.wulkanowy.data.db.dao.entities.Subject; import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; @@ -17,8 +18,22 @@ public final class DataObjectConverter { throw new IllegalStateException("Utility class"); } - public static List subjectsToSubjectEntities(List subjectList) { + public static List diariesToDiaryEntities(List diaryList) { + List diaryEntityList = new ArrayList<>(); + for (io.github.wulkanowy.api.Diary diary : diaryList) { + Diary diaryEntity = new Diary() + .setValue(diary.getId()) + .setName(diary.getName()) + .setStudentId(diary.getStudentId()) + .setIsCurrent(diary.isCurrent()); + diaryEntityList.add(diaryEntity); + } + + return diaryEntityList; + } + + public static List subjectsToSubjectEntities(List subjectList) { List subjectEntityList = new ArrayList<>(); for (io.github.wulkanowy.api.grades.Subject subject : subjectList) { @@ -33,7 +48,6 @@ public final class DataObjectConverter { } public static List gradesToGradeEntities(List gradeList) { - List gradeEntityList = new ArrayList<>(); for (io.github.wulkanowy.api.grades.Grade grade : gradeList) { @@ -67,7 +81,6 @@ public final class DataObjectConverter { public static List daysToDaysEntities(List dayList) { - List dayEntityList = new ArrayList<>(); for (io.github.wulkanowy.api.generic.Day day : dayList) { @@ -110,7 +123,6 @@ public final class DataObjectConverter { } public static List lessonsToTimetableLessonsEntities(List lessonList) { - List lessonEntityList = new ArrayList<>(); for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) { @@ -120,7 +132,6 @@ public final class DataObjectConverter { } public static List lessonsToAttendanceLessonsEntities(List lessonList) { - List lessonEntityList = new ArrayList<>(); for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) {