diff --git a/.circleci/config.yml b/.circleci/config.yml index 841ff9e0..387c5edd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,9 +31,18 @@ jobs: - run: name: Setup environment command: ./gradlew dependencies --no-daemon --stacktrace --console=plain -PdisablePreDex || true + - run: + name: Decrypt keys + command: | + openssl aes-256-cbc -d -in ./app/key-encrypted.p12 -k $ENCRYPT_KEY >> ./app/key.p12 + openssl aes-256-cbc -d -in ./app/upload-key-encrypted.jks -k $ENCRYPT_KEY >> ./app/upload-key.jks - run: name: Initial build command: ./gradlew build assembleDebug -x test -x lint -x fabricGenerateResourcesRelease --no-daemon --stacktrace --console=plain -PdisablePreDex + - run: + name: Clear keys + command: | + rm ./app/key.p12 ./app/upload-key.jks - store_artifacts: path: ./app/build/outputs/apk/ destination: apks/ @@ -78,11 +87,8 @@ jobs: name: Upload unit code coverage to codecov command: bash <(curl -s https://codecov.io/bash) -F app - store_artifacts: - path: ./app/build/reports/tests/ - destination: tests_reports/ - - store_artifacts: - path: ./app/build/reports/jacoco/jacocoTestDebugUnitTestReport/ - destination: coverage_reports/ + path: ./app/build/reports/ + destination: reports/ - store_test_results: path: ./app/build/test-results - persist_to_workspace: @@ -103,11 +109,8 @@ jobs: name: Upload code coverage to codecov command: bash <(curl -s https://codecov.io/bash) -F api - store_artifacts: - path: ./api/build/reports/tests/ - destination: tests_reports/ - - store_artifacts: - path: ./api/build/reports/jacoco/test/ - destination: coverage_reports/ + path: ./api/build/reports/ + destination: reports/ - store_test_results: path: ./api/build/test-results - persist_to_workspace: @@ -121,11 +124,14 @@ jobs: - *attach_workspace - run: name: Setup emulator - command: sdkmanager "system-images;android-19;google_apis;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-19;google_apis;armeabi-v7a" + command: sdkmanager "system-images;android-16;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-16;default;armeabi-v7a" - run: name: Launch emulator command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel on background: true + - run: + name: Change circle-android script file permissions + command: sudo chmod +rx /bin/circle-android - run: name: Wait emulator command: | @@ -169,30 +175,69 @@ jobs: name: Run sonarqube runner command: ./gradlew -x test -x lint sonarqube -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=$CIRCLE_BRANCH --no-daemon --stacktrace --console=plain -PdisablePreDex + deploy: + <<: *container_config + steps: + - *attach_workspace + - restore_cache: + <<: *general_cache_key + - run: + name: Decrypt keys + command: | + openssl aes-256-cbc -d -in ./app/key-encrypted.p12 -k $ENCRYPT_KEY >> ./app/key.p12 + openssl aes-256-cbc -d -in ./app/upload-key-encrypted.jks -k $ENCRYPT_KEY >> ./app/upload-key.jks + - run: + name: Publish release + command: ./gradlew publishRelease --no-daemon --stacktrace --console=plain -PdisablePreDex + workflows: version: 2 - build_check_tests: + build-test-deploy: jobs: - - build + - build: + filters: + tags: + only: /.*/ - lint: + filters: + tags: + only: /.*/ requires: - build - app-test: + filters: + tags: + only: /.*/ requires: - build - api-test: + filters: + tags: + only: /.*/ requires: - build - instrumented: - requires: - - build + filters: + tags: + only: /.*/ requires: - build - sonarcube: + filters: + tags: + only: /.*/ requires: - build - lint - app-test - api-test - instrumented + - deploy: + requires: + - instrumented + filters: + tags: + only: /\d+\.\d+\.\d+/ + branches: + ignore: /.*/ diff --git a/.gitignore b/.gitignore index 3b524b65..4f2819d4 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,8 @@ local.properties .idea/tasks.xml .idea/vcs.xml .idea/workspace.xml +.idea/caches/ +.idea/codeStyles/ *.iml # OS-specific files @@ -42,3 +44,7 @@ local.properties .Trashes ehthumbs.db Thumbs.db +.idea/codeStyles/ +.idea/caches/ +app/key.p12 +app/upload-key.jks diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d8fdd058..bf80ae04 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,7 +12,7 @@ build: script: - ./gradlew --no-daemon --stacktrace dependencies || true - ./gradlew --no-daemon --stacktrace assembleDebug - - mv app/build/outputs/apk/app-debug.apk . + - mv app/build/outputs/apk/debug/app-debug.apk . artifacts: name: "${CI_PROJECT_NAME}_${CI_BUILD_REF_NAME}-${CI_BUILD_ID}" paths: @@ -26,7 +26,7 @@ tests: - .gradle policy: pull script: - - ./gradlew --no-daemon --stacktrace test + - ./gradlew --no-daemon --stacktrace -x fabricGenerateResourcesRelease test artifacts: paths: - app/build/reports/tests @@ -39,7 +39,7 @@ lint: - .gradle policy: pull script: - - ./gradlew --no-daemon --stacktrace lint + - ./gradlew --no-daemon --stacktrace -x fabricGenerateResourcesRelease lint artifacts: paths: - app/build/reports diff --git a/README.md b/README.md index 76a8d6e6..ee11a9c6 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ # Wulkanowy -[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) -[![Dependency Status](https://www.versioneye.com/user/projects/5969ff0b0fb24f004f8c711b/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/5969ff0b0fb24f004f8c711b) -[![CircleCI](https://img.shields.io/circleci/project/github/wulkanowy/wulkanowy.svg?style=flat-square)](https://circleci.com/gh/wulkanowy/wulkanowy) +[![CircleCI](https://img.shields.io/circleci/project/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://circleci.com/gh/wulkanowy/wulkanowy) [![Bitrise](https://img.shields.io/bitrise/daeff1893f3c8128/master.svg?token=Hjm1ACamk86JDeVVJHOeqQ&style=flat-square)](https://www.bitrise.io/app/daeff1893f3c8128) +[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![BCH compliance](https://bettercodehub.com/edge/badge/wulkanowy/wulkanowy?branch=master)](https://bettercodehub.com/) +[![Scrutinizer](https://img.shields.io/scrutinizer/g/wulkanowy/wulkanowy.svg?style=flat-square)](https://scrutinizer-ci.com/g/wulkanowy/wulkanowy/?branch=master) [![Bintray](https://img.shields.io/bintray/v/wulkanowy/wulkanowy/api.svg?style=flat-square)](https://bintray.com/wulkanowy/wulkanowy/api) +[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) -[Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-release-bitrise-signed.apk) +[Pobierz wersję beta](https://play.google.com/store/apps/details?id=io.github.wulkanowy&utm_source=vcs) -Wulkanowy to projekt aplikacji na androida ułatwiającej używanie dziennika VULCANa. +[Pobierz wersję DEV](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/0) + +Androidowy klient dziennika VULCAN UONET+. diff --git a/api/build.gradle b/api/build.gradle index ebaca4c8..5e5eeee3 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'java-library' +apply plugin: 'kotlin' apply plugin: 'org.sonarqube' apply plugin: 'jacoco' apply plugin: 'com.jfrog.bintray' @@ -28,12 +29,15 @@ jacocoTestReport { } dependencies { - implementation 'org.jsoup:jsoup:1.10.3' - implementation 'org.apache.commons:commons-lang3:3.7' - implementation 'com.google.code.gson:gson:2.8.2' + implementation "org.jsoup:jsoup:$jsoup" + implementation "org.apache.commons:commons-lang3:$apacheLang" + implementation "com.google.code.gson:gson:$gson" + implementation "org.slf4j:slf4j-api:$slf4jApi" - testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.13.0' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + + testImplementation "junit:junit:$junit" + testImplementation "org.mockito:mockito-core:$mockito" } version = PUBLISH_VERSION 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 f7a663ec..c6360d04 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Client.java +++ b/api/src/main/java/io/github/wulkanowy/api/Client.java @@ -3,58 +3,175 @@ package io.github.wulkanowy.api; import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.Date; +import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; + +import io.github.wulkanowy.api.generic.School; +import io.github.wulkanowy.api.login.Login; public class Client { - private String protocol; + private String protocol = "https"; - private String host; + private String host = "vulcan.net.pl"; + + private String email; + + private String password; private String symbol; + private String schoolId; + + private List schools; + + private Date lastSuccessRequest; + private Cookies cookies = new Cookies(); - Client(String protocol, String host, String symbol) { - this.protocol = protocol; - this.host = host; + private static final Logger logger = LoggerFactory.getLogger(Client.class); + + Client(String email, String password, String symbol, String schoolId) { + this.email = email; + this.password = password; this.symbol = symbol; + this.schoolId = schoolId; + + setFullEndpointInfo(email); } - String getHost() { - return host; + private void setFullEndpointInfo(String info) { + String[] creds = info.split("\\\\"); + + email = info; + + if (creds.length > 2) { + String[] url = creds[0].split("://"); + + protocol = url[0]; + String[] path = url[1].split("/"); + host = path[0]; + if (path.length > 1) { + symbol = path[1]; + } + email = creds[2]; + } + } + + private void login() throws IOException, VulcanException { + if (isLoggedIn()) { + return; + } + + logger.info("Not logged. Login..."); + + clearCookies(); + new Login(this).login(email, password, symbol); + lastSuccessRequest = new Date(); + + logger.info("Login successful on {} at {}", getHost(), new Date()); + } + + private boolean isLoggedIn() { + logger.trace("Last success request: {}", lastSuccessRequest); + logger.trace("Cookies: {}", getCookies().size()); + + return getCookies().size() > 0 && lastSuccessRequest != null && + 5 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime()); + + } + + public String getSymbol() { + return symbol; } public void setSymbol(String symbol) { this.symbol = symbol; } - Map getCookies() { + private Map getCookies() { return cookies.getItems(); } - private String getFilledUrl(String url) { + public void clearCookies() { + cookies = new Cookies(); + } + + public String getHost() { + return host; + } + + public void setSchools(List schools) { + this.schools = schools; + this.schoolId = schools.get(0).getId(); + } + + public List getSchools() throws IOException, VulcanException { + login(); + return schools; + } + + public String getSchoolId() throws IOException, VulcanException { + return schoolId != null ? schoolId : getSchools().get(0).getId(); + } + + String getFilledUrl(String url) { return url .replace("{schema}", protocol) - .replace("{host}", host.replace(":", "%253A")) - .replace("{symbol}", symbol == null ? "Default" : symbol); + .replace("{host}", host) + .replace("{symbol}", symbol) + .replace("{ID}", schoolId != null ? schoolId : ""); } - Document getPageByUrl(String url) throws IOException { - Connection.Response response = Jsoup.connect(getFilledUrl(url)) + 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 synchronized Document getPageByUrl(String url, boolean loginBefore, Map cookies) throws IOException, VulcanException { + if (loginBefore) { + login(); + } + + if (null != cookies) { + this.cookies.addItems(cookies); + } + + url = getFilledUrl(url); + + logger.debug("GET {}", url); + + Connection.Response response = Jsoup.connect(url) .followRedirects(true) .cookies(getCookies()) .execute(); this.cookies.addItems(response.cookies()); - return response.parse(); + Document doc = checkForErrors(response.parse(), response.statusCode()); + + if (loginBefore) { + lastSuccessRequest = new Date(); + } + + return doc; } - public Document postPageByUrl(String url, String[][] params) throws IOException { - Connection connection = Jsoup.connect(getFilledUrl(url)); + public synchronized Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException { + url = getFilledUrl(url); + + logger.debug("POST {}", url); + + Connection connection = Jsoup.connect(url); for (String[] data : params) { connection.data(data[0], data[1]); @@ -68,11 +185,19 @@ public class Client { this.cookies.addItems(response.cookies()); - return response.parse(); + response.bufferUp(); // fixes cert parsing issues #109 + + return checkForErrors(response.parse(), response.statusCode()); } - public String getJsonStringByUrl(String url) throws IOException { - Connection.Response response = Jsoup.connect(getFilledUrl(url)) + public String getJsonStringByUrl(String url) throws IOException, VulcanException { + login(); + + url = getFilledUrl(url); + + logger.debug("GET {}", url); + + Connection.Response response = Jsoup.connect(url) .followRedirects(true) .ignoreContentType(true) .cookies(getCookies()) @@ -83,8 +208,14 @@ public class Client { return response.body(); } - public String postJsonStringByUrl(String url, String[][] params) throws IOException { - Connection connection = Jsoup.connect(getFilledUrl(url)); + public String postJsonStringByUrl(String url, String[][] params) throws IOException, VulcanException { + login(); + + url = getFilledUrl(url); + + logger.debug("POST {}", url); + + Connection connection = Jsoup.connect(url); for (String[] data : params) { connection.data(data[0], data[1]); @@ -101,4 +232,24 @@ public class Client { return response.body(); } + + Document checkForErrors(Document doc, int code) throws VulcanException { + lastSuccessRequest = null; + + String title = doc.select("title").text(); + if ("Przerwa techniczna".equals(title)) { + throw new VulcanOfflineException(title); + } + + String singIn = doc.select(".loginButton").text(); + if ("Zaloguj się".equals(singIn)) { + throw new NotLoggedInErrorException(singIn); + } + + if ("Błąd strony".equals(title)) { + throw new NotLoggedInErrorException(title + " " + doc.body() + ", status: " + code); + } + + return doc; + } } diff --git a/api/src/main/java/io/github/wulkanowy/api/Cookies.java b/api/src/main/java/io/github/wulkanowy/api/Cookies.java index c97d9658..dfe4c4b5 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Cookies.java +++ b/api/src/main/java/io/github/wulkanowy/api/Cookies.java @@ -3,21 +3,15 @@ package io.github.wulkanowy.api; import java.util.HashMap; import java.util.Map; -public class Cookies { +class Cookies { private Map jar = new HashMap<>(); - public Map getItems() { + Map getItems() { return jar; } - public Cookies setItems(Map items) { - this.jar = items; - return this; - } - - public Cookies addItems(Map items) { - this.jar.putAll(items); - return this; + void addItems(Map items) { + jar.putAll(items); } } diff --git a/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt b/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt new file mode 100644 index 00000000..3b620db5 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.api + +import java.text.SimpleDateFormat +import java.util.* + +const val LOG_DATE_PATTERN = "dd.MM.yyyy" +const val API_DATE_PATTERN = "yyyy-MM-dd" + +const val TICKS_AT_EPOCH = 621355968000000000L +const val TICKS_PER_MILLISECOND = 10000 + +fun getFormattedDate(date: String): String { + return getFormattedDate(date, API_DATE_PATTERN) +} + +fun getFormattedDate(date: String, format: String): String { + return getFormattedDate(date, LOG_DATE_PATTERN, format) +} + +fun getFormattedDate(date: String, fromFormat: String, toFormat: String): String { + val sdf = SimpleDateFormat(fromFormat, Locale.ROOT) + val d = sdf.parse(date) + sdf.applyPattern(toFormat) + + return sdf.format(d) +} + +fun getDateAsTick(dateString: String?): String { + if (dateString.isNullOrEmpty()) { + return "" + } + + return getDateAsTick(dateString as String, API_DATE_PATTERN).toString() +} + +fun getDateAsTick(dateString: String, dateFormat: String): Long { + val format = SimpleDateFormat(dateFormat, Locale.ROOT) + format.timeZone = TimeZone.getTimeZone("UTC") + val dateObject = format.parse(dateString) + + return getDateAsTick(dateObject) +} + +fun getDateAsTick(date: Date): Long { + val calendar = Calendar.getInstance() + calendar.time = date + + return calendar.timeInMillis * TICKS_PER_MILLISECOND + TICKS_AT_EPOCH +} + +fun getDate(netTicks: Long): Date { + return Date((netTicks - TICKS_AT_EPOCH) / TICKS_PER_MILLISECOND) +} diff --git a/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java b/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java new file mode 100644 index 00000000..179fc8cc --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java @@ -0,0 +1,8 @@ +package io.github.wulkanowy.api; + +public class NotLoggedInErrorException extends VulcanException { + + public NotLoggedInErrorException(String message) { + super(message); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/Semester.java b/api/src/main/java/io/github/wulkanowy/api/Semester.java deleted file mode 100644 index 64a07c97..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/Semester.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.api; - -public class Semester { - - private String number = ""; - - private String id = ""; - - private boolean isCurrent = false; - - public String getNumber() { - return number; - } - - public Semester setNumber(String number) { - this.number = number; - return this; - } - - public String getId() { - return id; - } - - public Semester setId(String id) { - this.id = id; - return this; - } - - public boolean isCurrent() { - return isCurrent; - } - - public Semester setCurrent(boolean current) { - isCurrent = 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 614b77b6..84ec1aed 100644 --- a/api/src/main/java/io/github/wulkanowy/api/SnP.java +++ b/api/src/main/java/io/github/wulkanowy/api/SnP.java @@ -6,21 +6,30 @@ import org.jsoup.nodes.Element; import java.io.IOException; import java.util.List; -import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.api.generic.Diary; +import io.github.wulkanowy.api.generic.ParamItem; +import io.github.wulkanowy.api.generic.Semester; +import io.github.wulkanowy.api.generic.Student; public interface SnP { - String getId(); + void setDiaryID(String id); - void storeContextCookies() throws IOException, NotLoggedInErrorException; + String getStudentID(); + + List getStudents() throws IOException, VulcanException; + + StudentAndParent setUp() throws IOException, VulcanException; String getRowDataChildValue(Element e, int index); - Document getSnPPageDocument(String url) throws IOException; + Document getSnPPageDocument(String url) throws IOException, VulcanException; - List getSemesters() throws IOException; + 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/StudentAndParent.java b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java index a8d61913..ba527f6a 100644 --- a/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java +++ b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java @@ -3,85 +3,108 @@ package io.github.wulkanowy.api; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; +import java.net.URL; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; -import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.api.generic.Diary; +import io.github.wulkanowy.api.generic.ParamItem; +import io.github.wulkanowy.api.generic.Semester; +import io.github.wulkanowy.api.generic.Student; public class StudentAndParent implements SnP { - private static final String START_PAGE_URL = "{schema}://uonetplus.{host}/{symbol}/Start.mvc/Index"; - private static final String BASE_URL = "{schema}://uonetplus-opiekun.{host}/{symbol}/{ID}/"; private static final String GRADES_PAGE_URL = "Oceny/Wszystkie"; private Client client; - private String id; + private String studentID; - StudentAndParent(Client client) { + private String diaryID; + + private static final Logger logger = LoggerFactory.getLogger(StudentAndParent.class); + + StudentAndParent(Client client, String studentID, String diaryID) { this.client = client; + this.studentID = studentID; + this.diaryID = diaryID; } - StudentAndParent(Client client, String id) { - this(client); - this.id = id; - } + public StudentAndParent setUp() throws IOException, VulcanException { + if (null == getStudentID() || "".equals(getStudentID())) { + Document doc = client.getPageByUrl(BASE_URL); - private String getBaseUrl() { - return BASE_URL.replace("{ID}", getId()); - } + if (doc.select("#idSection").isEmpty()) { + logger.error("Expected SnP page, got page with title: {} {}", doc.title(), doc.selectFirst("body")); + throw new VulcanException("Nieznany błąd podczas pobierania danych. Strona: " + doc.title()); + } - public String getId() { - return id; - } + Student student = getCurrent(getStudents(doc)); + studentID = student.getId(); - public void storeContextCookies() throws IOException, NotLoggedInErrorException { - client.getPageByUrl(getSnpHomePageUrl()); - } - - String getSnpHomePageUrl() throws IOException, NotLoggedInErrorException { - if (null != getId()) { - return getBaseUrl(); + Diary diary = getCurrent(getDiaries(doc)); + diaryID = diary.getId(); } - // get url to uonetplus-opiekun.vulcan.net.pl - Document startPage = client.getPageByUrl(START_PAGE_URL); - Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first(); - - if (null == studentTileLink) { - throw new NotLoggedInErrorException(); - } - - String snpPageUrl = studentTileLink.attr("href"); - - this.id = getExtractedIdFromUrl(snpPageUrl); - - return snpPageUrl; + return this; } - String getExtractedIdFromUrl(String snpPageUrl) throws NotLoggedInErrorException { - String[] path = snpPageUrl.split(client.getHost())[1].split("/"); - - if (5 != path.length) { - throw new NotLoggedInErrorException(); - } - - return path[2]; + public String getStudentID() { + return studentID; } public String getRowDataChildValue(Element e, int index) { return e.select(".daneWiersz .wartosc").get(index - 1).text(); } - public Document getSnPPageDocument(String url) throws IOException { - return client.getPageByUrl(getBaseUrl() + url); + public void setDiaryID(String id) { + this.diaryID = id; } - public List getSemesters() throws IOException { + public Document getSnPPageDocument(String url) throws IOException, VulcanException { + Map cookies = new HashMap<>(); + cookies.put("idBiezacyDziennik", diaryID); + cookies.put("idBiezacyUczen", studentID); + + Document doc = client.getPageByUrl(BASE_URL + url, true, cookies); + + if (!doc.title().startsWith("Witryna ucznia i rodzica")) { + logger.error("Expected SnP page, got page with title: {} {}", doc.title(), doc.selectFirst("body")); + throw new VulcanException("Nieznany błąd podczas pobierania danych. Strona: " + doc.title()); + } + + if (doc.title().endsWith("Strona główna")) { + throw new VulcanException("Sesja została nieprawidłowo zainicjowana"); + } + + return doc; + } + + public List getDiaries() throws IOException, VulcanException { + return getDiaries(client.getPageByUrl(BASE_URL)); + } + + 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(BASE_URL)); + } + + private List getStudents(Document doc) throws IOException, VulcanException { + return getList(doc.select("#uczenDropDownList option"), Student.class); + } + + public List getSemesters() throws IOException, VulcanException { return getSemesters(getSnPPageDocument(GRADES_PAGE_URL)); } @@ -92,10 +115,10 @@ public class StudentAndParent implements SnP { for (Element e : semesterOptions) { Semester semester = new Semester() - .setId(e.text()) - .setNumber(e.attr("value")); + .setId(e.attr("value")) + .setName(e.text()); - if ("selected".equals(e.attr("selected"))) { + if (isCurrent(e)) { semester.setCurrent(true); } @@ -105,15 +128,44 @@ 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); + } + + 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 a44e868f..bf482969 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Vulcan.java +++ b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java @@ -1,19 +1,20 @@ package io.github.wulkanowy.api; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; +import java.util.List; import io.github.wulkanowy.api.attendance.AttendanceStatistics; import io.github.wulkanowy.api.attendance.AttendanceTable; import io.github.wulkanowy.api.exams.ExamsWeek; +import io.github.wulkanowy.api.generic.School; import io.github.wulkanowy.api.grades.GradesList; import io.github.wulkanowy.api.grades.SubjectsList; -import io.github.wulkanowy.api.login.AccountPermissionException; -import io.github.wulkanowy.api.login.BadCredentialsException; -import io.github.wulkanowy.api.login.Login; -import io.github.wulkanowy.api.login.LoginErrorException; -import io.github.wulkanowy.api.login.NotLoggedInErrorException; -import io.github.wulkanowy.api.login.VulcanOfflineException; import io.github.wulkanowy.api.messages.Messages; +import io.github.wulkanowy.api.mobile.RegisterDevice; +import io.github.wulkanowy.api.mobile.RegisteredDevices; import io.github.wulkanowy.api.notes.AchievementsList; import io.github.wulkanowy.api.notes.NotesList; import io.github.wulkanowy.api.school.SchoolInfo; @@ -24,173 +25,109 @@ import io.github.wulkanowy.api.user.FamilyInformation; public class Vulcan { - private String id; - - private String symbol; - private SnP snp; - private String protocolSchema = "https"; - - private String logHost = "vulcan.net.pl"; - - private String email; - private Client client; - private Login login; + private String studentId; - public void setClient(Client client) { - this.client = client; + private String diaryId; + + private static final Logger logger = LoggerFactory.getLogger(Vulcan.class); + + public void setCredentials(String email, String password, String symbol, String schoolId, String studentId, String diaryId) { + this.studentId = studentId; + this.diaryId = diaryId; + + client = new Client(email, password, symbol, schoolId); + + logger.debug("Client created with symbol " + symbol); } - public void setLogin(Login login) { - this.login = login; - } - - public void login(String email, String password, String symbol) - throws BadCredentialsException, AccountPermissionException, - LoginErrorException, IOException, VulcanOfflineException { - - setFullEndpointInfo(email); - login = getLogin(); - - this.symbol = login.login(this.email, password, symbol); - } - - public Vulcan login(String email, String password, String symbol, String id) - throws BadCredentialsException, AccountPermissionException, - LoginErrorException, IOException, VulcanOfflineException { - login(email, password, symbol); - - this.id = id; - - return this; - } - - String getProtocolSchema() { - return protocolSchema; - } - - String getLogHost() { - return logHost; - } - - public String getEmail() { - return email; - } - - public String getSymbol() { - return symbol; - } - - private void setFullEndpointInfo(String email) { - String[] creds = email.split("\\\\"); - - this.email = email; - - if (creds.length >= 2) { - String[] url = creds[0].split("://"); - - this.protocolSchema = url[0]; - this.logHost = url[1]; - this.email = creds[2]; + public Client getClient() throws NotLoggedInErrorException { + if (null == client) { + throw new NotLoggedInErrorException("Vulcan must be initialized by calling setCredentials() prior to fetch data"); } - } - - protected Client getClient() { - if (null != client) { - return client; - } - - client = new Client(getProtocolSchema(), getLogHost(), symbol); return client; } - protected Login getLogin() { - if (null != login) { - return login; - } - - login = new Login(getClient()); - - return login; + public String getSymbol() throws NotLoggedInErrorException { + return getClient().getSymbol(); } - public SnP getStudentAndParent() throws IOException, NotLoggedInErrorException { - if (0 == getClient().getCookies().size()) { - throw new NotLoggedInErrorException(); - } - - if (null != snp) { - return snp; - } - - snp = createSnp(getClient(), id); - - snp.storeContextCookies(); - - return snp; + public List getSchools() throws VulcanException, IOException { + return getClient().getSchools(); } - SnP createSnp(Client client, String id) { - if (null == id) { - return new StudentAndParent(client); + public SnP getStudentAndParent() throws VulcanException, IOException { + if (null != this.snp) { + return this.snp; } - return new StudentAndParent(client, id); + this.snp = new StudentAndParent(getClient(), studentId, diaryId) + .setUp(); + + return this.snp; } - public AttendanceStatistics getAttendanceStatistics() throws IOException, NotLoggedInErrorException { - return new AttendanceStatistics(getStudentAndParent()); - } - - public AttendanceTable getAttendanceTable() throws IOException, NotLoggedInErrorException { + public AttendanceTable getAttendanceTable() throws IOException, VulcanException { return new AttendanceTable(getStudentAndParent()); } - public ExamsWeek getExamsList() throws IOException, NotLoggedInErrorException { + public AttendanceStatistics getAttendanceStatistics() throws IOException, VulcanException { + return new AttendanceStatistics(getStudentAndParent()); + } + + public ExamsWeek getExamsList() throws IOException, VulcanException { return new ExamsWeek(getStudentAndParent()); } - public GradesList getGradesList() throws IOException, NotLoggedInErrorException { + public GradesList getGradesList() throws IOException, VulcanException { return new GradesList(getStudentAndParent()); } - public SubjectsList getSubjectsList() throws IOException, NotLoggedInErrorException { + public SubjectsList getSubjectsList() throws IOException, VulcanException { return new SubjectsList(getStudentAndParent()); } - public AchievementsList getAchievementsList() throws IOException, NotLoggedInErrorException { + public AchievementsList getAchievementsList() throws IOException, VulcanException { return new AchievementsList(getStudentAndParent()); } - public NotesList getNotesList() throws IOException, NotLoggedInErrorException { + public NotesList getNotesList() throws IOException, VulcanException { return new NotesList(getStudentAndParent()); } - public SchoolInfo getSchoolInfo() throws IOException, NotLoggedInErrorException { + public SchoolInfo getSchoolInfo() throws IOException, VulcanException { return new SchoolInfo(getStudentAndParent()); } - public TeachersInfo getTeachersInfo() throws IOException, NotLoggedInErrorException { + public TeachersInfo getTeachersInfo() throws IOException, VulcanException { return new TeachersInfo(getStudentAndParent()); } - public Timetable getTimetable() throws IOException, NotLoggedInErrorException { + public Timetable getTimetable() throws IOException, VulcanException { return new Timetable(getStudentAndParent()); } - public BasicInformation getBasicInformation() throws IOException, NotLoggedInErrorException { + public BasicInformation getBasicInformation() throws IOException, VulcanException { return new BasicInformation(getStudentAndParent()); } - public FamilyInformation getFamilyInformation() throws IOException, NotLoggedInErrorException { + public FamilyInformation getFamilyInformation() throws IOException, VulcanException { return new FamilyInformation(getStudentAndParent()); } - public Messages getMessages() { + public RegisteredDevices getRegisteredDevices() throws VulcanException, IOException { + return new RegisteredDevices(getStudentAndParent()); + } + + public RegisterDevice getRegisterDevice() throws VulcanException, IOException { + return new RegisterDevice(getStudentAndParent()); + } + + public Messages getMessages() throws VulcanException { return new Messages(getClient()); } } diff --git a/api/src/main/java/io/github/wulkanowy/api/VulcanException.java b/api/src/main/java/io/github/wulkanowy/api/VulcanException.java new file mode 100644 index 00000000..0bc0c51f --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/VulcanException.java @@ -0,0 +1,12 @@ +package io.github.wulkanowy.api; + +public class VulcanException extends Exception { + + public 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/VulcanOfflineException.java b/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java new file mode 100644 index 00000000..24ab48e6 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java @@ -0,0 +1,8 @@ +package io.github.wulkanowy.api; + +public class VulcanOfflineException extends VulcanException { + + VulcanOfflineException(String message) { + super(message); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceStatistics.java b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceStatistics.java index 023a436e..29b6a6d9 100644 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceStatistics.java +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceStatistics.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.List; import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.api.generic.Month; import io.github.wulkanowy.api.generic.Subject; @@ -22,15 +23,15 @@ public class AttendanceStatistics { this.snp = snp; } - public Types getTypesTable() throws IOException { + public Types getTypesTable() throws IOException, VulcanException { return getTypesTable(""); } - public Types getTypesTable(String tick) throws IOException { + public Types getTypesTable(String tick) throws IOException, VulcanException { return getTypesTable(tick, -1); } - public List getSubjectList() throws IOException { + public List getSubjectList() throws IOException, VulcanException { Element mainContainer = snp.getSnPPageDocument(attendancePageUrl) .select(".mainContainer #idPrzedmiot").first(); @@ -46,7 +47,7 @@ public class AttendanceStatistics { return subjectList; } - public Types getTypesTable(String tick, Integer subjectId) throws IOException { + public Types getTypesTable(String tick, Integer subjectId) throws IOException, VulcanException { Element mainContainer = snp.getSnPPageDocument((attendancePageUrl + "?data={tick}&idPrzedmiot={subject}") .replace("{tick}", tick) diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java index 7c9fc27f..7caa70d8 100644 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java @@ -8,33 +8,42 @@ import java.util.ArrayList; import java.util.List; import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.api.generic.Day; import io.github.wulkanowy.api.generic.Lesson; import io.github.wulkanowy.api.generic.Week; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getDateAsTick; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; + public class AttendanceTable { - private SnP snp; + private final static String ATTENDANCE_PAGE_URL = "Frekwencja.mvc?data="; - private String attendancePageUrl = "Frekwencja.mvc?data="; + private SnP snp; public AttendanceTable(SnP snp) { this.snp = snp; } - public Week getWeekTable() throws IOException { + public Week getWeekTable() throws IOException, VulcanException { return getWeekTable(""); } - public Week getWeekTable(String tick) throws IOException { - Element table = snp.getSnPPageDocument(attendancePageUrl + tick) + public Week getWeekTable(String date) throws IOException, VulcanException { + Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + getDateAsTick(date)) .select(".mainContainer .presentData").first(); Elements headerCells = table.select("thead th"); List days = new ArrayList<>(); for (int i = 1; i < headerCells.size(); i++) { - days.add(new Day().setDate(headerCells.get(i).html().split("
")[1])); + String[] dayHeaderCell = headerCells.get(i).html().split("
"); + + days.add(new Day() + .setDayName(dayHeaderCell[0]) + .setDate(getFormattedDate(dayHeaderCell[1].trim())) + ); } Elements hoursInDays = table.select("tbody tr"); @@ -46,26 +55,29 @@ public class AttendanceTable { // fill hours in day int size = hours.size(); for (int i = 1; i < size; i++) { - days.get(i - 1).setLesson(getNewLesson(hours.get(i))); + Lesson lesson = new Lesson(); + lesson.setDate(days.get(i - 1).getDate()); + lesson.setNumber(Integer.valueOf(hours.get(0).text())); + + addLessonDetails(lesson, hours.get(i)); + + days.get(i - 1).setLesson(lesson); } } - String[] dayDescription = headerCells.get(1).html().split("
"); - return new Week() - .setStartDayDate(dayDescription[1]) + .setStartDayDate(days.get(0).getDate()) .setDays(days); } - private Lesson getNewLesson(Element cell) { - Lesson lesson = new Lesson(); + private void addLessonDetails(Lesson lesson, Element cell) { lesson.setSubject(cell.select("span").text()); if (LessonTypes.CLASS_NOT_EXIST.equals(cell.attr("class"))) { lesson.setNotExist(true); lesson.setEmpty(true); - return lesson; + return; } switch (cell.select("div").attr("class")) { @@ -95,7 +107,5 @@ public class AttendanceTable { lesson.setEmpty(true); break; } - - return lesson; } } diff --git a/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java b/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java index b4921b8b..391056ac 100644 --- a/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java +++ b/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java @@ -1,5 +1,6 @@ package io.github.wulkanowy.api.exams; +import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; @@ -9,8 +10,12 @@ import java.util.ArrayList; import java.util.List; import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.api.generic.Week; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getDateAsTick; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; + public class ExamsWeek { private static final String EXAMS_PAGE_URL = "Sprawdziany.mvc/Terminarz?rodzajWidoku=2&data="; @@ -21,12 +26,12 @@ public class ExamsWeek { this.snp = snp; } - public Week getCurrent() throws IOException { + public Week getCurrent() throws IOException, VulcanException { return getWeek("", true); } - public Week getWeek(String tick, final boolean onlyNotEmpty) throws IOException { - Document examsPage = snp.getSnPPageDocument(EXAMS_PAGE_URL + tick); + public Week getWeek(String date, final boolean onlyNotEmpty) throws IOException, VulcanException { + Document examsPage = snp.getSnPPageDocument(EXAMS_PAGE_URL + getDateAsTick(date)); Elements examsDays = examsPage.select(".mainContainer > div:not(.navigation)"); List days = new ArrayList<>(); @@ -40,7 +45,9 @@ public class ExamsWeek { } if (null != dayHeading) { - day.setDate(dayHeading.text().split(", ")[1]); + String[] dateHeader = dayHeading.text().split(", "); + day.setDayName(StringUtils.capitalize(dateHeader[0])); + day.setDate(getFormattedDate(dateHeader[1])); } Elements exams = item.select("article"); @@ -50,15 +57,17 @@ public class ExamsWeek { .setType(snp.getRowDataChildValue(e, 2)) .setDescription(snp.getRowDataChildValue(e, 3)) .setTeacher(snp.getRowDataChildValue(e, 4).split(", ")[0]) - .setEntryDate(snp.getRowDataChildValue(e, 4).split(", ")[1]) + .setEntryDate(getFormattedDate(snp.getRowDataChildValue(e, 4).split(", ")[1])) ); } days.add(day); } + return new Week() - .setStartDayDate(examsDays.select("h2").first().text().split(" ")[1]) + .setStartDayDate(getFormattedDate(examsPage.select(".mainContainer > h2") + .first().text().split(" ")[1])) .setDays(days); } } diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Day.java b/api/src/main/java/io/github/wulkanowy/api/generic/Day.java index 6bf26c04..fbb5ee0a 100644 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Day.java +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Day.java @@ -11,10 +11,6 @@ public class Day { private String dayName = ""; - private boolean isFreeDay = false; - - private String freeDayName = ""; - public Lesson getLesson(int index) { return lessons.get(index); } @@ -41,23 +37,8 @@ public class Day { return dayName; } - public void setDayName(String dayName) { + public Day setDayName(String dayName) { this.dayName = dayName; - } - - public boolean isFreeDay() { - return isFreeDay; - } - - public void setFreeDay(boolean freeDay) { - isFreeDay = freeDay; - } - - public String getFreeDayName() { - return freeDayName; - } - - public void setFreeDayName(String freeDayName) { - this.freeDayName = freeDayName; + return this; } } diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Diary.java b/api/src/main/java/io/github/wulkanowy/api/generic/Diary.java new file mode 100644 index 00000000..5c7c8593 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Diary.java @@ -0,0 +1,38 @@ +package io.github.wulkanowy.api.generic; + +public class Diary implements ParamItem { + + private String id = ""; + + private String name = ""; + + private boolean current = false; + + public String getId() { + return id; + } + + public Diary setId(String id) { + this.id = id; + 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/generic/Lesson.java b/api/src/main/java/io/github/wulkanowy/api/generic/Lesson.java index 8dd65327..27bf9bf0 100644 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Lesson.java +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Lesson.java @@ -2,7 +2,7 @@ package io.github.wulkanowy.api.generic; public class Lesson { - private String number = ""; + private int number = 0; private String subject = ""; @@ -48,12 +48,13 @@ public class Lesson { private boolean isExemption = false; - public String getNumber() { + public int getNumber() { return number; } - public void setNumber(String number) { + public Lesson setNumber(int number) { this.number = number; + return this; } public String getSubject() { diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/ParamItem.java b/api/src/main/java/io/github/wulkanowy/api/generic/ParamItem.java new file mode 100644 index 00000000..e7edfbf4 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/ParamItem.java @@ -0,0 +1,12 @@ +package io.github.wulkanowy.api.generic; + +public interface ParamItem { + + ParamItem setId(String id); + + ParamItem setName(String name); + + ParamItem setCurrent(boolean isCurrent); + + boolean isCurrent(); +} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/School.kt b/api/src/main/java/io/github/wulkanowy/api/generic/School.kt new file mode 100644 index 00000000..c14fc5d2 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/School.kt @@ -0,0 +1,7 @@ +package io.github.wulkanowy.api.generic + +data class School( + val name: String, + val id: String, + val current: Boolean +) diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Semester.java b/api/src/main/java/io/github/wulkanowy/api/generic/Semester.java new file mode 100644 index 00000000..db4a724d --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Semester.java @@ -0,0 +1,37 @@ +package io.github.wulkanowy.api.generic; + +public class Semester implements ParamItem { + + private String id = ""; + + private String name = ""; + + private boolean current = false; + + public String getId() { + return id; + } + + public Semester setId(String id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public Semester setName(String number) { + this.name = number; + return this; + } + + public boolean isCurrent() { + return current; + } + + public Semester setCurrent(boolean current) { + this.current = current; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Student.java b/api/src/main/java/io/github/wulkanowy/api/generic/Student.java new file mode 100644 index 00000000..4ed5dd37 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Student.java @@ -0,0 +1,37 @@ +package io.github.wulkanowy.api.generic; + +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 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/grades/Grade.java b/api/src/main/java/io/github/wulkanowy/api/grades/Grade.java index 564b3e47..31e1bbbd 100644 --- a/api/src/main/java/io/github/wulkanowy/api/grades/Grade.java +++ b/api/src/main/java/io/github/wulkanowy/api/grades/Grade.java @@ -18,8 +18,6 @@ public class Grade { private String teacher = ""; - private String semester = ""; - public String getSubject() { return subject; } @@ -99,14 +97,4 @@ public class Grade { return this; } - - public String getSemester() { - return semester; - } - - public Grade setSemester(String semester) { - this.semester = semester; - - return this; - } } 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 44c45c9a..cb9fe465 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 @@ -5,78 +5,71 @@ import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.IOException; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Date; import java.util.List; -import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; -import io.github.wulkanowy.api.Semester; import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; public class GradesList { private static final String GRADES_PAGE_URL = "Oceny/Wszystkie?details=2&okres="; - private SnP snp = null; - - private List grades = new ArrayList<>(); + private SnP snp; public GradesList(SnP snp) { this.snp = snp; } - private String getGradesPageUrl() { - return GRADES_PAGE_URL; - } - - public List getAll() throws IOException, ParseException { - return getAll(""); - } - - public List getAll(String semester) throws IOException, ParseException { - Document gradesPage = snp.getSnPPageDocument(getGradesPageUrl() + semester); + public List getAll(String semester) throws IOException, VulcanException { + Document gradesPage = snp.getSnPPageDocument(GRADES_PAGE_URL + semester); Elements gradesRows = gradesPage.select(".ocenySzczegoly-table > tbody > tr"); - Semester currentSemester = snp.getCurrentSemester(snp.getSemesters(gradesPage)); + + List grades = new ArrayList<>(); 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)); } return grades; } + + private Grade getGrade(Element row) { + String descriptions = row.select("td:nth-child(3)").text(); + + String symbol = descriptions.split(", ")[0]; + String description = descriptions.replaceFirst(Pattern.quote(symbol), "").replaceFirst(", ", ""); + String color = getColor(row.select("td:nth-child(2) span.ocenaCzastkowa").attr("style")); + String date = getFormattedDate(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()); + } + + 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; + } } diff --git a/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java b/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java index decf4468..37902d0d 100644 --- a/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java +++ b/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java @@ -9,19 +9,25 @@ import java.util.ArrayList; import java.util.List; import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; public class SubjectsList { - private static final String SUBJECTS_PAGE_URL = "Oceny/Wszystkie?details=1"; + private static final String SUBJECTS_PAGE_URL = "Oceny/Wszystkie?details=1&okres="; - private SnP snp = null; + private SnP snp; public SubjectsList(SnP snp) { this.snp = snp; } - public List getAll() throws IOException { - Document subjectPage = snp.getSnPPageDocument(SUBJECTS_PAGE_URL); + + public List getAll() throws IOException, VulcanException { + return getAll(""); + } + + public List getAll(String semester) throws IOException, VulcanException { + Document subjectPage = snp.getSnPPageDocument(SUBJECTS_PAGE_URL + semester); Elements rows = subjectPage.select(".ocenyZwykle-table > tbody > tr"); diff --git a/api/src/main/java/io/github/wulkanowy/api/login/AccountPermissionException.java b/api/src/main/java/io/github/wulkanowy/api/login/AccountPermissionException.java index 15be359c..de3901ff 100644 --- a/api/src/main/java/io/github/wulkanowy/api/login/AccountPermissionException.java +++ b/api/src/main/java/io/github/wulkanowy/api/login/AccountPermissionException.java @@ -1,4 +1,10 @@ package io.github.wulkanowy.api.login; -public class AccountPermissionException extends Exception { +import io.github.wulkanowy.api.VulcanException; + +public class AccountPermissionException extends VulcanException { + + AccountPermissionException(String message) { + super(message); + } } diff --git a/api/src/main/java/io/github/wulkanowy/api/login/BadCredentialsException.java b/api/src/main/java/io/github/wulkanowy/api/login/BadCredentialsException.java index 4502bb15..13efd4a1 100644 --- a/api/src/main/java/io/github/wulkanowy/api/login/BadCredentialsException.java +++ b/api/src/main/java/io/github/wulkanowy/api/login/BadCredentialsException.java @@ -1,4 +1,10 @@ package io.github.wulkanowy.api.login; -public class BadCredentialsException extends Exception { +import io.github.wulkanowy.api.VulcanException; + +public class BadCredentialsException extends VulcanException { + + BadCredentialsException(String message) { + super(message); + } } 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 e680804a..443a9387 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 @@ -2,81 +2,125 @@ package io.github.wulkanowy.api.login; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import org.jsoup.parser.Parser; import org.jsoup.select.Elements; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import io.github.wulkanowy.api.Client; +import io.github.wulkanowy.api.NotLoggedInErrorException; +import io.github.wulkanowy.api.VulcanException; public class Login { - private static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn" + - "?ReturnUrl=%2F{symbol}%2FFS%2FLS%3Fwa%3Dwsignin1.0%26wtrealm%3D" + + protected static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn"; + + private static final String LOGIN_PAGE_URL_QUERY = "?ReturnUrl=%2F{symbol}%2FFS%2FLS%3Fwa%3Dwsignin1.0%26wtrealm%3D" + "{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; + private static final Logger logger = LoggerFactory.getLogger(Login.class); public Login(Client client) { this.client = client; } - public String login(String email, String password, String symbol) - throws BadCredentialsException, LoginErrorException, - AccountPermissionException, IOException, VulcanOfflineException { - String certificate = sendCredentials(email, password, symbol); + public void login(String email, String password, String symbol) throws VulcanException, IOException { + Document certDoc = sendCredentials(email, password); - return sendCertificate(certificate, symbol); + if ("Błąd".equals(certDoc.title())) { + client.clearCookies(); + throw new NotLoggedInErrorException(certDoc.body().text()); + } + + sendCertificate(certDoc, symbol); } - String sendCredentials(String email, String password, String symbol) - throws IOException, BadCredentialsException { - this.symbol = symbol; - - Document html = client.postPageByUrl(LOGIN_PAGE_URL, new String[][]{ + Document sendCredentials(String email, String password) throws IOException, VulcanException { + String[][] credentials = new String[][]{ {"LoginName", email}, {"Password", password} - }); + }; - if (null != html.select(".ErrorMessage").first()) { - throw new BadCredentialsException(); + Document nextDoc = sendCredentialsData(credentials, LOGIN_PAGE_URL + LOGIN_PAGE_URL_QUERY.replace(":", "%253A")); + + Element errorMessage = nextDoc.selectFirst(".ErrorMessage, #ErrorTextLabel"); + if (null != errorMessage) { + throw new BadCredentialsException(errorMessage.text()); } - return html.select("input[name=wresult]").attr("value"); + return nextDoc; } - String sendCertificate(String certificate, String defaultSymbol) - throws IOException, LoginErrorException, AccountPermissionException, VulcanOfflineException { - this.symbol = findSymbol(defaultSymbol, certificate); - client.setSymbol(this.symbol); + private Document sendCredentialsData(String[][] credentials, String nextUrl) throws IOException, VulcanException { + Element formFirst = client.getPageByUrl(nextUrl, false).selectFirst("#form1"); - Document html = client.postPageByUrl(LOGIN_ENDPOINT_PAGE_URL, new String[][]{ + if (null != formFirst) { // only on adfs login + Document formSecond = client.postPageByUrl( + formFirst.attr("abs:action"), + getFormStateParams(formFirst, "", "") + ); + credentials = getFormStateParams(formSecond, credentials[0][1], credentials[1][1]); + nextUrl = formSecond.selectFirst("#form1").attr("abs:action"); + } + + return client.postPageByUrl(nextUrl, credentials); + } + + private String[][] getFormStateParams(Element form, String email, String password) { + return new String[][]{ + {"__VIEWSTATE", form.select("#__VIEWSTATE").val()}, + {"__VIEWSTATEGENERATOR", form.select("#__VIEWSTATEGENERATOR").val()}, + {"__EVENTVALIDATION", form.select("#__EVENTVALIDATION").val()}, + {"__db", form.select("input[name=__db]").val()}, + {"PassiveSignInButton.x", "0"}, + {"PassiveSignInButton.y", "0"}, + {"SubmitButton.x", "0"}, + {"SubmitButton.y", "0"}, + {"UsernameTextBox", email}, + {"PasswordTextBox", password}, + }; + } + + void sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException { + client.setSymbol(findSymbol(defaultSymbol, doc.select("input[name=wresult]").val())); + + Document targetDoc = sendCertData(doc); + String title = targetDoc.title(); + + if ("Working...".equals(title)) { // on adfs login + logger.info("ADFS login"); + title = sendCertData(targetDoc).title(); + } + + if ("Logowanie".equals(title)) { + throw new AccountPermissionException("No account access. Try another symbol"); + } + + if (!"Uonet+".equals(title)) { + logger.debug("Login failed. Body: {}", targetDoc.body()); + throw new LoginErrorException("Expected page title `UONET+`, got " + title); + } + + client.setSchools(new StartPage(client).getSchools(targetDoc)); + } + + 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", certificate} + {"wresult", doc.select("input[name=wresult]").val()}, + {"wctx", doc.select("input[name=wctx]").val()} }); - - if (html.getElementsByTag("title").text().equals("Logowanie")) { - throw new AccountPermissionException(); - } - - if (html.getElementsByTag("title").text().equals("Przerwa techniczna")) { - throw new VulcanOfflineException(); - } - - if (!html.select("title").text().equals("Uonet+")) { - throw new LoginErrorException(); - } - - return this.symbol; } - private String findSymbol(String symbol, String certificate) { + private String findSymbol(String symbol, String certificate) throws AccountPermissionException { if ("Default".equals(symbol)) { return findSymbolInCertificate(certificate); } @@ -84,14 +128,19 @@ public class Login { return symbol; } - String findSymbolInCertificate(String certificate) { - Elements els = Jsoup.parse(certificate.replaceAll(":", ""), "", Parser.xmlParser()) + String findSymbolInCertificate(String certificate) throws AccountPermissionException { + Elements instances = Jsoup + .parse(certificate.replaceAll(":", ""), "", Parser.xmlParser()) .select("[AttributeName=\"UserInstance\"] samlAttributeValue"); - if (els.isEmpty()) { + if (instances.isEmpty()) { // on adfs login return ""; } - return els.get(1).text(); + if (instances.size() < 2) { // 1st index is always `Default` + throw new AccountPermissionException("First login detected, specify symbol"); + } + + return instances.get(1).text(); } -} \ No newline at end of file +} diff --git a/api/src/main/java/io/github/wulkanowy/api/login/LoginErrorException.java b/api/src/main/java/io/github/wulkanowy/api/login/LoginErrorException.java index eec2b4e4..be7439df 100644 --- a/api/src/main/java/io/github/wulkanowy/api/login/LoginErrorException.java +++ b/api/src/main/java/io/github/wulkanowy/api/login/LoginErrorException.java @@ -1,4 +1,10 @@ package io.github.wulkanowy.api.login; -public class LoginErrorException extends NotLoggedInErrorException { +import io.github.wulkanowy.api.NotLoggedInErrorException; + +class LoginErrorException extends NotLoggedInErrorException { + + LoginErrorException(String message) { + super(message); + } } diff --git a/api/src/main/java/io/github/wulkanowy/api/login/NotLoggedInErrorException.java b/api/src/main/java/io/github/wulkanowy/api/login/NotLoggedInErrorException.java deleted file mode 100644 index d33b892d..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/login/NotLoggedInErrorException.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.github.wulkanowy.api.login; - -public class NotLoggedInErrorException extends Exception { -} diff --git a/api/src/main/java/io/github/wulkanowy/api/login/StartPage.kt b/api/src/main/java/io/github/wulkanowy/api/login/StartPage.kt new file mode 100644 index 00000000..57463a08 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/StartPage.kt @@ -0,0 +1,46 @@ +package io.github.wulkanowy.api.login + +import io.github.wulkanowy.api.Client +import io.github.wulkanowy.api.VulcanException +import io.github.wulkanowy.api.generic.School +import org.jsoup.nodes.Document +import org.slf4j.LoggerFactory +import java.net.URL + +class StartPage(val client: Client) { + + private val logger = LoggerFactory.getLogger(StartPage::class.java) + + fun getSchools(startPage: Document): MutableList { + val schoolList = mutableListOf() + + val snpLinks = startPage.select(".panel.linkownia.pracownik.klient a[href*=\"uonetplus-opiekun\"]") + + logger.debug("SnP links: {}", snpLinks.size) + + if (snpLinks.isEmpty()) { + throw VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?") + } + + snpLinks.map { + schoolList.add(School( + it.text(), + getExtractedSchoolSymbolFromUrl(it.attr("href")), + it == snpLinks.first() + )) + } + + return schoolList + } + + internal fun getExtractedSchoolSymbolFromUrl(snpPageUrl: String): String { + val path = URL(snpPageUrl).path.split("/") + + if (6 != path.size) { + logger.error("Expected snp url, got {}", snpPageUrl) + throw VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?") + } + + return path[2] + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/login/VulcanOfflineException.java b/api/src/main/java/io/github/wulkanowy/api/login/VulcanOfflineException.java deleted file mode 100644 index 45cf6458..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/login/VulcanOfflineException.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.github.wulkanowy.api.login; - -public class VulcanOfflineException extends Exception { -} diff --git a/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java b/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java index 90af17ce..14dca67b 100644 --- a/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java +++ b/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java @@ -1,4 +1,10 @@ package io.github.wulkanowy.api.messages; -class BadRequestException extends Exception { +import io.github.wulkanowy.api.VulcanException; + +class BadRequestException extends VulcanException { + + BadRequestException(String message) { + super(message); + } } diff --git a/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java b/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java index 4eb969cd..ec2a3fba 100644 --- a/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java +++ b/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java @@ -7,7 +7,8 @@ import java.io.IOException; import java.util.List; import io.github.wulkanowy.api.Client; -import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.api.NotLoggedInErrorException; +import io.github.wulkanowy.api.VulcanException; public class Messages { @@ -37,19 +38,19 @@ public class Messages { this.client = client; } - public List getReceived() throws IOException, NotLoggedInErrorException, BadRequestException { + public List getReceived() throws IOException, VulcanException { return getMessages(RECEIVED_URL); } - public List getSent() throws IOException, NotLoggedInErrorException, BadRequestException { + public List getSent() throws IOException, VulcanException { return getMessages(SENT_URL); } - public List getDeleted() throws IOException, NotLoggedInErrorException, BadRequestException { + public List getDeleted() throws IOException, VulcanException { return getMessages(DELETED_URL); } - private List getMessages(String url) throws IOException, NotLoggedInErrorException, BadRequestException { + private List getMessages(String url) throws IOException, VulcanException { String res = client.getJsonStringByUrl(url); List messages; @@ -58,16 +59,16 @@ public class Messages { messages = new Gson().fromJson(res, MessagesContainer.class).data; } catch (JsonParseException e) { if (res.contains(ERROR_TITLE)) { - throw new BadRequestException(); + throw new BadRequestException(ERROR_TITLE); } - throw new NotLoggedInErrorException(); + throw new NotLoggedInErrorException("You are probably not logged in"); } return messages; } - public Message getMessage(int id, int folder) throws IOException, BadRequestException, NotLoggedInErrorException { + public Message getMessage(int id, int folder) throws IOException, VulcanException { String res = client.postJsonStringByUrl(MESSAGE_URL, new String[][]{ {"idWiadomosc", String.valueOf(id)}, {"Folder", String.valueOf(folder)} @@ -79,10 +80,10 @@ public class Messages { message = new Gson().fromJson(res, MessageContainer.class).data; } catch (JsonParseException e) { if (res.contains(ERROR_TITLE)) { - throw new BadRequestException(); + throw new BadRequestException(ERROR_TITLE); } - throw new NotLoggedInErrorException(); + throw new NotLoggedInErrorException("You are probably not logged in. Force login"); } return message; diff --git a/api/src/main/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt new file mode 100644 index 00000000..f0da01e9 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.api.mobile + +import io.github.wulkanowy.api.SnP +import org.jsoup.nodes.Element + +class RegisterDevice(private val snp: SnP) { + + companion object { + const val REGISTER_URL = "DostepMobilny.mvc/Rejestruj" + } + + data class Token( + val token: String, + val symbol: String, + val pin: String + ) + + fun getToken(): Token { + val form = snp.getSnPPageDocument(REGISTER_URL).selectFirst("#rejestracja-formularz") + + val fields = form.select(".blockElement") + + return Token( + getValue(fields[1]), + getValue(fields[2]), + getValue(fields[3]) + ) + } + + fun getValue(e: Element): String { + return e.text().split(":")[1].trim() + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/mobile/RegisteredDevices.kt b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisteredDevices.kt new file mode 100644 index 00000000..d622b4de --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisteredDevices.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.api.mobile + +import io.github.wulkanowy.api.SnP +import io.github.wulkanowy.api.getFormattedDate + +class RegisteredDevices(private val snp: SnP) { + + companion object { + const val DEVICES_LIST_URL = "DostepMobilny.mvc" + } + + data class Device( + val name: String, + val system: String, + val date: String, + val id: Int + ) + + fun getList(): List { + val items = snp.getSnPPageDocument(DEVICES_LIST_URL).select("table tbody tr") + val devices: MutableList = mutableListOf() + + for (item in items) { + val cells = item.select("td") + val system = cells[0].text().split("(").last().removeSuffix(")") + + devices.add(Device( + cells[0].text().replace(" ($system)", ""), + system, + getFormattedDate(cells[1].text(), "dd.MM.yyyy 'godz:' HH:mm:ss", "yyyy-MM-dd HH:mm:ss"), + cells[2].select("a").attr("href") + .split("/").last().toInt() + )) + } + + return devices + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java b/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java index c821e8be..346df832 100644 --- a/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java +++ b/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java @@ -8,24 +8,25 @@ import java.util.ArrayList; import java.util.List; import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; public class AchievementsList { private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; - private SnP snp = null; - - private List achievements = new ArrayList<>(); + private SnP snp; public AchievementsList(SnP snp) { this.snp = snp; } - public List getAllAchievements() throws IOException { + public List getAllAchievements() throws IOException, VulcanException { Element pageFragment = snp.getSnPPageDocument(NOTES_PAGE_URL) .select(".mainContainer > div").get(1); Elements items = pageFragment.select("article"); + List achievements = new ArrayList<>(); + for (Element item : items) { achievements.add(item.text()); } diff --git a/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java b/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java index 34b8540d..54f57edf 100644 --- a/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java +++ b/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java @@ -8,29 +8,32 @@ import java.util.ArrayList; import java.util.List; import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; public class NotesList { private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; - private SnP snp = null; - - private List notes = new ArrayList<>(); + private SnP snp; public NotesList(SnP snp) { this.snp = snp; } - public List getAllNotes() throws IOException { + public List getAllNotes() throws IOException, VulcanException { Element pageFragment = snp.getSnPPageDocument(NOTES_PAGE_URL) .select(".mainContainer > div").get(0); Elements items = pageFragment.select("article"); Elements dates = pageFragment.select("h2"); + List notes = new ArrayList<>(); + int index = 0; for (Element item : items) { notes.add(new Note() - .setDate(dates.get(index++).text()) + .setDate(getFormattedDate(dates.get(index++).text())) .setTeacher(snp.getRowDataChildValue(item, 1)) .setCategory(snp.getRowDataChildValue(item, 2)) .setContent(snp.getRowDataChildValue(item, 3)) diff --git a/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java b/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java index 4eeb641c..f0dcd43d 100644 --- a/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java +++ b/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java @@ -5,18 +5,19 @@ import org.jsoup.nodes.Element; import java.io.IOException; import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; public class SchoolInfo { private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; - private SnP snp = null; + private SnP snp; public SchoolInfo(SnP snp) { this.snp = snp; } - public SchoolData getSchoolData() throws IOException { + public SchoolData getSchoolData() throws IOException, VulcanException { Element e = snp.getSnPPageDocument(SCHOOL_PAGE_URL) .select(".mainContainer > article").get(0); diff --git a/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java b/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java index e8af6744..ec8429bd 100644 --- a/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java +++ b/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java @@ -9,18 +9,19 @@ import java.util.ArrayList; import java.util.List; import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; public class TeachersInfo { private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; - private SnP snp = null; + private SnP snp; public TeachersInfo(SnP snp) { this.snp = snp; } - public TeachersData getTeachersData() throws IOException { + public TeachersData getTeachersData() throws IOException, VulcanException { Document doc = snp.getSnPPageDocument(SCHOOL_PAGE_URL); Elements rows = doc.select(".mainContainer > table tbody tr"); String description = doc.select(".mainContainer > p").first().text(); diff --git a/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java b/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java index 1d526494..95c40167 100644 --- a/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java +++ b/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java @@ -3,60 +3,64 @@ package io.github.wulkanowy.api.timetable; import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Date; import java.util.List; -import java.util.Locale; import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.generic.Day; +import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.api.generic.Lesson; import io.github.wulkanowy.api.generic.Week; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getDateAsTick; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; + public class Timetable { - private static final String TIMETABLE_PAGE_URL = "Lekcja.mvc/PlanLekcji?data="; + private static final String TIMETABLE_PAGE_URL = "Lekcja.mvc/PlanZajec?data="; private SnP snp; + private static final Logger logger = LoggerFactory.getLogger(Timetable.class); + public Timetable(SnP snp) { this.snp = snp; } - public Week getWeekTable() throws IOException, ParseException { + public Week getWeekTable() throws IOException, VulcanException { return getWeekTable(""); } - public Week getWeekTable(final String tick) throws IOException, ParseException { - Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + tick) + public Week getWeekTable(final String date) throws IOException, VulcanException { + Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + getDateAsTick(date)) .select(".mainContainer .presentData").first(); - List days = getDays(table.select("thead th")); + List days = getDays(table.select("thead th")); setLessonToDays(table, days); - return new Week() + return new Week() .setStartDayDate(days.get(0).getDate()) .setDays(days); } - private List getDays(Elements tableHeaderCells) throws ParseException { - List days = new ArrayList<>(); + private List getDays(Elements tableHeaderCells) { + List days = new ArrayList<>(); + int numberOfDays = tableHeaderCells.size(); - for (int i = 2; i < 7; i++) { + if (numberOfDays > 7) { + logger.info("Number of days: {}", numberOfDays); + } + + for (int i = 2; i < numberOfDays; i++) { String[] dayHeaderCell = tableHeaderCells.get(i).html().split("
"); - SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT); - Date d = sdf.parse(dayHeaderCell[1].trim()); - sdf.applyPattern("yyyy-MM-dd"); - - Day day = new Day(); + TimetableDay day = new TimetableDay(); day.setDayName(dayHeaderCell[0]); - day.setDate(sdf.format(d)); + day.setDate(getFormattedDate(dayHeaderCell[1].trim())); if (tableHeaderCells.get(i).hasClass("free-day")) { day.setFreeDay(true); @@ -69,7 +73,7 @@ public class Timetable { return days; } - private void setLessonToDays(Element table, List days) { + private void setLessonToDays(Element table, List days) { for (Element row : table.select("tbody tr")) { Elements hours = row.select("td"); @@ -81,7 +85,7 @@ public class Timetable { lesson.setStartTime(startEndEnd[0]); lesson.setEndTime(startEndEnd[1]); lesson.setDate(days.get(i - 2).getDate()); - lesson.setNumber(hours.get(0).text()); + lesson.setNumber(Integer.valueOf(hours.get(0).text())); addLessonDetails(lesson, hours.get(i).select("div")); @@ -98,7 +102,16 @@ public class Timetable { addLessonInfoFromElement(lesson, e.first()); break; case 2: - addLessonInfoFromElement(lesson, e.last()); + Element span = e.last().selectFirst("span"); + if (null == span) { + addLessonInfoFromElement(lesson, e.first()); + } else if (span.hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) { + lesson.setNewMovedInOrChanged(true); + lesson.setDescription("poprzednio: " + getLessonAndGroupInfoFromSpan(span)[0]); + addLessonInfoFromElement(lesson, e.first()); + } else { + addLessonInfoFromElement(lesson, e.last()); + } break; case 3: addLessonInfoFromElement(lesson, e.get(1)); @@ -113,7 +126,9 @@ public class Timetable { Elements warn = e.select(".uwaga-panel"); if (!warn.isEmpty()) { - e.select(".x-treelabel-rlz").last().text("(" + warn.text() + ")"); + e.select("span").last() + .addClass("x-treelabel-rlz") + .text(warn.text()); e.remove(1); } } @@ -121,6 +136,11 @@ public class Timetable { private void addLessonInfoFromElement(Lesson lesson, Element e) { Elements spans = e.select("span"); + if (spans.isEmpty()) { + logger.warn("Lesson span is empty"); + return; + } + addTypeInfo(lesson, spans); addNormalLessonInfo(lesson, spans); addChangesInfo(lesson, spans); @@ -164,7 +184,8 @@ public class Timetable { lesson.setRoom(spans.get(5).text()); lesson.setMovedOrCanceled(false); lesson.setNewMovedInOrChanged(true); - lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")") + lesson.setDescription(StringUtils.defaultString(StringUtils.substringBetween( + spans.last().text(), "(", ")"), spans.last().text()) + " (poprzednio: " + spans.get(0).text() + ")"); } else if (9 == spans.size()) { String[] subjectAndGroupInfo = getLessonAndGroupInfoFromSpan(spans.get(4)); @@ -175,13 +196,15 @@ public class Timetable { lesson.setMovedOrCanceled(false); lesson.setNewMovedInOrChanged(true); lesson.setDivisionIntoGroups(true); - lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")") + lesson.setDescription(StringUtils.defaultString(StringUtils.substringBetween( + spans.last().text(), "(", ")"), spans.last().text()) + " (poprzednio: " + getLessonAndGroupInfoFromSpan(spans.get(0))[0] + ")"); } else if (4 <= spans.size()) { lesson.setSubject(spans.get(0).text()); lesson.setTeacher(spans.get(1).text()); lesson.setRoom(spans.get(2).text()); - lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")")); + lesson.setDescription(StringUtils.defaultString(StringUtils.substringBetween( + spans.last().text(), "(", ")"), spans.last().text())); } } @@ -204,12 +227,17 @@ public class Timetable { } private String[] getLessonAndGroupInfoFromSpan(Element span) { + if (!span.text().contains("[")) { + return new String[] {span.text(), ""}; + } + String[] subjectNameArray = span.text().split(" "); String groupName = subjectNameArray[subjectNameArray.length - 1]; return new String[]{ span.text().replace(" " + groupName, ""), - StringUtils.substringBetween(groupName, "[", "]") + StringUtils.defaultString(StringUtils.substringBetween( + groupName, "[", "]"), groupName) }; } } diff --git a/api/src/main/java/io/github/wulkanowy/api/timetable/TimetableDay.java b/api/src/main/java/io/github/wulkanowy/api/timetable/TimetableDay.java new file mode 100644 index 00000000..1aa29de2 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/timetable/TimetableDay.java @@ -0,0 +1,26 @@ +package io.github.wulkanowy.api.timetable; + +import io.github.wulkanowy.api.generic.Day; + +public class TimetableDay extends Day { + + private boolean isFreeDay = false; + + private String freeDayName = ""; + + public boolean isFreeDay() { + return isFreeDay; + } + + public void setFreeDay(boolean freeDay) { + isFreeDay = freeDay; + } + + public String getFreeDayName() { + return freeDayName; + } + + public void setFreeDayName(String freeDayName) { + this.freeDayName = freeDayName; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java b/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java index 875cc8da..4edc5117 100644 --- a/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java +++ b/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java @@ -6,6 +6,7 @@ import org.jsoup.nodes.Element; import java.io.IOException; import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; public class BasicInformation { @@ -21,7 +22,7 @@ public class BasicInformation { this.snp = snp; } - public Document getStudentDataPageDocument() throws IOException { + public Document getStudentDataPageDocument() throws IOException, VulcanException { if (null == studentDataPageDocument) { studentDataPageDocument = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL); } @@ -29,7 +30,7 @@ public class BasicInformation { return studentDataPageDocument; } - public PersonalData getPersonalData() throws IOException { + public PersonalData getPersonalData() throws IOException, VulcanException { Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(0); String name = snp.getRowDataChildValue(e, 1); @@ -48,7 +49,7 @@ public class BasicInformation { .setParentsNames(snp.getRowDataChildValue(e, 7)); } - public AddressData getAddressData() throws IOException { + public AddressData getAddressData() throws IOException, VulcanException { Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(1); return new AddressData() @@ -58,7 +59,7 @@ public class BasicInformation { } - public ContactDetails getContactDetails() throws IOException { + public ContactDetails getContactDetails() throws IOException, VulcanException { Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(2); return new ContactDetails() diff --git a/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java b/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java index 555de866..f5a459ee 100644 --- a/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java +++ b/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.List; import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; public class FamilyInformation { @@ -19,7 +20,7 @@ public class FamilyInformation { this.snp = snp; } - public List getFamilyMembers() throws IOException { + public List getFamilyMembers() throws IOException, VulcanException { Elements membersElements = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL) .select(".mainContainer > article:nth-of-type(n+4)"); diff --git a/api/src/test/java/io/github/wulkanowy/api/ClientTest.java b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java new file mode 100644 index 00000000..298affac --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java @@ -0,0 +1,78 @@ +package io.github.wulkanowy.api; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.junit.Assert; +import org.junit.Test; + +public class ClientTest { + + private String getFixtureAsString(String fixtureFileName) { + return FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName)); + } + + @Test + public void setFullEndpointInfoTest() { + Client client = new Client("http://fakelog.cf\\\\admin", "pass", "Default", "123"); + + Assert.assertEquals("fakelog.cf", client.getHost()); + Assert.assertEquals("Default", client.getSymbol()); + } + + @Test + public void setFullEndpointInfoWithSymbolTest() { + Client client = new Client("http://fakelog.cf/notdefault\\\\admin", "pass", "Default", "123"); + + Assert.assertEquals("fakelog.cf", client.getHost()); + Assert.assertEquals("notdefault", client.getSymbol()); // + } + + @Test + public void checkForNoErrorsTest() throws Exception { + Client client = new Client("", "", "", "123"); + + Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-success.html")); + + Assert.assertEquals(doc, client.checkForErrors(doc, 200)); + } + + @Test(expected = VulcanOfflineException.class) + public void checkForErrorsOffline() throws Exception { + Client client = new Client("", "", "", "123"); + + Document doc = Jsoup.parse(getFixtureAsString("login/PrzerwaTechniczna.html")); + + client.checkForErrors(doc, 200); + } + + @Test(expected = NotLoggedInErrorException.class) + public void checkForErrors() throws Exception { + Client client = new Client("", "", "", "123"); + + Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-notLoggedIn.html")); + + client.checkForErrors(doc, 200); + } + + @Test + public void getFilledUrlTest() throws Exception { + Client client = new Client("http://fakelog.cf\\\\admin", "", "symbol123", "321"); + + Assert.assertEquals("http://uonetplus-opiekun.fakelog.cf/symbol123/321/Oceny/Wszystkie", + client.getFilledUrl("{schema}://uonetplus-opiekun.{host}/{symbol}/{ID}/Oceny/Wszystkie")); + } + + @Test + public void getSymbolTest() { + Client client = new Client("", "", "symbol4321", "123"); + + Assert.assertEquals("symbol4321", client.getSymbol()); + } + + @Test + public void getSchoolIdTest() throws Exception { + Client client = new Client("", "", "1", "123456"); + + Assert.assertEquals("123456", client.getSchoolId()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/DateTimeUtilsTest.kt b/api/src/test/java/io/github/wulkanowy/api/DateTimeUtilsTest.kt new file mode 100644 index 00000000..1105b2a7 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/DateTimeUtilsTest.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.api + +import org.junit.Assert +import org.junit.Test +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.* + +class DateTimeUtilsTest { + + @Test + fun getTicksDateObjectTest() { + val format = SimpleDateFormat("dd.MM.yyyy", Locale.ROOT) + format.timeZone = TimeZone.getTimeZone("UTC") + val date = format.parse("31.07.2017") + + Assert.assertEquals(636370560000000000L, getDateAsTick(date)) + + val calendar = Calendar.getInstance() + calendar.time = date + calendar.add(Calendar.DAY_OF_YEAR, -14) + val dateTwoWeekBefore = calendar.time + + Assert.assertEquals(636358464000000000L, getDateAsTick(dateTwoWeekBefore)) + } + + @Test(expected = ParseException::class) + fun getTicsStringInvalidFormatTest() { + Assert.assertEquals(636370560000000000L, getDateAsTick("31.07.2017", "dd.MMM.yyyy")) + } + + @Test + fun getTicsStringFormatTest() { + Assert.assertEquals(636370560000000000L, getDateAsTick("31.07.2017", "dd.MM.yyyy")) + } + + @Test + fun getTicsStringTest() { + Assert.assertEquals("636370560000000000", getDateAsTick("2017-07-31")) + Assert.assertEquals("636334272000000000", getDateAsTick("2017-06-19")) + Assert.assertEquals("636189120000000000", getDateAsTick("2017-01-02")) + Assert.assertEquals("636080256000000000", getDateAsTick("2016-08-29")) + } + + @Test + fun getDateTest() { + val format = SimpleDateFormat("dd.MM.yyyy", Locale.ROOT) + format.timeZone = TimeZone.getTimeZone("UTC") + val date = format.parse("31.07.2017") + + Assert.assertEquals(date, getDate(636370560000000000L)) + } +} 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 89a5b190..4c6443b1 100644 --- a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java @@ -7,10 +7,11 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; +import java.io.IOException; import java.util.ArrayList; import java.util.List; -import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.api.generic.Semester; public class StudentAndParentTest { @@ -24,102 +25,79 @@ 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()); - } - - @Test - public void getSnpPageUrlWithIdTest() throws Exception { - Assert.assertEquals("{schema}://uonetplus-opiekun.{host}/{symbol}/123456/", - (new StudentAndParent(client, "123456")).getSnpHomePageUrl()); - } - - @Test - public void getSnpPageUrlWithoutIdTest() throws Exception { - String input = FixtureHelper.getAsString(getClass().getResourceAsStream("Start.html")); - Document startPageDocument = Jsoup.parse(input); - - Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); - Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(startPageDocument); - StudentAndParent snp = new StudentAndParent(client); - - Assert.assertEquals("https://uonetplus-opiekun.vulcan.net.pl/symbol/534213/Start/Index/", - snp.getSnpHomePageUrl()); - } - - @Test(expected = NotLoggedInErrorException.class) - public void getSnpPageUrlWithWrongPage() throws Exception { - Document wrongPageDocument = Jsoup.parse( - FixtureHelper.getAsString(getClass().getResourceAsStream("OcenyWszystkie-semester.html")) - ); - - Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(wrongPageDocument); - StudentAndParent snp = new StudentAndParent(client); - - snp.getSnpHomePageUrl(); - } - - @Test - public void getExtractedIDStandardTest() throws Exception { - Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); - StudentAndParent snp = new StudentAndParent(client, "symbol"); - Assert.assertEquals("123456", snp.getExtractedIdFromUrl("https://uonetplus-opiekun" - + ".vulcan.net.pl/powiat/123456/Start/Index/")); - } - - @Test - public void getExtractedIDDemoTest() throws Exception { - Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); - StudentAndParent snp = new StudentAndParent(client, "symbol"); - Assert.assertEquals("demo12345", - snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/")); - } - - @Test(expected = NotLoggedInErrorException.class) - public void getExtractedIDNotLoggedTest() throws Exception { - Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); - StudentAndParent snp = new StudentAndParent(client, "symbol"); - Assert.assertEquals("123", - snp.getExtractedIdFromUrl("https://uonetplus.vulcan.net.pl/powiat/")); + public void snpTest() { + StudentAndParent snp = new StudentAndParent(client, "1234", null); + Assert.assertEquals("1234", snp.getStudentID()); } @Test public void getSemestersTest() throws Exception { - SnP snp = new StudentAndParent(client, "123456"); + SnP snp = new StudentAndParent(client, 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("1", semesters.get(0).getName()); + Assert.assertEquals("1234", semesters.get(0).getId()); Assert.assertFalse(semesters.get(0).isCurrent()); - Assert.assertEquals("2", semesters.get(1).getId()); - Assert.assertEquals("1235", semesters.get(1).getNumber()); + Assert.assertEquals("2", semesters.get(1).getName()); + Assert.assertEquals("1235", semesters.get(1).getId()); 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 { + String input = FixtureHelper.getAsString(getClass().getResourceAsStream("WitrynaUczniaIRodzica.html")); + Document snpHome = Jsoup.parse(input); + + 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("Jan Kowal", snp.getStudents().get(0).getName()); + Assert.assertEquals("100", snp.getStudents().get(0).getId()); } } 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..764e8009 100644 --- a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java +++ b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java @@ -5,6 +5,8 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.mockito.Mockito; +import io.github.wulkanowy.api.generic.Semester; + public abstract class StudentAndParentTestCase { protected StudentAndParent getSnp(String fixtureFileName) throws Exception { @@ -16,7 +18,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 a80c798a..287935c0 100644 --- a/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java @@ -1,86 +1,31 @@ package io.github.wulkanowy.api; +import org.hamcrest.CoreMatchers; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; - -import java.util.HashMap; -import java.util.Map; - -import io.github.wulkanowy.api.login.Login; -import io.github.wulkanowy.api.login.NotLoggedInErrorException; public class VulcanTest { - private Vulcan vulcan; + @Test(expected = NotLoggedInErrorException.class) + public void getClientWithoutLoginTest() throws Exception { + Vulcan vulcan = new Vulcan(); - @Before - public void setUp() throws Exception { - vulcan = new Vulcan(); - vulcan.setClient(Mockito.mock(Client.class)); - vulcan.setLogin(Mockito.mock(Login.class)); + vulcan.getClient(); } @Test - public void setFullEndpointInfoTest() throws Exception { - vulcan.login("http://fakelog.net\\\\admin", "pass", "Default", "123"); + public void getClientTest() throws Exception { + Vulcan vulcan = new Vulcan(); + vulcan.setCredentials("email", "password", "symbol", null, null, null); - Assert.assertEquals("http", vulcan.getProtocolSchema()); - Assert.assertEquals("fakelog.net", vulcan.getLogHost()); - Assert.assertEquals("admin", vulcan.getEmail()); + Assert.assertThat(vulcan.getClient(), CoreMatchers.instanceOf(Client.class)); } @Test public void getClientTwiceTest() throws Exception { Vulcan vulcan = new Vulcan(); - Assert.assertTrue(vulcan.getClient().equals(vulcan.getClient())); - } + vulcan.setCredentials("email", "password", "symbol", null, null, null); - @Test - public void getLoginTwiceTest() throws Exception { - Vulcan vulcan = new Vulcan(); - Assert.assertTrue(vulcan.getLogin().equals(vulcan.getLogin())); - } - - @Test(expected = NotLoggedInErrorException.class) - public void getStudentAndParentNotLoggedInTest() throws Exception { - vulcan.getStudentAndParent(); - } - - @Test - public void getStudentAndParentTwiceTest() throws Exception { - Client client = Mockito.mock(Client.class); - Map cookies = new HashMap<>(); - cookies.put("test", "test"); - Mockito.when(client.getCookies()).thenReturn(cookies); - - SnP snp = Mockito.mock(StudentAndParent.class); - Mockito.doNothing().when(snp).storeContextCookies(); - - Vulcan vulcan = Mockito.mock(Vulcan.class); - Mockito.when(vulcan.getClient()).thenReturn(client); - Mockito.when(vulcan.getStudentAndParent()).thenCallRealMethod(); - Mockito.when(vulcan.createSnp(Mockito.any(Client.class), Mockito.any())).thenReturn(snp); - - vulcan.getStudentAndParent(); - vulcan.getStudentAndParent(); - } - - @Test - public void createSnPTest() throws Exception { - vulcan.login("wulkanowy@wulkanowy.io", "wulkanowy123", "wulkan"); - - SnP snp1 = vulcan.createSnp(Mockito.mock(Client.class), null); - Assert.assertEquals(null, snp1.getId()); - - SnP snp2 = vulcan.createSnp(Mockito.mock(Client.class), "wulkan"); - Assert.assertEquals("wulkan", snp2.getId()); - - } - - @Test(expected = NotLoggedInErrorException.class) - public void getAttendanceExceptionText() throws Exception { - vulcan.getAttendanceTable(); + Assert.assertEquals(vulcan.getClient(), vulcan.getClient()); } } diff --git a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java index 1420a475..40ff4f06 100644 --- a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java @@ -20,8 +20,8 @@ public class AttendanceTableTest extends StudentAndParentTestCase { @Test public void getWeekStartByDate() throws Exception { - Assert.assertEquals("31.08.2015", excellent.getWeekTable().getStartDayDate()); - Assert.assertEquals("05.09.2016", full.getWeekTable().getStartDayDate()); + Assert.assertEquals("2015-08-31", excellent.getWeekTable().getStartDayDate()); + Assert.assertEquals("2016-09-05", full.getWeekTable().getStartDayDate()); } @Test @@ -38,13 +38,13 @@ public class AttendanceTableTest extends StudentAndParentTestCase { @Test public void getDayDate() throws Exception { - Assert.assertEquals("31.08.2015", excellent.getWeekTable().getDay(0).getDate()); - Assert.assertEquals("02.09.2015", excellent.getWeekTable().getDay(2).getDate()); - Assert.assertEquals("04.09.2015", excellent.getWeekTable().getDay(4).getDate()); + Assert.assertEquals("2015-08-31", excellent.getWeekTable().getDay(0).getDate()); + Assert.assertEquals("2015-09-02", excellent.getWeekTable().getDay(2).getDate()); + Assert.assertEquals("2015-09-04", excellent.getWeekTable().getDay(4).getDate()); - Assert.assertEquals("05.09.2016", full.getWeekTable().getDay(0).getDate()); - Assert.assertEquals("07.09.2016", full.getWeekTable().getDay(2).getDate()); - Assert.assertEquals("09.09.2016", full.getWeekTable().getDay(4).getDate()); + Assert.assertEquals("2016-09-05", full.getWeekTable().getDay(0).getDate()); + Assert.assertEquals("2016-09-07", full.getWeekTable().getDay(2).getDate()); + Assert.assertEquals("2016-09-09", full.getWeekTable().getDay(4).getDate()); } @Test diff --git a/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java b/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java index 9b57ddd6..0a99c465 100644 --- a/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java @@ -12,20 +12,25 @@ public class ExamsWeekTest extends StudentAndParentTestCase { private ExamsWeek onePerDay; + private ExamsWeek empty; + @Before public void getCurrent() throws Exception { onePerDay = new ExamsWeek(getSnp("Sprawdziany-one-per-day.html")); + empty = new ExamsWeek(getSnp("Sprawdziany-empty.html")); } @Test public void getWeekTest() throws Exception { - Assert.assertEquals("23.10.2017", onePerDay.getCurrent().getStartDayDate()); + Assert.assertEquals("2017-10-23", onePerDay.getCurrent().getStartDayDate()); + Assert.assertEquals("2018-04-30", empty.getCurrent().getStartDayDate()); } @Test public void getDaysListTest() throws Exception { Assert.assertEquals(3, onePerDay.getCurrent().getDays().size()); Assert.assertEquals(7, onePerDay.getWeek("", false).getDays().size()); + Assert.assertEquals(0, empty.getCurrent().getDays().size()); } @Test @@ -45,9 +50,18 @@ public class ExamsWeekTest extends StudentAndParentTestCase { public void getDayDateTest() throws Exception { List dayList = onePerDay.getCurrent().getDays(); - Assert.assertEquals("23.10.2017", dayList.get(0).getDate()); - Assert.assertEquals("24.10.2017", dayList.get(1).getDate()); - Assert.assertEquals("27.10.2017", dayList.get(2).getDate()); + Assert.assertEquals("2017-10-23", dayList.get(0).getDate()); + Assert.assertEquals("2017-10-24", dayList.get(1).getDate()); + Assert.assertEquals("2017-10-27", dayList.get(2).getDate()); + } + + @Test + public void getDayNameTest() throws Exception { + List dayList = onePerDay.getCurrent().getDays(); + + Assert.assertEquals("Poniedziałek", dayList.get(0).getDayName()); + Assert.assertEquals("Wtorek", dayList.get(1).getDayName()); + Assert.assertEquals("Piątek", dayList.get(2).getDayName()); } @Test @@ -90,8 +104,8 @@ public class ExamsWeekTest extends StudentAndParentTestCase { public void getExamEntryDateTest() throws Exception { List dayList = onePerDay.getCurrent().getDays(); - Assert.assertEquals("16.10.2017", dayList.get(0).getExamList().get(0).getEntryDate()); - Assert.assertEquals("17.10.2017", dayList.get(1).getExamList().get(0).getEntryDate()); - Assert.assertEquals("16.10.2017", dayList.get(2).getExamList().get(0).getEntryDate()); + Assert.assertEquals("2017-10-16", dayList.get(0).getExamList().get(0).getEntryDate()); + Assert.assertEquals("2017-10-17", dayList.get(1).getExamList().get(0).getEntryDate()); + Assert.assertEquals("2017-10-16", dayList.get(2).getExamList().get(0).getEntryDate()); } } diff --git a/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java b/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java index bee5b4fe..deab7617 100644 --- a/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java @@ -19,12 +19,12 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getAllTest() throws Exception { - Assert.assertEquals(6, filled.getAll().size()); // 2 items are skipped + Assert.assertEquals(7, filled.getAll("").size()); // 2 items are skipped } @Test public void getSubjectTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("Zajęcia z wychowawcą", list.get(0).getSubject()); Assert.assertEquals("Język angielski", list.get(3).getSubject()); @@ -34,7 +34,7 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getValueTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("5", list.get(0).getValue()); Assert.assertEquals("5", list.get(3).getValue()); @@ -44,7 +44,7 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getColorTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("000000", list.get(0).getColor()); Assert.assertEquals("1289F7", list.get(3).getColor()); @@ -54,27 +54,29 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getSymbolTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("A1", list.get(0).getSymbol()); Assert.assertEquals("BW3", list.get(3).getSymbol()); Assert.assertEquals("STR", list.get(4).getSymbol()); Assert.assertEquals("K", list.get(5).getSymbol()); + Assert.assertEquals("+Odp", list.get(6).getSymbol()); } @Test public void getDescriptionTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("Dzień Kobiet w naszej klasie", list.get(0).getDescription()); Assert.assertEquals("Writing", list.get(3).getDescription()); Assert.assertEquals("", list.get(4).getDescription()); Assert.assertEquals("Kordian", list.get(5).getDescription()); + Assert.assertEquals("Kordian", list.get(6).getDescription()); } @Test public void getWeightTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("1,00", list.get(0).getWeight()); Assert.assertEquals("3,00", list.get(3).getWeight()); @@ -84,7 +86,7 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getDateTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("2017-03-21", list.get(0).getDate()); Assert.assertEquals("2017-06-02", list.get(3).getDate()); @@ -94,21 +96,11 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getTeacherTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("Patryk Maciejewski", list.get(0).getTeacher()); Assert.assertEquals("Oliwia Woźniak", list.get(3).getTeacher()); Assert.assertEquals("Klaudia Dziedzic", list.get(4).getTeacher()); Assert.assertEquals("Amelia Stępień", list.get(5).getTeacher()); } - - @Test - public void getSemesterTest() throws Exception { - List list = filled.getAll(); - - Assert.assertEquals("7654321", list.get(0).getSemester()); - Assert.assertEquals("7654321", list.get(3).getSemester()); - Assert.assertEquals("7654321", list.get(4).getSemester()); - Assert.assertEquals("7654321", list.get(5).getSemester()); - } } 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 335ab84b..86a3ef40 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 @@ -1,6 +1,5 @@ package io.github.wulkanowy.api.login; - import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.junit.Assert; @@ -12,93 +11,121 @@ 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); + Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(doc); return client; } @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")); + Mockito.when(client.postPageByUrl(Mockito.eq(Login.LOGIN_PAGE_URL), Mockito.any(String[][].class))) + .thenReturn(getFixtureAsDocument("Logowanie-certyfikat.html")); + Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString()); + Mockito.when(client.getSymbol()).thenCallRealMethod(); + Mockito.when(client.getHost()).thenReturn("fakelog.cf"); + Login login = new Login(client); + login.login("a@a", "pswd", "d123"); - Assert.assertEquals("d123", login.login("a@a", "pswd", "d123")); + Assert.assertEquals("d123", client.getSymbol()); } @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+","") + getFixtureAsString("cert-stock.xml").replaceAll("\\s+", ""), + login.sendCredentials("a@a", "passwd") + .select("input[name=wresult]") + .attr("value") + .replaceAll("\\s+", "") ); } @Test public void sendCertificateNotDefaultSymbolSuccessTest() throws Exception { - Login login = new Login(getClient("Logowanie-success.html")); + Client client = getClient("Logowanie-success.html"); + Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString()); + Mockito.when(client.getSymbol()).thenCallRealMethod(); + Mockito.when(client.getHost()).thenReturn("fakelog.cf"); + Login login = new Login(client); - Assert.assertEquals("wulkanowyschool321", - login.sendCertificate("", "wulkanowyschool321")); + login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "wulkanowyschool321"); + + Assert.assertEquals("wulkanowyschool321", client.getSymbol()); } @Test public void sendCertificateDefaultSymbolSuccessTest() throws Exception { - Login login = new Login(getClient("Logowanie-success.html")); + Client client = getClient("Logowanie-success.html"); + Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString()); + Mockito.when(client.getSymbol()).thenCallRealMethod(); + Mockito.when(client.getHost()).thenReturn("fakelog.cf"); + Login login = new Login(client); - Assert.assertEquals("demo12345", - login.sendCertificate(getFixtureAsString("cert.xml"), "Default")); + login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "Default"); + + Assert.assertEquals("demo12345", client.getSymbol()); } @Test(expected = AccountPermissionException.class) public void sendCertificateAccountPermissionTest() throws Exception { - Login login = new Login(getClient("Logowanie-brak-dostepu.html")); + Client client = getClient("Logowanie-brak-dostepu.html"); - login.sendCertificate(getFixtureAsString("cert.xml"), "demo123"); + Login login = new Login(client); + + login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "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"); - } - - @Test(expected = VulcanOfflineException.class) - public void sendCertificateVulcanOfflineTest() throws Exception { - Login login = new Login(getClient("PrzerwaTechniczna.html")); - - login.sendCertificate(getFixtureAsString("cert.xml"), "demo123"); + login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "demo123"); } @Test public void findSymbolInCertificateTest() throws Exception { Login login = new Login(getClient("Logowanie-certyfikat.html")); - String certificate = getFixtureAsString("cert.xml"); + String certificate = getFixtureAsString("cert-stock.xml"); Assert.assertEquals("demo12345", login.findSymbolInCertificate(certificate)); } - @Test - public void findSymbolInInvalidCertificateTest() throws Exception { + @Test(expected = AccountPermissionException.class) + public void findSymbolInCertificateWithoutSecondInstanceTest() throws Exception { Login login = new Login(getClient("Logowanie-certyfikat.html")); - Assert.assertEquals("", login.findSymbolInCertificate("")); // change to real cert with empty symbols + login.findSymbolInCertificate(getFixtureAsString("cert-no-symbols.xml")); } } diff --git a/api/src/test/java/io/github/wulkanowy/api/login/StartPageTest.kt b/api/src/test/java/io/github/wulkanowy/api/login/StartPageTest.kt new file mode 100644 index 00000000..fb2bf00c --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/login/StartPageTest.kt @@ -0,0 +1,68 @@ +package io.github.wulkanowy.api.login + +import io.github.wulkanowy.api.Client +import io.github.wulkanowy.api.FixtureHelper +import io.github.wulkanowy.api.VulcanException +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito +import org.mockito.Mockito.mock + +class StartPageTest { + + private val client: Client = mock(Client::class.java) + + @Before fun setUp() { + Mockito.`when`(client.host).thenReturn("fakelog.cf") + } + + private fun getDoc(name: String): Document = Jsoup.parse(FixtureHelper.getAsString(javaClass.getResourceAsStream(name))) + + @Test fun getSchoolTest() { + assertEquals("534213", StartPage(client).getSchools(getDoc("../Start-std.html"))[0].id) + } + + @Test fun getMultiSchoolTest() { + val schools = StartPage(client).getSchools(getDoc("../Start-multi.html")) + + assertEquals("123456", schools[0].id) + assertEquals("123457", schools[1].id) + } + + @Test fun getSchoolNameTest() { + assertEquals("Uczeń", StartPage(client).getSchools(getDoc("../Start-std.html"))[0].name) + } + + @Test fun getMultiSchoolNameTest() { + val schools = StartPage(client).getSchools(getDoc("../Start-multi.html")) + + assertEquals("GIMBB", schools[0].name) + assertEquals("SPBB", schools[1].name) + } + + @Test(expected = VulcanException::class) + fun getSnpPageUrlWithWrongPage() { + StartPage(client).getSchools(getDoc("../OcenyWszystkie-semester.html")) + } + + @Test + fun getExtractedIDStandardTest() { + assertEquals("123456", StartPage(client) + .getExtractedSchoolSymbolFromUrl("https://uonetplus-opiekun.fakelog.cf/powiat/123456/Start/Index/")) + } + + @Test + fun getExtractedIDDemoTest() { + assertEquals("demo12345", StartPage(client) + .getExtractedSchoolSymbolFromUrl("https://uonetplus-opiekun.fakelog.cf/demoupowiat/demo12345/Start/Index/")) + } + + @Test(expected = VulcanException::class) + fun getExtractedIDNotLoggedTest() { + assertEquals("123", StartPage(client) + .getExtractedSchoolSymbolFromUrl("https://uonetplus.NOTfakelog.cf/powiat/")) + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/messages/MessagesTest.java b/api/src/test/java/io/github/wulkanowy/api/messages/MessagesTest.java index 44880a88..14fa4627 100644 --- a/api/src/test/java/io/github/wulkanowy/api/messages/MessagesTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/messages/MessagesTest.java @@ -8,7 +8,7 @@ import java.util.List; import io.github.wulkanowy.api.Client; import io.github.wulkanowy.api.FixtureHelper; -import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.api.NotLoggedInErrorException; public class MessagesTest { diff --git a/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt new file mode 100644 index 00000000..f67310c8 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.api.mobile + +import io.github.wulkanowy.api.StudentAndParentTestCase +import org.junit.Assert.assertEquals +import org.junit.Test + +class RegisterDeviceTest : StudentAndParentTestCase() { + + @Test + fun getTokenTest() { + val registration = RegisterDevice(getSnp("Rejestruj.html")) + + assertEquals("3S1A1B2C", registration.getToken().token) + assertEquals("Default", registration.getToken().symbol) + assertEquals("1234567", registration.getToken().pin) + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/mobile/RegisteredDevicesListTest.kt b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisteredDevicesListTest.kt new file mode 100644 index 00000000..3cd8b97c --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisteredDevicesListTest.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.api.mobile + +import io.github.wulkanowy.api.StudentAndParentTestCase +import org.junit.Assert.assertEquals +import org.junit.Test + +class RegisteredDevicesListTest : StudentAndParentTestCase() { + + private val filled = RegisteredDevices(getSnp("DostepMobilny-filled.html")) + + @Test + fun getListTest() { + assertEquals(2, filled.getList().size) + } + + @Test + fun getNameTest() { + assertEquals("google Android SDK built for x86", filled.getList()[0].name) + assertEquals("google (Android SDK) built for x86", filled.getList()[1].name) + } + + @Test + fun getSystemTest() { + assertEquals("Android 8.1.0", filled.getList()[0].system) + assertEquals("Android 8.1.0", filled.getList()[1].system) + } + + @Test + fun getDateTest() { + assertEquals("2018-01-20 22:35:30", filled.getList()[0].date) + } + + @Test + fun getIdTest() { + assertEquals(321, filled.getList()[0].id) + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java b/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java index d76c0648..304eb01a 100644 --- a/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java @@ -30,8 +30,8 @@ public class NotesListTest extends StudentAndParentTestCase { public void getDateTest() throws Exception { List filledList = filled.getAllNotes(); - Assert.assertEquals("06.06.2017", filledList.get(0).getDate()); - Assert.assertEquals("01.10.2016", filledList.get(2).getDate()); + Assert.assertEquals("2017-06-06", filledList.get(0).getDate()); + Assert.assertEquals("2016-10-01", filledList.get(2).getDate()); } @Test diff --git a/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java b/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java index 65267634..7b3cce6e 100644 --- a/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java @@ -83,11 +83,11 @@ public class TimetableTest extends StudentAndParentTestCase { @Test public void getLessonNumberTest() throws Exception { - Assert.assertEquals("2", std.getWeekTable().getDay(0).getLesson(1).getNumber()); - Assert.assertEquals("5", std.getWeekTable().getDay(2).getLesson(4).getNumber()); - Assert.assertEquals("0", full.getWeekTable().getDay(0).getLesson(0).getNumber()); - Assert.assertEquals("13", full.getWeekTable().getDay(4).getLesson(13).getNumber()); - Assert.assertEquals("3", holidays.getWeekTable().getDay(3).getLesson(3).getNumber()); + Assert.assertEquals(2, std.getWeekTable().getDay(0).getLesson(1).getNumber()); + Assert.assertEquals(5, std.getWeekTable().getDay(2).getLesson(4).getNumber()); + Assert.assertEquals(0, full.getWeekTable().getDay(0).getLesson(0).getNumber()); + Assert.assertEquals(13, full.getWeekTable().getDay(4).getLesson(13).getNumber()); + Assert.assertEquals(3, holidays.getWeekTable().getDay(3).getLesson(3).getNumber()); } @Test @@ -105,11 +105,13 @@ public class TimetableTest extends StudentAndParentTestCase { Assert.assertEquals("Zajęcia techniczne", std.getWeekTable().getDay(2).getLesson(4).getSubject()); Assert.assertEquals("Wychowanie fizyczne", std.getWeekTable().getDay(1).getLesson(1).getSubject()); Assert.assertEquals("Język angielski", full.getWeekTable().getDay(0).getLesson(1).getSubject()); + Assert.assertEquals("Wychowanie fizyczne", full.getWeekTable().getDay(0).getLesson(9).getSubject()); Assert.assertEquals("Wychowanie do życia w rodzinie", full.getWeekTable().getDay(2).getLesson(0).getSubject()); Assert.assertEquals("Wychowanie fizyczne", full.getWeekTable().getDay(3).getLesson(1).getSubject()); Assert.assertEquals("Uroczyste zakończenie roku szkolnego", full.getWeekTable().getDay(4).getLesson(0).getSubject()); Assert.assertEquals("Fizyka", full.getWeekTable().getDay(0).getLesson(0).getSubject()); Assert.assertEquals("Metodologia programowania", full.getWeekTable().getDay(1).getLesson(0).getSubject()); + Assert.assertEquals("Język niemiecki", full.getWeekTable().getDay(4).getLesson(2).getSubject()); Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getSubject()); } @@ -122,6 +124,7 @@ public class TimetableTest extends StudentAndParentTestCase { Assert.assertEquals("Nowak Jadwiga", full.getWeekTable().getDay(2).getLesson(0).getTeacher()); Assert.assertEquals("Nowicka Irena", full.getWeekTable().getDay(3).getLesson(1).getTeacher()); Assert.assertEquals("Baran Małgorzata", full.getWeekTable().getDay(4).getLesson(0).getTeacher()); + Assert.assertEquals("", full.getWeekTable().getDay(4).getLesson(1).getTeacher()); Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getTeacher()); } @@ -148,7 +151,13 @@ public class TimetableTest extends StudentAndParentTestCase { Assert.assertEquals("zastępstwo (poprzednio: Religia)", full.getWeekTable().getDay(2).getLesson(0).getDescription()); Assert.assertEquals("zastępstwo (poprzednio: Wychowanie fizyczne)", full.getWeekTable().getDay(3).getLesson(1).getDescription()); Assert.assertEquals("", full.getWeekTable().getDay(4).getLesson(0).getDescription()); + Assert.assertEquals("", full.getWeekTable().getDay(4).getLesson(1).getDescription()); + Assert.assertEquals("bez nawiasów (poprzednio: Religia)", full.getWeekTable().getDay(4).getLesson(3).getDescription()); + Assert.assertEquals("poprzednio: Wychowanie fizyczne", full.getWeekTable().getDay(4).getLesson(2).getDescription()); Assert.assertEquals("egzamin", full.getWeekTable().getDay(3).getLesson(0).getDescription()); + Assert.assertEquals("", full.getWeekTable().getDay(4).getLesson(1).getDescription()); + Assert.assertEquals("poprzednio: Zajęcia z wychowawcą", full.getWeekTable().getDay(4).getLesson(5).getDescription()); + Assert.assertEquals("opis w uwadze bez klasy w spanie", full.getWeekTable().getDay(4).getLesson(4).getDescription()); Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getDescription()); } @@ -237,6 +246,8 @@ public class TimetableTest extends StudentAndParentTestCase { Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(2).isNewMovedInOrChanged()); Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(3).isNewMovedInOrChanged()); Assert.assertTrue(full.getWeekTable().getDay(3).getLesson(1).isNewMovedInOrChanged()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(1).isNewMovedInOrChanged()); + Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(2).isNewMovedInOrChanged()); Assert.assertFalse(holidays.getWeekTable().getDay(3).getLesson(3).isNewMovedInOrChanged()); } } 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/Start-multi.html b/api/src/test/resources/io/github/wulkanowy/api/Start-multi.html new file mode 100644 index 00000000..d5323677 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/Start-multi.html @@ -0,0 +1,29 @@ + + + + + Uonet+ + + +
+
+
+
+ +
+
+
+
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/Start-std.html b/api/src/test/resources/io/github/wulkanowy/api/Start-std.html new file mode 100644 index 00000000..2cf03d63 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/Start-std.html @@ -0,0 +1,37 @@ + + + + + Uonet+ + + +
+ +
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/Start.html b/api/src/test/resources/io/github/wulkanowy/api/Start.html deleted file mode 100644 index 40e69161..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/Start.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Uonet+ - - -
- -
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/WitrynaUczniaIRodzica.html b/api/src/test/resources/io/github/wulkanowy/api/WitrynaUczniaIRodzica.html new file mode 100644 index 00000000..66e2942f --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/WitrynaUczniaIRodzica.html @@ -0,0 +1,37 @@ + + + + + Witryna ucznia i rodzica – Strona główna + + +
    +
  • + + +
  • +
  • + + +
  • +
+ +
wersja: 17.09.0008.26553
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-empty.html b/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-empty.html new file mode 100644 index 00000000..bf1032b4 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-empty.html @@ -0,0 +1,19 @@ + + + + + Witryna ucznia i rodzica – Terminarz sprawdzianów + + +
+

Sprawdziany

+

Tydzień 30.04.2018 - 06.05.2018

+

Nie zaplanowano żadnych sprawdzianów na wybrany tydzień

+ +
+
wersja: 17.09.0009.26859
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-filled.html b/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-filled.html index a229d1d5..d0cdb664 100644 --- a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-filled.html +++ b/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-filled.html @@ -100,6 +100,14 @@ 06.02.2017 Amelia Stępień + + Język polski + 5 + +Odp, Kordian + 5,00 + 11.05.2017 + Amelia Stępień + diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-certyfikat.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-certyfikat.html index a8496cd1..f53a3485 100644 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-certyfikat.html +++ b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-certyfikat.html @@ -3,10 +3,10 @@ Working... -
+ - - + +